From 19adfd5116a23c3ccff70c8c12ecc3ffe48736c8 Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 10 Feb 2024 09:40:46 -0800 Subject: [PATCH 01/46] sets responsive breakpoint to bootstrap md 767.98px --- .../wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css index eac1145d..266adcc8 100644 --- a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css +++ b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css @@ -197,7 +197,7 @@ } } -@media (max-width: 767px) { +@media (max-width: 767.98px) { .app-logo { height: 80px; display: flex; From 6c3526d47eb1393f87a32f6704b19ebc35c565b4 Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 10 Feb 2024 09:42:40 -0800 Subject: [PATCH 02/46] sets responsive breakpoint to 767.98 bootstrap md --- .../wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css index 9eec314f..1b9ea409 100644 --- a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css +++ b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css @@ -96,7 +96,7 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu z-index: 1000; } -@media (max-width: 767px) { +@media (max-width: 767.98px) { .app-menu { width: 100%; From 266e08817ed3dcf5d568fe9646b95258e5aa4598 Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 10 Feb 2024 09:43:18 -0800 Subject: [PATCH 03/46] sets responsive breakpoint to bootstrap md 767.98px --- .../Client/wwwroot/Themes/[Owner].Theme.[Theme]/Theme.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/wwwroot/Themes/[Owner].Theme.[Theme]/Theme.css b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/wwwroot/Themes/[Owner].Theme.[Theme]/Theme.css index e78e77d1..2a6101ef 100644 --- a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/wwwroot/Themes/[Owner].Theme.[Theme]/Theme.css +++ b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/wwwroot/Themes/[Owner].Theme.[Theme]/Theme.css @@ -90,7 +90,7 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu mix-blend-mode: difference; } -@media (max-width: 767px) { +@media (max-width: 767.98px) { .app-menu { width: 100%; From f68e9c7681c3e10c268c4903a8bd2bb0a54ab7d8 Mon Sep 17 00:00:00 2001 From: Cody Date: Mon, 12 Feb 2024 11:41:33 -0800 Subject: [PATCH 04/46] updates bootstrap to latest --- Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs b/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs index 49f21038..465c06bf 100644 --- a/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs +++ b/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs @@ -16,9 +16,9 @@ namespace Oqtane.Themes.OqtaneTheme ContainerSettingsType = "Oqtane.Themes.OqtaneTheme.ContainerSettings, Oqtane.Client", Resources = new List() { - new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.0/cyborg/bootstrap.min.css", Integrity = "sha512-jwIqEv8o/kTBMJVtbNCBrDqhBojl0YSUam+EFpLjVOC86Ci6t4ZciTnIkelFNOik+dEQVymKGcQLiaJZNAfWRg==", CrossOrigin = "anonymous" }, + new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.2/cyborg/bootstrap.min.css", Integrity = "sha512-RfNxVfFNFgqk9MXO4TCKXYXn9hgc+keHCg3xFFGbnp2q7Cifda+YYzMTDHwsQtNx4DuqIMgfvZead7XOtB9CDQ==", CrossOrigin = "anonymous" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" }, - new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js", Integrity = "sha512-VK2zcvntEufaimc+efOYi622VN5ZacdnufnmX7zIhCPmjhKnOi9ZDMtg1/ug5l183f19gG1/cBstPO4D8N/Img==", CrossOrigin = "anonymous" } + new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.bundle.min.js", Integrity = "sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg==", CrossOrigin = "anonymous" } } }; } From d723bbe3b7d2d0eb80c839acb20d54f45bd44fd5 Mon Sep 17 00:00:00 2001 From: Cody Date: Mon, 12 Feb 2024 11:44:27 -0800 Subject: [PATCH 05/46] Updates to bootstrap 5.3.2 --- .../wwwroot/Themes/Templates/External/Client/ThemeInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/ThemeInfo.cs b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/ThemeInfo.cs index cac21823..5d7a086f 100644 --- a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/ThemeInfo.cs +++ b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/ThemeInfo.cs @@ -17,9 +17,9 @@ namespace [Owner].Theme.[Theme] Resources = new List() { // obtained from https://cdnjs.com/libraries - new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css", Integrity = "sha512-t4GWSVZO1eC8BM339Xd7Uphw5s17a86tIZIj8qRxhnKub6WoyhnrxeCIMeAqBPgdZGlCcG2PrZjMc+Wr78+5Xg==", CrossOrigin = "anonymous" }, + new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css", Integrity = "sha512-t4GWSVZO1eC8BM339Xd7Uphw5s17a86tIZIj8qRxhnKub6WoyhnrxeCIMeAqBPgdZGlCcG2PrZjMc+Wr78+5Xg==", CrossOrigin = "anonymous" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" }, - new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js", Integrity = "sha512-VK2zcvntEufaimc+efOYi622VN5ZacdnufnmX7zIhCPmjhKnOi9ZDMtg1/ug5l183f19gG1/cBstPO4D8N/Img==", CrossOrigin = "anonymous" } + new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.bundle.min.js", Integrity = "sha512-X/YkDZyjTf4wyc2Vy16YGCPHwAY8rZJY+POgokZjQB2mhIRFJCckEGc6YyX9eNsPfn0PzThEuNs+uaomE5CO6A==", CrossOrigin = "anonymous" } } }; From d48c257b19b19dbee58a4c359571209e1b56a988 Mon Sep 17 00:00:00 2001 From: Cody Date: Mon, 12 Feb 2024 11:53:42 -0800 Subject: [PATCH 06/46] Update Bootstrap 5.3.2 Integrity --- .../wwwroot/Themes/Templates/External/Client/ThemeInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/ThemeInfo.cs b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/ThemeInfo.cs index 5d7a086f..f56ef74c 100644 --- a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/ThemeInfo.cs +++ b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/ThemeInfo.cs @@ -17,7 +17,7 @@ namespace [Owner].Theme.[Theme] Resources = new List() { // obtained from https://cdnjs.com/libraries - new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css", Integrity = "sha512-t4GWSVZO1eC8BM339Xd7Uphw5s17a86tIZIj8qRxhnKub6WoyhnrxeCIMeAqBPgdZGlCcG2PrZjMc+Wr78+5Xg==", CrossOrigin = "anonymous" }, + new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css", Integrity = "sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg==", CrossOrigin = "anonymous" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" }, new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.bundle.min.js", Integrity = "sha512-X/YkDZyjTf4wyc2Vy16YGCPHwAY8rZJY+POgokZjQB2mhIRFJCckEGc6YyX9eNsPfn0PzThEuNs+uaomE5CO6A==", CrossOrigin = "anonymous" } } From 6168621a362c993962d401152a20f5f88e8c2299 Mon Sep 17 00:00:00 2001 From: Cody Date: Mon, 12 Feb 2024 11:58:08 -0800 Subject: [PATCH 07/46] Update ThemeInfo.cs --- Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs b/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs index 465c06bf..92bd9afe 100644 --- a/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs +++ b/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs @@ -18,7 +18,7 @@ namespace Oqtane.Themes.OqtaneTheme { new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.2/cyborg/bootstrap.min.css", Integrity = "sha512-RfNxVfFNFgqk9MXO4TCKXYXn9hgc+keHCg3xFFGbnp2q7Cifda+YYzMTDHwsQtNx4DuqIMgfvZead7XOtB9CDQ==", CrossOrigin = "anonymous" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" }, - new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.bundle.min.js", Integrity = "sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg==", CrossOrigin = "anonymous" } + new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.bundle.min.js", Integrity = "sha512-X/YkDZyjTf4wyc2Vy16YGCPHwAY8rZJY+POgokZjQB2mhIRFJCckEGc6YyX9eNsPfn0PzThEuNs+uaomE5CO6A==", CrossOrigin = "anonymous" } } }; } From 740b89258d30c1084b3b295f9f43c1b6682fa4c6 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 16 Feb 2024 19:26:01 +0800 Subject: [PATCH 08/46] Fix #3807: parsing the data attributes. --- Oqtane.Client/UI/ThemeBuilder.razor | 9 ++++++++- Oqtane.Server/wwwroot/js/interop.js | 31 ++++++++++++++++++----------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index 9432e2ac..98cae26c 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -145,6 +145,7 @@ string integrity = ""; string crossorigin = ""; string type = ""; + var dataAttributes = new Dictionary(); foreach (var attribute in attributes) { if (attribute.Contains("=")) @@ -167,6 +168,12 @@ case "type": type = value[1]; break; + default: + if(!string.IsNullOrWhiteSpace(value[0]) && value[0].StartsWith("data-")) + { + dataAttributes.Add(value[0], value[1]); + } + break; } } } @@ -174,7 +181,7 @@ if (!string.IsNullOrEmpty(src)) { src = (src.Contains("://")) ? src : PageState.Alias.BaseUrl + src; - scripts.Add(new { href = src, bundle = "", integrity = integrity, crossorigin = crossorigin, es6module = (type == "module"), location = location.ToString().ToLower() }); + scripts.Add(new { href = src, bundle = "", integrity = integrity, crossorigin = crossorigin, es6module = (type == "module"), location = location.ToString().ToLower(), dataAttributes = dataAttributes }); } else { diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index 8305f766..b457ad1d 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -206,18 +206,25 @@ Oqtane.Interop = { returnPromise: true, before: function (path, element) { for (let s = 0; s < scripts.length; s++) { - if (path === scripts[s].href && scripts[s].integrity !== '') { - element.integrity = scripts[s].integrity; - } - if (path === scripts[s].href && scripts[s].crossorigin !== '') { - element.crossOrigin = scripts[s].crossorigin; - } - if (path === scripts[s].href && scripts[s].es6module === true) { - element.type = "module"; - } - if (path === scripts[s].href && scripts[s].location === 'body') { - document.body.appendChild(element); - return false; // return false to bypass default DOM insertion mechanism + if (path === scripts[s].href) { + if (scripts[s].integrity !== '') { + element.integrity = scripts[s].integrity; + } + if (scripts[s].crossorigin !== '') { + element.crossOrigin = scripts[s].crossorigin; + } + if (scripts[s].es6module === true) { + element.type = "module"; + } + if (typeof scripts[s].dataAttributes !== "undefined" && scripts[s].dataAttributes !== null) { + for (var key in scripts[s].dataAttributes) { + element.setAttribute(key, scripts[s].dataAttributes[key]); + } + } + if (scripts[s].location === 'body') { + document.body.appendChild(element); + return false; // return false to bypass default DOM insertion mechanism + } } } } From b0487798c2ecc9b87cfb7d089f4827fb31f2a8ca Mon Sep 17 00:00:00 2001 From: sbwalker Date: Fri, 16 Feb 2024 11:13:31 -0500 Subject: [PATCH 09/46] fix issues with installer --- Oqtane.Server/Components/App.razor | 4 +++- .../Infrastructure/UpgradeManager.cs | 1 - Oqtane.Server/Services/SiteService.cs | 22 +++++++++++-------- Oqtane.Shared/Shared/Constants.cs | 2 +- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Oqtane.Server/Components/App.razor b/Oqtane.Server/Components/App.razor index e8103ca2..0e3d5ac3 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -31,7 +31,7 @@ @inject IVisitorRepository VisitorRepository @inject IJwtManager JwtManager -@if (_pageState != null) +@if (_initialized) { @@ -93,6 +93,7 @@ } @code { + private bool _initialized = false; private string _renderMode = RenderModes.Interactive; private string _runtime = Runtimes.Server; private bool _prerender = true; @@ -266,6 +267,7 @@ _message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name"; } } + _initialized = true; } private void HandleDefaultAliasRedirect(Alias alias, string url) diff --git a/Oqtane.Server/Infrastructure/UpgradeManager.cs b/Oqtane.Server/Infrastructure/UpgradeManager.cs index a81b5ca0..d23103a4 100644 --- a/Oqtane.Server/Infrastructure/UpgradeManager.cs +++ b/Oqtane.Server/Infrastructure/UpgradeManager.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; -using Oqtane.Extensions; using Oqtane.Models; using Oqtane.Repository; using Oqtane.Shared; diff --git a/Oqtane.Server/Services/SiteService.cs b/Oqtane.Server/Services/SiteService.cs index 8f7c659a..c1b1a1ab 100644 --- a/Oqtane.Server/Services/SiteService.cs +++ b/Oqtane.Server/Services/SiteService.cs @@ -27,11 +27,11 @@ namespace Oqtane.Services private readonly ILanguageRepository _languages; private readonly IUserPermissions _userPermissions; private readonly ISettingRepository _settings; + private readonly ITenantManager _tenantManager; private readonly ISyncManager _syncManager; private readonly ILogManager _logger; private readonly IMemoryCache _cache; private readonly IHttpContextAccessor _accessor; - private readonly Alias _alias; public ServerSiteService(ISiteRepository sites, IPageRepository pages, IThemeRepository themes, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IMemoryCache cache, IHttpContextAccessor accessor) { @@ -43,11 +43,11 @@ namespace Oqtane.Services _languages = languages; _userPermissions = userPermissions; _settings = settings; + _tenantManager = tenantManager; _syncManager = syncManager; _logger = logger; _cache = cache; _accessor = accessor; - _alias = tenantManager.GetAlias(); } public async Task> GetSitesAsync() @@ -80,8 +80,9 @@ namespace Oqtane.Services private Site GetSite(int siteid) { + var alias = _tenantManager.GetAlias(); var site = _sites.GetSite(siteid); - if (site != null && site.SiteId == _alias.SiteId) + if (site != null && site.SiteId == alias.SiteId) { // site settings site.Settings = _settings.GetSettings(EntityNames.Site, site.SiteId) @@ -179,8 +180,9 @@ namespace Oqtane.Services { if (_accessor.HttpContext.User.IsInRole(RoleNames.Host)) { + var alias = _tenantManager.GetAlias(); site = _sites.AddSite(site); - _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Create); + _syncManager.AddSyncEvent(alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Create); _logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Create, "Site Added {Site}", site); } else @@ -194,17 +196,18 @@ namespace Oqtane.Services { if (_accessor.HttpContext.User.IsInRole(RoleNames.Admin)) { + var alias = _tenantManager.GetAlias(); var current = _sites.GetSite(site.SiteId, false); - if (site.SiteId == _alias.SiteId && site.TenantId == _alias.TenantId && current != null) + if (site.SiteId == alias.SiteId && site.TenantId == alias.TenantId && current != null) { site = _sites.UpdateSite(site); - _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Update); + _syncManager.AddSyncEvent(alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Update); string action = SyncEventActions.Refresh; if (current.RenderMode != site.RenderMode || current.Runtime != site.Runtime) { action = SyncEventActions.Reload; } - _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, action); + _syncManager.AddSyncEvent(alias.TenantId, EntityNames.Site, site.SiteId, action); _logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Update, "Site Updated {Site}", site); } else @@ -224,11 +227,12 @@ namespace Oqtane.Services { if (_accessor.HttpContext.User.IsInRole(RoleNames.Host)) { + var alias = _tenantManager.GetAlias(); var site = _sites.GetSite(siteId); - if (site != null && site.SiteId == _alias.SiteId) + if (site != null && site.SiteId == alias.SiteId) { _sites.DeleteSite(siteId); - _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Delete); + _syncManager.AddSyncEvent(alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Delete); _logger.Log(siteId, LogLevel.Information, this, LogFunction.Delete, "Site Deleted {SiteId}", siteId); } else diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index f8db5bf9..e538ef9d 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -5,7 +5,7 @@ namespace Oqtane.Shared public class Constants { public static readonly string Version = "5.1.0"; - public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.1.0"; + public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0"; public const string PackageId = "Oqtane.Framework"; public const string ClientId = "Oqtane.Client"; public const string UpdaterPackageId = "Oqtane.Updater"; From 8d0aa65ab2216a4c60f616c06338fcfae6bd793b Mon Sep 17 00:00:00 2001 From: sbwalker Date: Fri, 16 Feb 2024 11:17:14 -0500 Subject: [PATCH 10/46] fix #3811 - add PreserveCompilationContext back to server project --- Oqtane.Server/Oqtane.Server.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 0ea8b4d3..c06187fe 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -17,6 +17,7 @@ Oqtane true $(DefineConstants);OQTANE;OQTANE3 + true none From 04da9891081b4eba23c50c652cc7deaa4d8595a8 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Fri, 16 Feb 2024 15:08:04 -0500 Subject: [PATCH 11/46] get title and headcontent working again on interactive render mode --- Oqtane.Client/UI/InteractiveRenderMode.cs | 4 ++-- Oqtane.Client/UI/ThemeBuilder.razor | 24 ----------------------- Oqtane.Server/Components/App.razor | 8 ++++++-- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/Oqtane.Client/UI/InteractiveRenderMode.cs b/Oqtane.Client/UI/InteractiveRenderMode.cs index 1094aafb..0483b748 100644 --- a/Oqtane.Client/UI/InteractiveRenderMode.cs +++ b/Oqtane.Client/UI/InteractiveRenderMode.cs @@ -6,7 +6,7 @@ namespace Oqtane.UI { public static class InteractiveRenderMode { - public static IComponentRenderMode GetInteractiveRenderMode(string runtime, bool prerender) + public static IComponentRenderMode? GetInteractiveRenderMode(string runtime, bool prerender) { switch (runtime) { @@ -17,7 +17,7 @@ namespace Oqtane.UI case Runtimes.Auto: return new InteractiveAutoRenderMode(prerender: prerender); } - return new InteractiveServerRenderMode(prerender: prerender); // default to interactiver server + return null; // default to inherit existing render mode } } } diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index ccb80cf2..262a919a 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -53,30 +53,6 @@ { headcontent = AddHeadContent(headcontent, PageState.Page.HeadContent); } - if (PageState.RenderMode == RenderModes.Static) - { - string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"); - int count = 0; - foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet && item.Level == ResourceLevel.Module)) - { - // if (resource.Url.StartsWith("~")) - // { - // resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(name) + "/").Replace("//", "/"); - // } - if (!resource.Url.Contains("://") && PageState.Alias.BaseUrl != "" && !resource.Url.StartsWith(PageState.Alias.BaseUrl)) - { - resource.Url = PageState.Alias.BaseUrl + resource.Url; - } - - if (!headcontent.Contains(resource.Url, StringComparison.OrdinalIgnoreCase)) - { - count++; - string id = "id=\"app-stylesheet-" + resource.Level.ToString().ToLower() + "-" + batch + "-" + count.ToString("00") + "\" "; - headcontent += "" + Environment.NewLine; - } - } - - } SiteState.Properties.HeadContent = headcontent; DynamicComponent = builder => diff --git a/Oqtane.Server/Components/App.razor b/Oqtane.Server/Components/App.razor index 0e3d5ac3..1b741832 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -184,12 +184,12 @@ } } ManageStyleSheets(resources, alias, theme.ThemeName); + + // scripts if (_renderMode == RenderModes.Static) { ManageScripts(resources, alias); } - - // scripts if (_renderMode == RenderModes.Interactive && _runtime == Runtimes.Server) { _reconnectScript = CreateReconnectScript(); @@ -233,6 +233,10 @@ } // create initial PageState + // page needs to be populated + // site.modules need to be populated + // user needs to be populated + _pageState = new PageState { Alias = alias, From 100fc20928581b1c1ef7c79771acdeef9af07c49 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Fri, 16 Feb 2024 15:15:02 -0500 Subject: [PATCH 12/46] ensure script resources are loaded before rendering ThemeBuilder --- Oqtane.Client/UI/SiteRouter.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 752ff996..db59fd4f 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -50,7 +50,7 @@ DynamicComponent = builder => { - if (PageState != null) + if (PageState != null && !PageState.Refresh) { builder.OpenComponent(0, typeof(ThemeBuilder)); builder.CloseComponent(); From 2c17551d50253d796c935555ae2bcf0e2a4fee1f Mon Sep 17 00:00:00 2001 From: sbwalker Date: Fri, 16 Feb 2024 15:36:23 -0500 Subject: [PATCH 13/46] remove PageRepository reference in App --- Oqtane.Server/Components/App.razor | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Oqtane.Server/Components/App.razor b/Oqtane.Server/Components/App.razor index 1b741832..c566722d 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -21,7 +21,6 @@ @inject IConfigManager ConfigManager @inject ITenantManager TenantManager @inject ISiteService SiteService -@inject IPageRepository PageRepository @inject IThemeRepository ThemeRepository @inject ILanguageRepository LanguageRepository @inject IServerStateManager ServerStateManager @@ -140,12 +139,19 @@ _prerender = site.Prerender; Route route = new Route(url, alias.Path); - var page = PageRepository.GetPage(route.PagePath, site.SiteId); - if (page == null && route.PagePath == "" && site.HomePageId != null) + var page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase)); + if (page == null && route.PagePath == "") // naked path refers to site home page { - page = PageRepository.GetPage(site.HomePageId.Value); + if (site.HomePageId != null) + { + page = site.Pages.FirstOrDefault(item => item.PageId == site.HomePageId); + } + if (page == null) + { + // fallback to use the first page in the collection + page = site.Pages.FirstOrDefault(); + } } - if (page == null || page.IsDeleted) { HandlePageNotFound(site, page, route); @@ -233,10 +239,6 @@ } // create initial PageState - // page needs to be populated - // site.modules need to be populated - // user needs to be populated - _pageState = new PageState { Alias = alias, From db6c65c7e88cf190bf089532c490e418f20e1626 Mon Sep 17 00:00:00 2001 From: mostafametwally Date: Fri, 16 Feb 2024 23:17:56 +0100 Subject: [PATCH 14/46] fix account lockout to read from correct setting, consider timezone offset and enable lockout --- .../Extensions/OqtaneSiteIdentityBuilderExtensions.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Oqtane.Server/Extensions/OqtaneSiteIdentityBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneSiteIdentityBuilderExtensions.cs index d732421f..6234d2fb 100644 --- a/Oqtane.Server/Extensions/OqtaneSiteIdentityBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneSiteIdentityBuilderExtensions.cs @@ -21,8 +21,9 @@ namespace Oqtane.Extensions options.Password.RequireNonAlphanumeric = bool.Parse(sitesettings.GetValue("IdentityOptions:Password:RequireNonAlphanumeric", options.Password.RequireNonAlphanumeric.ToString())); // lockout options - options.Lockout.MaxFailedAccessAttempts = int.Parse(sitesettings.GetValue("IdentityOptions:Password:MaxFailedAccessAttempts", options.Lockout.MaxFailedAccessAttempts.ToString())); - options.Lockout.DefaultLockoutTimeSpan = TimeSpan.Parse(sitesettings.GetValue("IdentityOptions:Password:DefaultLockoutTimeSpan", options.Lockout.DefaultLockoutTimeSpan.ToString())); + options.Lockout.MaxFailedAccessAttempts = int.Parse(sitesettings.GetValue("IdentityOptions:Lockout:MaxFailedAccessAttempts", options.Lockout.MaxFailedAccessAttempts.ToString())); + options.Lockout.DefaultLockoutTimeSpan = TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow) + TimeSpan.Parse(sitesettings.GetValue("IdentityOptions:Lockout:DefaultLockoutTimeSpan", options.Lockout.DefaultLockoutTimeSpan.ToString())); + options.Lockout.AllowedForNewUsers = options.Lockout.MaxFailedAccessAttempts > 0; }); return builder; From 5d64ea48baff2b292899ea424402cb65365a5e51 Mon Sep 17 00:00:00 2001 From: mostafametwally Date: Sat, 17 Feb 2024 12:28:55 +0100 Subject: [PATCH 15/46] added links from login to register and vice versa --- Oqtane.Client/Modules/Admin/Login/Index.razor | 9 +++++++-- Oqtane.Client/Modules/Admin/Register/Index.razor | 9 ++++++++- Oqtane.Client/Resources/Modules/Admin/Login/Index.resx | 3 +++ .../Resources/Modules/Admin/Register/Index.resx | 3 +++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index 64c1d93f..61c9fe9a 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -47,8 +47,13 @@

- - } + + @if (PageState.Site.AllowRegistration) + { +

+ @Localizer["Register"] + } + } } diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index b7c80851..8526e02e 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -60,6 +60,11 @@
+ @if (_allowsitelogin) + { +

+ @Localizer["Login"] + } @@ -80,12 +85,14 @@ else private string _confirm = string.Empty; private string _email = string.Empty; private string _displayname = string.Empty; + private bool _allowsitelogin = true; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous; protected override async Task OnInitializedAsync() { - _passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId); + _passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId); + _allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true")); } protected override void OnParametersSet() diff --git a/Oqtane.Client/Resources/Modules/Admin/Login/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Login/Index.resx index 3467911a..0b1a8780 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Login/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Login/Index.resx @@ -228,4 +228,7 @@ The Review Claims Option Was Enabled In External Login Settings. Please Visit The Event Log To View The Claims Returned By The Provider. + + Register as new user? + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx index 1895cbef..77d44196 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx @@ -177,4 +177,7 @@ Username: + + Already have account, Login? + \ No newline at end of file From a741b1b9ace601993f9693958d7d1a732fbc6aa4 Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 17 Feb 2024 07:22:32 -0800 Subject: [PATCH 16/46] Fix Login Button Control Url While On Login Page --- Oqtane.Client/Themes/Controls/Theme/LoginBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs b/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs index fc41f565..7d7d575c 100644 --- a/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs +++ b/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs @@ -52,7 +52,7 @@ namespace Oqtane.Themes.Controls else { // use existing value - loginurl = "?returnurl=" + PageState.QueryString["returnurl"]; + loginurl += "?returnurl=" + PageState.QueryString["returnurl"]; } // set logout url From 57a86cd8368f7b8a8d162495d05c4cd9a2d5e823 Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 17 Feb 2024 09:11:56 -0800 Subject: [PATCH 17/46] Fix handling of returnurl parameter in UserProfile component --- Oqtane.Client/Themes/Controls/Theme/UserProfile.razor | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Oqtane.Client/Themes/Controls/Theme/UserProfile.razor b/Oqtane.Client/Themes/Controls/Theme/UserProfile.razor index 69abd0e8..f4a2130b 100644 --- a/Oqtane.Client/Themes/Controls/Theme/UserProfile.razor +++ b/Oqtane.Client/Themes/Controls/Theme/UserProfile.razor @@ -31,7 +31,16 @@ protected override void OnParametersSet() { - _returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery); + if (!PageState.QueryString.ContainsKey("returnurl")) + { + // remember current url + _returnurl += WebUtility.UrlEncode(PageState.Route.PathAndQuery); + } + else + { + // use existing value + _returnurl += PageState.QueryString["returnurl"]; + } } } From 114ebac21b825ac75fbd62e999d2f0eb3e6cebc8 Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 17 Feb 2024 12:12:57 -0800 Subject: [PATCH 18/46] adds reload "true" boolean to the Save NavigateTo() method --- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 3084ef84..2fb8a06f 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -630,11 +630,11 @@ await logger.LogInformation("Page Saved {Page}", _page); if (!string.IsNullOrEmpty(PageState.ReturnUrl)) { - NavigationManager.NavigateTo(PageState.ReturnUrl); + NavigationManager.NavigateTo(PageState.ReturnUrl, true); } else { - NavigationManager.NavigateTo(NavigateUrl()); + NavigationManager.NavigateTo(NavigateUrl(), true); } } else From 972601caf617b6eeb27cf0fa419db7d7e00b601b Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 17 Feb 2024 17:55:38 -0800 Subject: [PATCH 19/46] correct documentation spelling error --- Oqtane.Client/Services/Interfaces/IPageService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/Services/Interfaces/IPageService.cs b/Oqtane.Client/Services/Interfaces/IPageService.cs index 2f5c4c6d..d4002c3f 100644 --- a/Oqtane.Client/Services/Interfaces/IPageService.cs +++ b/Oqtane.Client/Services/Interfaces/IPageService.cs @@ -10,7 +10,7 @@ namespace Oqtane.Services public interface IPageService { /// - /// Retuns a list of pages + /// Returns a list of pages /// /// /// From 2262d44638eceb80ac6510a1cb891668b37515ab Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 17 Feb 2024 18:17:13 -0800 Subject: [PATCH 20/46] Removes unnecessary Oqtane.UI namespace --- Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs index fbf2fafb..707728eb 100644 --- a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs +++ b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs @@ -1,5 +1,4 @@ using Oqtane.Models; -using Oqtane.UI; using System.Collections.Generic; using System.Threading.Tasks; From 79ce990644730375438d4d5096ef0b7f7a3c1247 Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 17 Feb 2024 18:22:01 -0800 Subject: [PATCH 21/46] cleanup unused namespaces --- Oqtane.Client/Services/ModuleDefinitionService.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index 97f70532..932bef8f 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -3,11 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using System; -using System.Reflection; using Oqtane.Documentation; using Oqtane.Shared; -using Oqtane.UI; namespace Oqtane.Services { From 77ce31128c0e61fcc8487a759f80485ab27281a0 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 18 Feb 2024 21:37:06 +0800 Subject: [PATCH 22/46] Fix #3833: introduce token replace class. --- .../Controllers/ModuleDefinitionController.cs | 90 ++++++---- Oqtane.Server/Controllers/ThemeController.cs | 88 ++++++---- .../OqtaneServiceCollectionExtensions.cs | 3 + .../Interfaces/ITokenReplace.cs | 27 +++ Oqtane.Server/Infrastructure/TokenReplace.cs | 158 ++++++++++++++++++ Oqtane.Shared/Interfaces/ITokenSource.cs | 13 ++ 6 files changed, 319 insertions(+), 60 deletions(-) create mode 100644 Oqtane.Server/Infrastructure/Interfaces/ITokenReplace.cs create mode 100644 Oqtane.Server/Infrastructure/TokenReplace.cs create mode 100644 Oqtane.Shared/Interfaces/ITokenSource.cs diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 4fbea7eb..39ae9f48 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -16,6 +16,7 @@ using Microsoft.Extensions.DependencyInjection; using System.Text.Json; using System.Net; using Oqtane.Modules; +using Oqtane.Infrastructure.Interfaces; namespace Oqtane.Controllers { @@ -319,52 +320,33 @@ namespace Oqtane.Controllers private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string rootFolder, string templatePath, ModuleDefinition moduleDefinition) { + var tokenReplace = InitializeTokenReplace(rootPath, rootFolder, moduleDefinition); + // process folder - string folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, "")); - folderPath = folderPath.Replace("[Owner]", moduleDefinition.Owner); - folderPath = folderPath.Replace("[Module]", moduleDefinition.Name); + var folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, "")); + folderPath = tokenReplace.ReplaceTokens(folderPath); if (!Directory.Exists(folderPath)) { Directory.CreateDirectory(folderPath); } - FileInfo[] files = current.GetFiles("*.*"); + tokenReplace.AddSource("Folder", folderPath); + var files = current.GetFiles("*.*"); if (files != null) { foreach (FileInfo file in files) { // process file - string filePath = Path.Combine(folderPath, file.Name); - filePath = filePath.Replace("[Owner]", moduleDefinition.Owner); - filePath = filePath.Replace("[Module]", moduleDefinition.Name); + var filePath = Path.Combine(folderPath, file.Name); + filePath = tokenReplace.ReplaceTokens(filePath); + tokenReplace.AddSource("File", Path.GetFileName(filePath)); - string text = System.IO.File.ReadAllText(file.FullName); - text = text.Replace("[Owner]", moduleDefinition.Owner); - text = text.Replace("[Module]", moduleDefinition.Name); - text = text.Replace("[Description]", moduleDefinition.Description); - text = text.Replace("[RootPath]", rootPath); - text = text.Replace("[RootFolder]", rootFolder); - text = text.Replace("[ServerManagerType]", moduleDefinition.ServerManagerType); - text = text.Replace("[Folder]", folderPath); - text = text.Replace("[File]", Path.GetFileName(filePath)); - if (moduleDefinition.Version == "local") - { - text = text.Replace("[FrameworkVersion]", Constants.Version); - text = text.Replace("[ClientReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll"); - text = text.Replace("[ServerReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Server.dll"); - text = text.Replace("[SharedReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll"); - } - else - { - text = text.Replace("[FrameworkVersion]", moduleDefinition.Version); - text = text.Replace("[ClientReference]", ""); - text = text.Replace("[ServerReference]", ""); - text = text.Replace("[SharedReference]", ""); - } + var text = System.IO.File.ReadAllText(file.FullName); + text = tokenReplace.ReplaceTokens(text); System.IO.File.WriteAllText(filePath, text); } - DirectoryInfo[] folders = current.GetDirectories(); + var folders = current.GetDirectories(); foreach (DirectoryInfo folder in folders.Reverse()) { @@ -372,5 +354,51 @@ namespace Oqtane.Controllers } } } + + private ITokenReplace InitializeTokenReplace(string rootPath, string rootFolder, ModuleDefinition moduleDefinition) + { + var tokenReplace = _serviceProvider.GetService(); + tokenReplace.AddSource(() => + { + return new Dictionary + { + { "RootPath", rootPath }, + { "RootFolder", rootFolder }, + { "Owner", moduleDefinition.Owner }, + { "Module", moduleDefinition.Name }, + { "Description", moduleDefinition.Description }, + { "ServerManagerType", moduleDefinition.ServerManagerType } + }; + }); + + if (moduleDefinition.Version == "local") + { + tokenReplace.AddSource(() => + { + return new Dictionary() + { + { "FrameworkVersion", Constants.Version }, + { "ClientReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll" }, + { "ServerReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Server.dll" }, + { "SharedReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll" }, + }; + }); + } + else + { + tokenReplace.AddSource(() => + { + return new Dictionary() + { + { "FrameworkVersion", moduleDefinition.Version }, + { "ClientReference", $"" }, + { "ServerReference", $"" }, + { "SharedReference", $"" }, + }; + }); + } + + return tokenReplace; + } } } diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index 40067fa3..f871d1d7 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -14,6 +14,8 @@ using System.Text.Json; using System.Net; using System.Reflection.Metadata; using System; +using Microsoft.Extensions.DependencyInjection; +using Oqtane.Infrastructure.Interfaces; // ReSharper disable StringIndexOfIsCultureSpecific.1 @@ -29,8 +31,9 @@ namespace Oqtane.Controllers private readonly ISyncManager _syncManager; private readonly ILogManager _logger; private readonly Alias _alias; + private readonly IServiceProvider _serviceProvider; - public ThemeController(IThemeRepository themes, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger) + public ThemeController(IThemeRepository themes, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IServiceProvider serviceProvider) { _themes = themes; _installationManager = installationManager; @@ -39,6 +42,7 @@ namespace Oqtane.Controllers _syncManager = syncManager; _logger = logger; _alias = tenantManager.GetAlias(); + _serviceProvider = serviceProvider; } // GET: api/ @@ -208,54 +212,80 @@ namespace Oqtane.Controllers private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string rootFolder, string templatePath, Theme theme) { + var tokenReplace = InitializeTokenReplace(rootPath, rootFolder, theme); + // process folder - string folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, "")); - folderPath = folderPath.Replace("[Owner]", theme.Owner); - folderPath = folderPath.Replace("[Theme]", theme.Name); + var folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, "")); + folderPath = tokenReplace.ReplaceTokens(folderPath); if (!Directory.Exists(folderPath)) { Directory.CreateDirectory(folderPath); } - FileInfo[] files = current.GetFiles("*.*"); + tokenReplace.AddSource("Folder", folderPath); + var files = current.GetFiles("*.*"); if (files != null) { foreach (FileInfo file in files) { // process file - string filePath = Path.Combine(folderPath, file.Name); - filePath = filePath.Replace("[Owner]", theme.Owner); - filePath = filePath.Replace("[Theme]", theme.Name); + var filePath = Path.Combine(folderPath, file.Name); + filePath = tokenReplace.ReplaceTokens(filePath); + tokenReplace.AddSource("File", Path.GetFileName(filePath)); - string text = System.IO.File.ReadAllText(file.FullName); - text = text.Replace("[Owner]", theme.Owner); - text = text.Replace("[Theme]", theme.Name); - text = text.Replace("[RootPath]", rootPath); - text = text.Replace("[RootFolder]", rootFolder); - text = text.Replace("[Folder]", folderPath); - text = text.Replace("[File]", Path.GetFileName(filePath)); - if (theme.Version == "local") - { - text = text.Replace("[FrameworkVersion]", Constants.Version); - text = text.Replace("[ClientReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll"); - text = text.Replace("[SharedReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll"); - } - else - { - text = text.Replace("[FrameworkVersion]", theme.Version); - text = text.Replace("[ClientReference]", ""); - text = text.Replace("[SharedReference]", ""); - } + var text = System.IO.File.ReadAllText(file.FullName); + text = tokenReplace.ReplaceTokens(text); System.IO.File.WriteAllText(filePath, text); } - DirectoryInfo[] folders = current.GetDirectories(); - + var folders = current.GetDirectories(); foreach (DirectoryInfo folder in folders.Reverse()) { ProcessTemplatesRecursively(folder, rootPath, rootFolder, templatePath, theme); } } } + + private ITokenReplace InitializeTokenReplace(string rootPath, string rootFolder, Theme theme) + { + var tokenReplace = _serviceProvider.GetService(); + tokenReplace.AddSource(() => + { + return new Dictionary + { + { "RootPath", rootPath }, + { "RootFolder", rootFolder }, + { "Owner", theme.Owner }, + { "Theme", theme.Name } + }; + }); + + if (theme.Version == "local") + { + tokenReplace.AddSource(() => + { + return new Dictionary() + { + { "FrameworkVersion", Constants.Version }, + { "ClientReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll" }, + { "SharedReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll" }, + }; + }); + } + else + { + tokenReplace.AddSource(() => + { + return new Dictionary() + { + { "FrameworkVersion", theme.Version }, + { "ClientReference", $"" }, + { "SharedReference", $"" }, + }; + }); + } + + return tokenReplace; + } } } diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index dc742cbf..a3324e49 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -19,6 +19,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; using Oqtane.Managers; using Oqtane.Models; using Oqtane.Modules; @@ -150,6 +151,8 @@ namespace Microsoft.Extensions.DependencyInjection // obsolete - replaced by ITenantManager services.AddTransient(); + services.AddTransient(); + return services; } diff --git a/Oqtane.Server/Infrastructure/Interfaces/ITokenReplace.cs b/Oqtane.Server/Infrastructure/Interfaces/ITokenReplace.cs new file mode 100644 index 00000000..ff934a17 --- /dev/null +++ b/Oqtane.Server/Infrastructure/Interfaces/ITokenReplace.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using Oqtane.Interfaces; + +namespace Oqtane.Infrastructure.Interfaces +{ + public interface ITokenReplace + { + void AddSource(ITokenSource source); + + void AddSource(Func> sourceFunc); + + void AddSource(IDictionary source); + + void AddSource(string key, object value); + + void AddSource(string name, ITokenSource source); + + void AddSource(string name, Func> sourceFunc); + + void AddSource(string name, IDictionary source); + + void AddSource(string name, string key, object value); + + string ReplaceTokens(string source); + } +} diff --git a/Oqtane.Server/Infrastructure/TokenReplace.cs b/Oqtane.Server/Infrastructure/TokenReplace.cs new file mode 100644 index 00000000..3c7e8a8c --- /dev/null +++ b/Oqtane.Server/Infrastructure/TokenReplace.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Text; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Interfaces; +using Oqtane.Models; + +namespace Oqtane.Infrastructure +{ + public class TokenReplace : ITokenReplace + { + public const string GenericName = "generic"; + + private const string TokenExpression = "(?:(?\\[\\])|\\[(?:(?[^{}\\]\\[:]+):(?[^\\]\\[\\|]+)|(?[^\\]\\[\\|]+))(?:\\|(?:(?[^\\]\\[]+)\\|(?[^\\]\\\\[]+))|\\|(?:(?[^\\|\\]\\[]+)))?\\])|(?\\[[^\\]\\[]+\\])|(?\\[{0,1}[^\\]\\[]+\\]{0,1})"; + + private Regex TokenizerRegex = new Regex(TokenExpression, RegexOptions.Compiled | RegexOptions.Singleline); + private IDictionary> _tokens; + + private readonly ILogManager _logger; + + public TokenReplace(ILogManager logger) + { + _tokens = new Dictionary>(); + _logger = logger; + } + + public void AddSource(ITokenSource source) + { + this.AddSource(GenericName, source); + } + + public void AddSource(Func> sourceFunc) + { + this.AddSource(GenericName, sourceFunc); + } + + public void AddSource(IDictionary source) + { + this.AddSource(GenericName, source); + } + + public void AddSource(string key, object value) + { + this.AddSource(GenericName, key, value); + } + + public void AddSource(string name, ITokenSource source) + { + var tokens = source.GetTokens(); + this.AddSource(name, tokens); + } + + public void AddSource(string name, Func> sourceFunc) + { + var tokens = sourceFunc(); + this.AddSource(name, tokens); + } + + public void AddSource(string name, IDictionary source) + { + if(source != null) + { + foreach (var key in source.Keys) + { + this.AddSource(name, key, source[key]); + } + } + } + + public void AddSource(string name, string key, object value) + { + if (string.IsNullOrWhiteSpace(name)) + { + name = GenericName; + } + + var source = _tokens.ContainsKey(name.ToLower()) ? _tokens[name.ToLower()] : null; + if(source == null) + { + source = new Dictionary(); + } + source[key] = value; + + _tokens[name.ToLower()] = source; + } + + public string ReplaceTokens(string source) + { + if (string.IsNullOrWhiteSpace(source)) + { + return source; + } + + var result = new StringBuilder(); + foreach (Match match in this.TokenizerRegex.Matches(source)) + { + var key = match.Result("${key}"); + if (!string.IsNullOrWhiteSpace(key)) + { + var sourceName = match.Result("${source}"); + if (string.IsNullOrWhiteSpace(sourceName) || sourceName == "[") + { + sourceName = GenericName; + } + + var format = match.Result("${format}"); + var emptyReplacment = match.Result("${empty}"); + var value = ReplaceTokenValue(sourceName, key, format); + if (string.IsNullOrWhiteSpace(value)) + { + if(!string.IsNullOrWhiteSpace(emptyReplacment)) + { + value = emptyReplacment; + } + else //keep the original content + { + value = match.Value; + } + } + + result.Append(value); + } + else + { + result.Append(match.Result("${text}")); + } + } + + return result.ToString(); + } + + private string ReplaceTokenValue(string sourceName, string key, string format) + { + if(!_tokens.ContainsKey(sourceName.ToLower())) + { + _logger.Log(Shared.LogLevel.Debug, this, Enums.LogFunction.Other, $"MissingSource:{sourceName}"); + return string.Empty; + } + + var tokens = _tokens[sourceName.ToLower()]; + if(!tokens.ContainsKey(key)) + { + _logger.Log(Shared.LogLevel.Debug, this, Enums.LogFunction.Other, $"MissingKey:{key}"); + return string.Empty; + } + + var value = tokens[key]; + if(value == null) + { + return string.Empty; + } + + //TODO: need to implement the format. + return value.ToString(); + } + } +} diff --git a/Oqtane.Shared/Interfaces/ITokenSource.cs b/Oqtane.Shared/Interfaces/ITokenSource.cs new file mode 100644 index 00000000..46fe7f42 --- /dev/null +++ b/Oqtane.Shared/Interfaces/ITokenSource.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Oqtane.Interfaces +{ + public interface ITokenSource + { + IDictionary GetTokens(); + } +} From 54418957b631fd0b198411103bac7fef13cd9487 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Sun, 18 Feb 2024 10:21:36 -0500 Subject: [PATCH 23/46] update Npgsql.EntityFrameworkCore.PostgreSQL to version 8.0.2 --- Oqtane.Client/Modules/ModuleBase.cs | 75 ++++++++++++++++++- Oqtane.Client/UI/InteractiveRenderMode.cs | 4 +- Oqtane.Client/UI/ModuleInstance.razor | 4 +- .../Oqtane.Database.PostgreSQL.csproj | 2 +- 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 1bb76dca..896c151a 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -10,7 +10,6 @@ using System.Collections.Generic; using Microsoft.JSInterop; using System.Linq; using System.Dynamic; -using System.Net.Http.Headers; namespace Oqtane.Modules { @@ -19,6 +18,7 @@ namespace Oqtane.Modules private Logger _logger; private string _urlparametersstate; private Dictionary _urlparameters; + private string _scripts = ""; protected Logger logger => _logger ?? (_logger = new Logger(this)); @@ -67,9 +67,80 @@ namespace Oqtane.Modules return _urlparameters; } } + public string Scripts + { + get + { + return _scripts; + } + } // base lifecycle method for handling JSInterop script registration + protected override void OnParametersSet() + { + //if (ModuleState.RenderMode == RenderModes.Static) + //{ + // _scripts = ""; + // var script = ""; + // List resources = null; + // var type = GetType(); + // if (type.BaseType == typeof(ModuleBase)) + // { + // if (PageState.Page.Resources != null) + // { + // resources = PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level != ResourceLevel.Site && item.Namespace == type.Namespace).ToList(); + // } + // } + // else // modulecontrolbase + // { + // if (Resources != null) + // { + // resources = Resources.Where(item => item.ResourceType == ResourceType.Script).ToList(); + // } + // } + // if (resources != null && resources.Any()) + // { + // var scripts = ""; + // var inline = 0; + // foreach (Resource resource in resources) + // { + // if (!string.IsNullOrEmpty(resource.Url)) + // { + // var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url; + // script += "{" + $"href:'{url}', bundle:'{resource.Bundle ?? ""}', integrity:'{resource.Integrity ?? ""}', crossorigin:'{resource.CrossOrigin ?? ""}', es6module:{resource.ES6Module.ToString().ToLower()}, location:'{resource.Location.ToString().ToLower()}'" + "};"; + // if (scripts.Contains(url)) + // { + // scripts += ((scripts != "") ? ",\n" : "") + script; + // } + // } + // else + // { + // inline += 1; + // script = $"includeScript('{GetType().Namespace.ToLower() + inline.ToString()}', '', '', '', '{resource.Content}', '{resource.Location.ToString().ToLower()}');\n"; + // if (_scripts.Contains(script)) + // { + // _scripts += script; + // } + // } + // } + // if (string.IsNullOrEmpty(scripts)) + // { + // script = "const args = [" + scripts + "];\n"; + // script += "includeScripts.apply(this, args);\n"; + // if (_scripts.Contains(script)) + // { + // _scripts += script; + // } + // } + // } + // if (!string.IsNullOrEmpty(_scripts)) + // { + // _scripts = "\n"; + // } + //} + } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) @@ -90,7 +161,7 @@ namespace Oqtane.Modules resources = Resources.Where(item => item.ResourceType == ResourceType.Script).ToList(); } } - if (resources != null &&resources.Any()) + if (resources != null && resources.Any()) { var interop = new Interop(JSRuntime); var scripts = new List(); diff --git a/Oqtane.Client/UI/InteractiveRenderMode.cs b/Oqtane.Client/UI/InteractiveRenderMode.cs index 0483b748..7f21f673 100644 --- a/Oqtane.Client/UI/InteractiveRenderMode.cs +++ b/Oqtane.Client/UI/InteractiveRenderMode.cs @@ -6,7 +6,7 @@ namespace Oqtane.UI { public static class InteractiveRenderMode { - public static IComponentRenderMode? GetInteractiveRenderMode(string runtime, bool prerender) + public static IComponentRenderMode GetInteractiveRenderMode(string runtime, bool prerender) { switch (runtime) { @@ -17,7 +17,7 @@ namespace Oqtane.UI case Runtimes.Auto: return new InteractiveAutoRenderMode(prerender: prerender); } - return null; // default to inherit existing render mode + return new InteractiveServerRenderMode(prerender: prerender); } } } diff --git a/Oqtane.Client/UI/ModuleInstance.razor b/Oqtane.Client/UI/ModuleInstance.razor index 1aea64b6..4fd08426 100644 --- a/Oqtane.Client/UI/ModuleInstance.razor +++ b/Oqtane.Client/UI/ModuleInstance.razor @@ -12,7 +12,8 @@ else @code { // this component is on the static side of the render mode boundary - // it passes state as serializable parameters across the boundary so that the state can be used by downstream interactive components + // it passes state as serializable parameters across the boundary + // this enables the state to be used by downstream interactive components [CascadingParameter] protected PageState PageState { get; set; } @@ -20,6 +21,7 @@ else [CascadingParameter] private Module ModuleState { get; set; } + [Obsolete("AddModuleMessage is deprecated. Use ModuleBase.AddModuleMessage instead.", false)] public void AddModuleMessage(string message, MessageType type) { diff --git a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj index 3444d372..ae43a364 100644 --- a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj +++ b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj @@ -35,7 +35,7 @@ - + From 2267dcb76819ecc36e3389b0cbc11d98090619da Mon Sep 17 00:00:00 2001 From: sbwalker Date: Sun, 18 Feb 2024 10:27:12 -0500 Subject: [PATCH 24/46] ensure PageState and ModuleState are passed to ModuleMessage in the event of an unhandled module exception --- Oqtane.Client/UI/RenderModeBoundary.razor | 34 +++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Oqtane.Client/UI/RenderModeBoundary.razor b/Oqtane.Client/UI/RenderModeBoundary.razor index 71ca8f3f..4e83b473 100644 --- a/Oqtane.Client/UI/RenderModeBoundary.razor +++ b/Oqtane.Client/UI/RenderModeBoundary.razor @@ -4,12 +4,12 @@ @inject ILogService LoggingService @inherits ErrorBoundary -@if (CurrentException is null) -{ - @if (ModuleType != null) - { - - + + + @if (CurrentException is null) + { + @if (ModuleType != null) + { @if (!string.IsNullOrEmpty(_messageContent) && _messagePosition == "top") { @@ -23,17 +23,17 @@ { } - - - } -} -else -{ - @if (!string.IsNullOrEmpty(_error)) - { - - } -} + } + } + else + { + @if (!string.IsNullOrEmpty(_error)) + { + + } + } + + @code { private Type ModuleType { get; set; } From 7910d006ca51004e3e4aceb78661c62ec693101f Mon Sep 17 00:00:00 2001 From: sbwalker Date: Sun, 18 Feb 2024 10:31:27 -0500 Subject: [PATCH 25/46] document the mandatory StreamRendering attribute --- Oqtane.Client/UI/Routes.razor | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Oqtane.Client/UI/Routes.razor b/Oqtane.Client/UI/Routes.razor index 5abd9703..7daa0cc9 100644 --- a/Oqtane.Client/UI/Routes.razor +++ b/Oqtane.Client/UI/Routes.razor @@ -3,7 +3,8 @@ @inject IInstallationService InstallationService @inject IJSRuntime JSRuntime @inject SiteState SiteState -@attribute [StreamRendering] +@* the following StreamRendering attribute is required - if it is removed the framework will not render a UI on static rendering *@ +@attribute [StreamRendering] @if (_initialized) { From be98f786b3eedc3993feebba0e4bf9615c0ff610 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 19 Feb 2024 14:30:09 -0500 Subject: [PATCH 26/46] add [StreamRendering] to Head component for static rendering --- Oqtane.Client/UI/Head.razor | 2 ++ Oqtane.Client/UI/Routes.razor | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Oqtane.Client/UI/Head.razor b/Oqtane.Client/UI/Head.razor index 75a6101f..d1435cc3 100644 --- a/Oqtane.Client/UI/Head.razor +++ b/Oqtane.Client/UI/Head.razor @@ -3,6 +3,8 @@ @using Oqtane.Shared @inject SiteState SiteState @implements IDisposable +@* the following StreamRendering attribute is required - if it is removed the framework will not render the content in static rendering *@ +@attribute [StreamRendering] @if (!string.IsNullOrEmpty(_title)) { diff --git a/Oqtane.Client/UI/Routes.razor b/Oqtane.Client/UI/Routes.razor index 7daa0cc9..f4e63cb1 100644 --- a/Oqtane.Client/UI/Routes.razor +++ b/Oqtane.Client/UI/Routes.razor @@ -3,7 +3,7 @@ @inject IInstallationService InstallationService @inject IJSRuntime JSRuntime @inject SiteState SiteState -@* the following StreamRendering attribute is required - if it is removed the framework will not render a UI on static rendering *@ +@* the following StreamRendering attribute is required - if it is removed the framework will not render the content in static rendering *@ @attribute [StreamRendering] @if (_initialized) From acc562bd7f155f8dd7cc89fd31403caa837999e9 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 19 Feb 2024 15:25:48 -0500 Subject: [PATCH 27/46] inject CSS and JS on static rendering --- Oqtane.Client/UI/ThemeBuilder.razor | 72 +++++++++++++++++++++++++++++ Oqtane.Server/Components/App.razor | 43 +++++++++-------- 2 files changed, 93 insertions(+), 22 deletions(-) diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index 262a919a..b30f8a34 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -53,6 +53,15 @@ { headcontent = AddHeadContent(headcontent, PageState.Page.HeadContent); } + + // stylesheets and scripts on static rendering + if (PageState.RenderMode == RenderModes.Static) + { + headcontent = AddHeadContent(headcontent, ManageStyleSheets(PageState.Page.Resources, PageState.Alias)); + headcontent = AddHeadContent(headcontent, ManageScripts(PageState.Page.Resources, PageState.Alias)); + } + + // send to Head component SiteState.Properties.HeadContent = headcontent; DynamicComponent = builder => @@ -193,4 +202,67 @@ await interop.IncludeScripts(scripts.ToArray()); } } + + private string ManageStyleSheets(List resources, Alias alias) + { + var stylesheets = ""; + if (resources != null) + { + string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"); + int count = 0; + foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Stylesheet)) + { + if (resource.Url.StartsWith("~")) + { + resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(resource.Namespace) + "/").Replace("//", "/"); + } + if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl)) + { + resource.Url = alias.BaseUrl + resource.Url; + } + + if (!stylesheets.Contains(resource.Url, StringComparison.OrdinalIgnoreCase)) + { + count++; + string id = "id=\"app-stylesheet-" + ResourceLevel.Page.ToString().ToLower() + "-" + batch + "-" + count.ToString("00") + "\" "; + stylesheets += "" + Environment.NewLine; + } + } + } + return stylesheets; + } + + private string ManageScripts(List resources, Alias alias) + { + var scripts = ""; + if (resources != null) + { + foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Script && item.Location == ResourceLocation.Head)) + { + var script = CreateScript(resource, alias); + if (!scripts.Contains(script, StringComparison.OrdinalIgnoreCase)) + { + scripts += script + Environment.NewLine; + } + } + } + return scripts; + } + + private string CreateScript(Resource resource, Alias alias) + { + if (!string.IsNullOrEmpty(resource.Url)) + { + var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url; + return ""; + } + else + { + // inline script + return ""; + } + } } diff --git a/Oqtane.Server/Components/App.razor b/Oqtane.Server/Components/App.razor index c566722d..ff8fe475 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -168,34 +168,33 @@ CreateJwtToken(alias); } - // stylesheets - var themes = ThemeRepository.GetThemes().ToList(); - var resources = new List(); - if (string.IsNullOrEmpty(page.ThemeType)) + // initial stylesheets (to prevent FOUC in interactive rendering) + if (_renderMode == RenderModes.Interactive) { - page.ThemeType = site.DefaultThemeType; - } - var theme = themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType)); - if (theme?.Resources != null) - { - resources.AddRange(theme.Resources); - } - var type = Type.GetType(page.ThemeType); - if (type != null) - { - var obj = Activator.CreateInstance(type) as IThemeControl; - if (obj?.Resources != null) + var themes = ThemeRepository.GetThemes().ToList(); + var resources = new List(); + if (string.IsNullOrEmpty(page.ThemeType)) { - resources.AddRange(obj.Resources); + page.ThemeType = site.DefaultThemeType; } + var theme = themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType)); + if (theme?.Resources != null) + { + resources.AddRange(theme.Resources); + } + var type = Type.GetType(page.ThemeType); + if (type != null) + { + var obj = Activator.CreateInstance(type) as IThemeControl; + if (obj?.Resources != null) + { + resources.AddRange(obj.Resources); + } + } + ManageStyleSheets(resources, alias, theme.ThemeName); } - ManageStyleSheets(resources, alias, theme.ThemeName); // scripts - if (_renderMode == RenderModes.Static) - { - ManageScripts(resources, alias); - } if (_renderMode == RenderModes.Interactive && _runtime == Runtimes.Server) { _reconnectScript = CreateReconnectScript(); From cdc0dee6f551d83cbfb613fb247891c8d8833bd3 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 19 Feb 2024 15:47:23 -0500 Subject: [PATCH 28/46] remove commented code --- Oqtane.Client/Modules/ModuleBase.cs | 72 ----------------------------- 1 file changed, 72 deletions(-) diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 896c151a..24d407b9 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -18,7 +18,6 @@ namespace Oqtane.Modules private Logger _logger; private string _urlparametersstate; private Dictionary _urlparameters; - private string _scripts = ""; protected Logger logger => _logger ?? (_logger = new Logger(this)); @@ -67,80 +66,9 @@ namespace Oqtane.Modules return _urlparameters; } } - public string Scripts - { - get - { - return _scripts; - } - } // base lifecycle method for handling JSInterop script registration - protected override void OnParametersSet() - { - //if (ModuleState.RenderMode == RenderModes.Static) - //{ - // _scripts = ""; - // var script = ""; - // List resources = null; - // var type = GetType(); - // if (type.BaseType == typeof(ModuleBase)) - // { - // if (PageState.Page.Resources != null) - // { - // resources = PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level != ResourceLevel.Site && item.Namespace == type.Namespace).ToList(); - // } - // } - // else // modulecontrolbase - // { - // if (Resources != null) - // { - // resources = Resources.Where(item => item.ResourceType == ResourceType.Script).ToList(); - // } - // } - // if (resources != null && resources.Any()) - // { - // var scripts = ""; - // var inline = 0; - // foreach (Resource resource in resources) - // { - // if (!string.IsNullOrEmpty(resource.Url)) - // { - // var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url; - // script += "{" + $"href:'{url}', bundle:'{resource.Bundle ?? ""}', integrity:'{resource.Integrity ?? ""}', crossorigin:'{resource.CrossOrigin ?? ""}', es6module:{resource.ES6Module.ToString().ToLower()}, location:'{resource.Location.ToString().ToLower()}'" + "};"; - // if (scripts.Contains(url)) - // { - // scripts += ((scripts != "") ? ",\n" : "") + script; - // } - // } - // else - // { - // inline += 1; - // script = $"includeScript('{GetType().Namespace.ToLower() + inline.ToString()}', '', '', '', '{resource.Content}', '{resource.Location.ToString().ToLower()}');\n"; - // if (_scripts.Contains(script)) - // { - // _scripts += script; - // } - // } - // } - // if (string.IsNullOrEmpty(scripts)) - // { - // script = "const args = [" + scripts + "];\n"; - // script += "includeScripts.apply(this, args);\n"; - // if (_scripts.Contains(script)) - // { - // _scripts += script; - // } - // } - // } - // if (!string.IsNullOrEmpty(_scripts)) - // { - // _scripts = "\n"; - // } - //} - } - protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) From 121a865bb892333c539c4019c4cd6a10e79364f7 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 19 Feb 2024 16:16:08 -0500 Subject: [PATCH 29/46] refactor App to eliminate 2 database calls --- Oqtane.Server/Components/App.razor | 48 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Oqtane.Server/Components/App.razor b/Oqtane.Server/Components/App.razor index ff8fe475..a08e06fe 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -171,27 +171,9 @@ // initial stylesheets (to prevent FOUC in interactive rendering) if (_renderMode == RenderModes.Interactive) { - var themes = ThemeRepository.GetThemes().ToList(); - var resources = new List(); - if (string.IsNullOrEmpty(page.ThemeType)) - { - page.ThemeType = site.DefaultThemeType; - } - var theme = themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType)); - if (theme?.Resources != null) - { - resources.AddRange(theme.Resources); - } - var type = Type.GetType(page.ThemeType); - if (type != null) - { - var obj = Activator.CreateInstance(type) as IThemeControl; - if (obj?.Resources != null) - { - resources.AddRange(obj.Resources); - } - } - ManageStyleSheets(resources, alias, theme.ThemeName); + var themeType = (string.IsNullOrEmpty(page.ThemeType)) ? site.DefaultThemeType : page.ThemeType; + var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == themeType)); + ManageStyleSheets(GetStyleSheetResources(site, page, theme), alias, theme.ThemeName); } // scripts @@ -216,11 +198,10 @@ if (culture == null) { // get default language for site - var languages = LanguageRepository.GetLanguages(alias.SiteId); - if (languages.Any()) + if (site.Languages.Any()) { // use default language if specified otherwise use first language in collection - culture = (languages.Where(l => l.IsDefault).SingleOrDefault() ?? languages.First()).Code; + culture = (site.Languages.Where(l => l.IsDefault).SingleOrDefault() ?? site.Languages.First()).Code; } else { @@ -596,6 +577,25 @@ } } + private List GetStyleSheetResources(Site site, Page page, Theme theme) + { + var resources = new List(); + if (theme?.Resources != null) + { + resources.AddRange(theme.Resources); + } + var type = Type.GetType(page.ThemeType); + if (type != null) + { + var obj = Activator.CreateInstance(type) as IThemeControl; + if (obj?.Resources != null) + { + resources.AddRange(obj.Resources); + } + } + return resources; + } + private void ManageScripts(List resources, Alias alias) { if (resources != null) From 4271289db098021bf02d6b49bf4b5c7e413e987a Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 19 Feb 2024 16:24:43 -0500 Subject: [PATCH 30/46] further refactoring of ManageStyleSheets --- Oqtane.Server/Components/App.razor | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Oqtane.Server/Components/App.razor b/Oqtane.Server/Components/App.razor index a08e06fe..7ab201fa 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -171,9 +171,7 @@ // initial stylesheets (to prevent FOUC in interactive rendering) if (_renderMode == RenderModes.Interactive) { - var themeType = (string.IsNullOrEmpty(page.ThemeType)) ? site.DefaultThemeType : page.ThemeType; - var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == themeType)); - ManageStyleSheets(GetStyleSheetResources(site, page, theme), alias, theme.ThemeName); + ManageStyleSheets(alias, site, page); } // scripts @@ -550,8 +548,12 @@ CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture))); } - private void ManageStyleSheets(List resources, Alias alias, string name) + private void ManageStyleSheets(Alias alias, Site site, Page page) { + var themeType = (string.IsNullOrEmpty(page.ThemeType)) ? site.DefaultThemeType : page.ThemeType; + var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == themeType)); + var resources = GetStyleSheetResources(page, theme); + if (resources != null) { string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"); @@ -560,7 +562,7 @@ { if (resource.Url.StartsWith("~")) { - resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(name) + "/").Replace("//", "/"); + resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(theme.ThemeName) + "/").Replace("//", "/"); } if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl)) { @@ -577,7 +579,7 @@ } } - private List GetStyleSheetResources(Site site, Page page, Theme theme) + private List GetStyleSheetResources(Page page, Theme theme) { var resources = new List(); if (theme?.Resources != null) From 4db3bafeda17e339828b5ed3c118912b4a71dfb6 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 19 Feb 2024 16:45:54 -0500 Subject: [PATCH 31/46] in static render mode do not filter scripts from head content --- Oqtane.Client/UI/Head.razor | 18 +++++++++--------- Oqtane.Client/UI/ThemeBuilder.razor | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Oqtane.Client/UI/Head.razor b/Oqtane.Client/UI/Head.razor index d1435cc3..3f59e2d4 100644 --- a/Oqtane.Client/UI/Head.razor +++ b/Oqtane.Client/UI/Head.razor @@ -49,15 +49,15 @@ private string RemoveScripts(string headcontent) { - if (!string.IsNullOrEmpty(headcontent)) - { - var index = headcontent.IndexOf("= 0) - { - headcontent = headcontent.Remove(index, headcontent.IndexOf("") + 9 - index); - index = headcontent.IndexOf("= 0) + // { + // headcontent = headcontent.Remove(index, headcontent.IndexOf("") + 9 - index); + // index = headcontent.IndexOf("= 0) { var element = content.Substring(index, content.IndexOf(">", index) - index + 1); - if (!string.IsNullOrEmpty(element) && !element.ToLower().StartsWith(" Date: Mon, 19 Feb 2024 18:30:01 -0500 Subject: [PATCH 32/46] Pass RenderMode and Runtime to Head component --- Oqtane.Client/UI/Head.razor | 24 +++++++++++++++--------- Oqtane.Maui/Head.razor | 14 ++++++++++++-- Oqtane.Server/Components/App.razor | 4 ++-- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/Oqtane.Client/UI/Head.razor b/Oqtane.Client/UI/Head.razor index 3f59e2d4..7929e506 100644 --- a/Oqtane.Client/UI/Head.razor +++ b/Oqtane.Client/UI/Head.razor @@ -19,6 +19,12 @@ private string _title = ""; private string _content = ""; + [Parameter] + public string RenderMode { get; set; } + + [Parameter] + public string Runtime { get; set; } + protected override void OnInitialized() { ((INotifyPropertyChanged)SiteState.Properties).PropertyChanged += PropertyChanged; @@ -49,15 +55,15 @@ private string RemoveScripts(string headcontent) { - // if (!string.IsNullOrEmpty(headcontent)) - // { - // var index = headcontent.IndexOf("= 0) - // { - // headcontent = headcontent.Remove(index, headcontent.IndexOf("") + 9 - index); - // index = headcontent.IndexOf("= 0) + { + headcontent = headcontent.Remove(index, headcontent.IndexOf("") + 9 - index); + index = headcontent.IndexOf(" +@using Oqtane.Shared; + + @code { - Type ComponentType = Type.GetType("Oqtane.UI.Head, Oqtane.Client"); + Type ComponentType = Type.GetType("Oqtane.UI.Head, Oqtane.Client"); + private IDictionary Parameters { get; set; } + + protected override void OnInitialized() + { + Parameters = new Dictionary(); + Parameters.Add(new KeyValuePair("RenderMode", RenderModes.Interactive)); + Parameters.Add(new KeyValuePair("Runtime", Runtimes.Hybrid)); + } } diff --git a/Oqtane.Server/Components/App.razor b/Oqtane.Server/Components/App.razor index 7ab201fa..7326e179 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -50,11 +50,11 @@ @if (_renderMode == RenderModes.Static) { - + } else { - + } @((MarkupString)_headResources) From 0f0d168976077c512025a7fa517ace82a9d0a6b4 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 20 Feb 2024 10:16:19 -0500 Subject: [PATCH 33/46] move stylesheet injection to App component to eliminate FOUC on static rendering --- Oqtane.Client/UI/SiteRouter.razor | 4 +- Oqtane.Client/UI/ThemeBuilder.razor | 9 -- Oqtane.Server/Components/App.razor | 136 +++++++++++++++++++--------- Oqtane.Shared/Models/Route.cs | 9 +- 4 files changed, 103 insertions(+), 55 deletions(-) diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index db59fd4f..882835e9 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -101,8 +101,8 @@ _error = ""; Route route = new Route(_absoluteUri, SiteState.Alias.Path); - int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1; - var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction; + int moduleid = int.Parse(route.ModuleId); + var action = route.Action; var querystring = Utilities.ParseQueryString(route.Query); var returnurl = ""; diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index 8bb4fe47..3f4669e8 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -53,15 +53,6 @@ { headcontent = AddHeadContent(headcontent, PageState.Page.HeadContent); } - - // stylesheets and scripts on static rendering - if (PageState.RenderMode == RenderModes.Static) - { - headcontent = AddHeadContent(headcontent, ManageStyleSheets(PageState.Page.Resources, PageState.Alias)); - headcontent = AddHeadContent(headcontent, ManageScripts(PageState.Page.Resources, PageState.Alias)); - } - - // send to Head component SiteState.Properties.HeadContent = headcontent; DynamicComponent = builder => diff --git a/Oqtane.Server/Components/App.razor b/Oqtane.Server/Components/App.razor index 7326e179..c8603059 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -168,13 +168,15 @@ CreateJwtToken(alias); } - // initial stylesheets (to prevent FOUC in interactive rendering) - if (_renderMode == RenderModes.Interactive) - { - ManageStyleSheets(alias, site, page); - } + // include stylesheets to prevent FOUC + var resources = GetPageResources(alias, site, page, int.Parse(route.ModuleId), route.Action); + ManageStyleSheets(resources); // scripts + if (_renderMode == RenderModes.Static) + { + ManageScripts(resources, alias); + } if (_renderMode == RenderModes.Interactive && _runtime == Runtimes.Server) { _reconnectScript = CreateReconnectScript(); @@ -548,56 +550,108 @@ CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture))); } - private void ManageStyleSheets(Alias alias, Site site, Page page) + private List GetPageResources(Alias alias, Site site, Page page, int moduleid, string action) { + var resources = new List(); + var themeType = (string.IsNullOrEmpty(page.ThemeType)) ? site.DefaultThemeType : page.ThemeType; var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == themeType)); - var resources = GetStyleSheetResources(page, theme); + if (theme != null) + { + resources = AddResources(resources, theme.Resources, ResourceLevel.Page, alias, "Themes", Utilities.GetTypeName(theme.ThemeName)); + } + var type = Type.GetType(themeType); + if (type != null) + { + var obj = Activator.CreateInstance(type) as IThemeControl; + if (obj != null) + { + resources = AddResources(resources, obj.Resources, ResourceLevel.Page, alias, "Themes", type.Namespace); + } + } + foreach (Module module in site.Modules) + { + if (module.PageId == page.PageId || module.ModuleId == moduleid) + { + var typename = ""; + if (module.ModuleDefinition != null) + { + typename = module.ModuleDefinition.ControlTypeTemplate; + resources = AddResources(resources, module.ModuleDefinition.Resources, ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName)); + } + if (Constants.DefaultModuleActions.Contains(action, StringComparer.OrdinalIgnoreCase)) + { + typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action); + } + else + { + typename = typename.Replace(Constants.ActionToken, action); + } + Type moduletype = Type.GetType(typename, false, true); // case insensitive + if (moduletype != null && moduletype.GetInterfaces().Contains(typeof(IModuleControl))) + { + module.ModuleType = Utilities.GetFullTypeName(moduletype.AssemblyQualifiedName); // get actual type name + } + if (moduletype != null && module.ModuleType != "") + { + var obj = Activator.CreateInstance(moduletype) as IModuleControl; + if (obj != null) + { + resources = AddResources(resources, obj.Resources, ResourceLevel.Module, alias, "Modules", type.Namespace); + } + } + } + } + + return resources; + } + + private List AddResources(List pageresources, List resources, ResourceLevel level, Alias alias, string type, string name) + { + if (resources != null) + { + foreach (var resource in resources) + { + if (resource.Level != ResourceLevel.Site) + { + if (resource.Url.StartsWith("~")) + { + 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 + if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower())) + { + resource.Level = level; + resource.Namespace = name; + pageresources.Add(resource); + } + } + } + } + return pageresources; + } + + private void ManageStyleSheets(List resources) + { if (resources != null) { string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"); int count = 0; foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Stylesheet)) { - if (resource.Url.StartsWith("~")) - { - resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(theme.ThemeName) + "/").Replace("//", "/"); - } - if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl)) - { - resource.Url = alias.BaseUrl + resource.Url; - } - - if (!_styleSheets.Contains(resource.Url, StringComparison.OrdinalIgnoreCase)) - { - count++; - string id = "id=\"app-stylesheet-" + ResourceLevel.Page.ToString().ToLower() + "-" + batch + "-" + count.ToString("00") + "\" "; - _styleSheets += "" + Environment.NewLine; - } + count++; + string id = "id=\"app-stylesheet-" + ResourceLevel.Page.ToString().ToLower() + "-" + batch + "-" + count.ToString("00") + "\" "; + _styleSheets += "" + Environment.NewLine; } } } - private List GetStyleSheetResources(Page page, Theme theme) - { - var resources = new List(); - if (theme?.Resources != null) - { - resources.AddRange(theme.Resources); - } - var type = Type.GetType(page.ThemeType); - if (type != null) - { - var obj = Activator.CreateInstance(type) as IThemeControl; - if (obj?.Resources != null) - { - resources.AddRange(obj.Resources); - } - } - return resources; - } - private void ManageScripts(List resources, Alias alias) { if (resources != null) diff --git a/Oqtane.Shared/Models/Route.cs b/Oqtane.Shared/Models/Route.cs index 56dcc3b1..73d648fa 100644 --- a/Oqtane.Shared/Models/Route.cs +++ b/Oqtane.Shared/Models/Route.cs @@ -32,8 +32,8 @@ namespace Oqtane.Models PathAndQuery = uri.PathAndQuery; AliasPath = aliaspath; PagePath = AbsolutePath; - ModuleId = ""; - Action = ""; + ModuleId = "-1"; + Action = Constants.DefaultAction; UrlParameters = ""; if (AliasPath.Length != 0 && PagePath.StartsWith("/" + AliasPath)) @@ -50,6 +50,7 @@ namespace Oqtane.Models if (pos != -1) { ModuleId = PagePath.Substring(pos + 3); + ModuleId = (int.TryParse(ModuleId, out _)) ? ModuleId : "-1"; PagePath = PagePath.Substring(0, pos); } if (ModuleId.Length != 0) @@ -57,8 +58,10 @@ namespace Oqtane.Models pos = ModuleId.IndexOf("/"); if (pos != -1) { - Action = ModuleId.Substring(pos + 1); + Action = ModuleId.Substring(pos + 1); + Action = (!string.IsNullOrEmpty(Action)) ? Action : Constants.DefaultAction; ModuleId = ModuleId.Substring(0, pos); + ModuleId = (int.TryParse(ModuleId, out _)) ? ModuleId : "-1"; } } if (PagePath.StartsWith("/")) From ab09810befd9ae6f4c58bd01b6c4ac11d1eb63f7 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 20 Feb 2024 13:50:09 -0500 Subject: [PATCH 34/46] handle site level scripts in App component --- Oqtane.Client/UI/SiteRouter.razor | 16 ++++-- Oqtane.Server/Components/App.razor | 53 +++++++++++-------- Oqtane.Server/Infrastructure/ServerState.cs | 1 - .../Infrastructure/ServerStateManager.cs | 2 - .../Repository/ModuleDefinitionRepository.cs | 11 ---- Oqtane.Server/Repository/ThemeRepository.cs | 11 ---- Oqtane.Shared/Enums/ResourceLevel.cs | 2 +- 7 files changed, 46 insertions(+), 50 deletions(-) diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 882835e9..fcff50b3 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -567,9 +567,19 @@ // ensure resource does not exist already if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower())) { - resource.Level = level; - resource.Namespace = name; - pageresources.Add(resource); + pageresources.Add(new Resource + { + ResourceType = resource.ResourceType, + Url = resource.Url, + Integrity = resource.Integrity, + CrossOrigin = resource.CrossOrigin, + Bundle = resource.Bundle, + Location = resource.Location, + ES6Module = resource.ES6Module, + Content = resource.Content, + Level = level, + Namespace = name + }); } } } diff --git a/Oqtane.Server/Components/App.razor b/Oqtane.Server/Components/App.razor index c8603059..567db715 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -23,7 +23,6 @@ @inject ISiteService SiteService @inject IThemeRepository ThemeRepository @inject ILanguageRepository LanguageRepository -@inject IServerStateManager ServerStateManager @inject ILocalizationManager LocalizationManager @inject IAliasRepository AliasRepository @inject IUrlMappingRepository UrlMappingRepository @@ -187,11 +186,6 @@ } _headResources += ParseScripts(site.HeadContent); _bodyResources += ParseScripts(site.BodyContent); - var scripts = ServerStateManager.GetServerState(alias.SiteKey).Scripts; - foreach (var script in scripts) - { - AddScript(script, alias); - } // set culture if not specified string culture = Context.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName]; @@ -604,6 +598,16 @@ } } + // site level resources for modules in site + var modules = site.Modules.GroupBy(item => item.ModuleDefinition.ModuleDefinitionName).Select(group => group.First()).ToList(); + foreach (var module in modules) + { + if (module.ModuleDefinition.Resources != null) + { + resources = AddResources(resources, module.ModuleDefinition.Resources.Where(item => item.Level == ResourceLevel.Site).ToList(), ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName)); + } + } + return resources; } @@ -613,24 +617,31 @@ { foreach (var resource in resources) { - if (resource.Level != ResourceLevel.Site) + if (resource.Url.StartsWith("~")) { - if (resource.Url.StartsWith("~")) - { - 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; - } + 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 - if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower())) + // ensure resource does not exist already + if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower())) + { + pageresources.Add(new Resource { - resource.Level = level; - resource.Namespace = name; - pageresources.Add(resource); - } + ResourceType = resource.ResourceType, + Url = resource.Url, + Integrity = resource.Integrity, + CrossOrigin = resource.CrossOrigin, + Bundle = resource.Bundle, + Location = resource.Location, + ES6Module = resource.ES6Module, + Content = resource.Content, + Level = level, + Namespace = name + }); } } } diff --git a/Oqtane.Server/Infrastructure/ServerState.cs b/Oqtane.Server/Infrastructure/ServerState.cs index d51e7d17..acb6f80a 100644 --- a/Oqtane.Server/Infrastructure/ServerState.cs +++ b/Oqtane.Server/Infrastructure/ServerState.cs @@ -7,7 +7,6 @@ namespace Oqtane.Infrastructure { public string SiteKey { get; set; } public List Assemblies { get; set; } = new List(); - public ListScripts { get; set; } = new List(); public bool IsInitialized { get; set; } = false; } } diff --git a/Oqtane.Server/Infrastructure/ServerStateManager.cs b/Oqtane.Server/Infrastructure/ServerStateManager.cs index 7db90d71..66747393 100644 --- a/Oqtane.Server/Infrastructure/ServerStateManager.cs +++ b/Oqtane.Server/Infrastructure/ServerStateManager.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Oqtane.Models; namespace Oqtane.Infrastructure { @@ -22,7 +21,6 @@ namespace Oqtane.Infrastructure serverState = new ServerState(); serverState.SiteKey = siteKey; serverState.Assemblies = new List(); - serverState.Scripts = new List(); serverState.IsInitialized = false; _serverStates.Add(serverState); } diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index 075646e4..563a5a1d 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -225,17 +225,6 @@ namespace Oqtane.Repository } } } - // build list of scripts for site - if (moduledefinition.Resources != null) - { - foreach (var resource in moduledefinition.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Site)) - { - if (!serverState.Scripts.Contains(resource)) - { - serverState.Scripts.Add(resource); - } - } - } } if (permissions.Count == 0) diff --git a/Oqtane.Server/Repository/ThemeRepository.cs b/Oqtane.Server/Repository/ThemeRepository.cs index 86d14d86..ae44815d 100644 --- a/Oqtane.Server/Repository/ThemeRepository.cs +++ b/Oqtane.Server/Repository/ThemeRepository.cs @@ -197,17 +197,6 @@ namespace Oqtane.Repository } } } - // build list of scripts for site - if (theme.Resources != null) - { - foreach (var resource in theme.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Site)) - { - if (!serverState.Scripts.Contains(resource)) - { - serverState.Scripts.Add(resource); - } - } - } } } } diff --git a/Oqtane.Shared/Enums/ResourceLevel.cs b/Oqtane.Shared/Enums/ResourceLevel.cs index 5fb9c9af..be15f349 100644 --- a/Oqtane.Shared/Enums/ResourceLevel.cs +++ b/Oqtane.Shared/Enums/ResourceLevel.cs @@ -2,7 +2,7 @@ namespace Oqtane.Shared { public enum ResourceLevel { - App, + Undefined, Site, Page, Module From f3f223fa227df62e15b3986c345386a18b329aa6 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 20 Feb 2024 14:15:38 -0500 Subject: [PATCH 35/46] fix route constructor --- Oqtane.Client/UI/Pane.razor | 2 +- Oqtane.Shared/Models/Route.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Oqtane.Client/UI/Pane.razor b/Oqtane.Client/UI/Pane.razor index 3412d959..606a83b9 100644 --- a/Oqtane.Client/UI/Pane.razor +++ b/Oqtane.Client/UI/Pane.razor @@ -67,7 +67,7 @@ else // pane matches current pane if (Name.ToLower() == pane.ToLower()) { - if (module.ModuleId == PageState.ModuleId && PageState.Action != Constants.DefaultAction) + if (PageState.ModuleId == module.ModuleId && PageState.Action != Constants.DefaultAction) { var moduleType = Type.GetType(module.ModuleType); if (moduleType != null) diff --git a/Oqtane.Shared/Models/Route.cs b/Oqtane.Shared/Models/Route.cs index 73d648fa..b5a608f3 100644 --- a/Oqtane.Shared/Models/Route.cs +++ b/Oqtane.Shared/Models/Route.cs @@ -49,8 +49,7 @@ namespace Oqtane.Models pos = PagePath.IndexOf("/" + Constants.ModuleDelimiter + "/"); if (pos != -1) { - ModuleId = PagePath.Substring(pos + 3); - ModuleId = (int.TryParse(ModuleId, out _)) ? ModuleId : "-1"; + ModuleId = PagePath.Substring(pos + 3); PagePath = PagePath.Substring(0, pos); } if (ModuleId.Length != 0) From e8c64074f31bd885b782582f92df76a5542e9967 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 20 Feb 2024 14:31:22 -0500 Subject: [PATCH 36/46] fix ModuleMessage so that it is not dependent on ModuleState --- Oqtane.Client/Modules/Controls/ModuleMessage.razor | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Oqtane.Client/Modules/Controls/ModuleMessage.razor b/Oqtane.Client/Modules/Controls/ModuleMessage.razor index 82034165..ffc341bf 100644 --- a/Oqtane.Client/Modules/Controls/ModuleMessage.razor +++ b/Oqtane.Client/Modules/Controls/ModuleMessage.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Controls +@using System.Globalization @inherits ModuleControlBase @inject NavigationManager NavigationManager @@ -10,7 +11,7 @@ { @((MarkupString)"  ")View Details } -
+
From df64abaf9c8e085b7d84593b9223e78494fc4fa7 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 20 Feb 2024 14:54:48 -0500 Subject: [PATCH 37/46] fix #3839 - changing theme in Page Add/Edit throwing exception --- Oqtane.Client/Modules/Admin/Pages/Add.razor | 7 ++++--- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 11 ++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 05a97d7a..27411dcf 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -201,7 +201,7 @@ @if (_themeSettingsType != null) { - @ThemeSettingsComponent + @_themeSettingsComponent } @@ -240,7 +240,7 @@ private PermissionGrid _permissionGrid; private Type _themeSettingsType; private object _themeSettings; - private RenderFragment ThemeSettingsComponent { get; set; } + private RenderFragment _themeSettingsComponent { get; set; } private bool _refresh = false; protected Page _parent = null; protected Dictionary _icons; @@ -337,13 +337,14 @@ private void ThemeSettings() { _themeSettingsType = null; + _themeSettingsComponent = null; var theme = PageState.Site.Themes.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype))); if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType)) { _themeSettingsType = Type.GetType(theme.ThemeSettingsType); if (_themeSettingsType != null) { - ThemeSettingsComponent = builder => + _themeSettingsComponent = builder => { builder.OpenComponent(0, _themeSettingsType); builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); }); diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 3084ef84..a61c28b2 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -234,7 +234,7 @@ @if (_themeSettingsType != null) { - @ThemeSettingsComponent + @_themeSettingsComponent
} @@ -278,7 +278,7 @@ @if (_themeSettingsType != null) { - @ThemeSettingsComponent + @_themeSettingsComponent
} @@ -317,7 +317,7 @@ private string _containertype = "-"; private Type _themeSettingsType; private object _themeSettings; - private RenderFragment ThemeSettingsComponent { get; set; } + private RenderFragment _themeSettingsComponent { get; set; } private string _headcontent; private string _bodycontent; private List _permissions = null; @@ -467,7 +467,7 @@ _containertype = _containers.First().TypeName; ThemeSettings(); StateHasChanged(); - + // if theme chosen is different than default site theme, display warning message to user if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName) { @@ -478,13 +478,14 @@ private void ThemeSettings() { _themeSettingsType = null; + _themeSettingsComponent = null; var theme = PageState.Site.Themes.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype))); if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType)) { _themeSettingsType = Type.GetType(theme.ThemeSettingsType); if (_themeSettingsType != null) { - ThemeSettingsComponent = builder => + _themeSettingsComponent = builder => { builder.OpenComponent(0, _themeSettingsType); builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); }); From 5685f2699b8a52e3332491242f6b48d69818f0c3 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 20 Feb 2024 15:26:15 -0500 Subject: [PATCH 38/46] fix #3838 - Schedule Jobs looping after new install --- Oqtane.Client/Modules/Admin/Jobs/Index.razor | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Jobs/Index.razor b/Oqtane.Client/Modules/Admin/Jobs/Index.razor index ee9fb6fe..6aeee7e7 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Index.razor @@ -49,17 +49,17 @@ else } @code { - private List _jobs; + private List _jobs; public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - - protected override async Task OnParametersSetAsync() - { - _jobs = await JobService.GetJobsAsync(); - if (_jobs.Count == 0) - { - AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning); - } + + protected override async Task OnInitializedAsync() + { + _jobs = await JobService.GetJobsAsync(); + if (_jobs.Count == 0) + { + AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning); + } } private string DisplayStatus(bool isEnabled, bool isExecuting) From 38ead4909e44c47559487178acdaa13bcff5ee30 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 21 Feb 2024 07:29:39 -0500 Subject: [PATCH 39/46] fix ModuleMessage - form name must be a constant value in static rendering or else it will not post correctly --- .../Modules/Controls/ModuleMessage.razor | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/ModuleMessage.razor b/Oqtane.Client/Modules/Controls/ModuleMessage.razor index ffc341bf..76b6b642 100644 --- a/Oqtane.Client/Modules/Controls/ModuleMessage.razor +++ b/Oqtane.Client/Modules/Controls/ModuleMessage.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Controls -@using System.Globalization @inherits ModuleControlBase @inject NavigationManager NavigationManager @@ -7,20 +6,24 @@ { } @code { private string _message = string.Empty; private string _classname = string.Empty; + private string _formname = "ModuleMessageForm"; [Parameter] public string Message { get; set; } @@ -28,6 +31,14 @@ [Parameter] public MessageType Type { get; set; } + protected override void OnInitialized() + { + if (ModuleState != null) + { + _formname += ModuleState.PageModuleId.ToString(); + } + } + protected override void OnParametersSet() { _message = Message; From fa17e7019a26be556286dba6eea0fa302e60b103 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 21 Feb 2024 08:04:24 -0500 Subject: [PATCH 40/46] add variability for StreamRendering based on render mode --- Oqtane.Client/UI/ModuleInstance.razor | 7 +++---- Oqtane.Client/UI/RenderModeBoundary.razor | 3 +++ .../UI/StreamRenderingDisabled.razor | 21 +++++++++++++++++++ Oqtane.Client/UI/StreamRenderingEnabled.razor | 21 +++++++++++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 Oqtane.Client/UI/StreamRenderingDisabled.razor create mode 100644 Oqtane.Client/UI/StreamRenderingEnabled.razor diff --git a/Oqtane.Client/UI/ModuleInstance.razor b/Oqtane.Client/UI/ModuleInstance.razor index 4fd08426..e2dbbbb5 100644 --- a/Oqtane.Client/UI/ModuleInstance.razor +++ b/Oqtane.Client/UI/ModuleInstance.razor @@ -1,19 +1,18 @@ @namespace Oqtane.UI @inject SiteState SiteState -@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Static) +@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive) { - + } else { - + } @code { // this component is on the static side of the render mode boundary // it passes state as serializable parameters across the boundary - // this enables the state to be used by downstream interactive components [CascadingParameter] protected PageState PageState { get; set; } diff --git a/Oqtane.Client/UI/RenderModeBoundary.razor b/Oqtane.Client/UI/RenderModeBoundary.razor index 4e83b473..56833e47 100644 --- a/Oqtane.Client/UI/RenderModeBoundary.razor +++ b/Oqtane.Client/UI/RenderModeBoundary.razor @@ -36,6 +36,9 @@ @code { + // this component is on the interactive side of the render mode boundary + // it receives state as serializable parameters so that the state can be made available to downstream components + private Type ModuleType { get; set; } RenderFragment DynamicComponent { get; set; } diff --git a/Oqtane.Client/UI/StreamRenderingDisabled.razor b/Oqtane.Client/UI/StreamRenderingDisabled.razor new file mode 100644 index 00000000..9b161585 --- /dev/null +++ b/Oqtane.Client/UI/StreamRenderingDisabled.razor @@ -0,0 +1,21 @@ +@attribute [StreamRendering(false)] + +@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Static) +{ + +} +else +{ + +} + +@code { + [Parameter] + public SiteState SiteState { get; set; } + + [Parameter] + public PageState PageState { get; set; } + + [Parameter] + public Module ModuleState { get; set; } +} diff --git a/Oqtane.Client/UI/StreamRenderingEnabled.razor b/Oqtane.Client/UI/StreamRenderingEnabled.razor new file mode 100644 index 00000000..a48bb967 --- /dev/null +++ b/Oqtane.Client/UI/StreamRenderingEnabled.razor @@ -0,0 +1,21 @@ +@attribute [StreamRendering(true)] + +@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Static) +{ + +} +else +{ + +} + +@code { + [Parameter] + public SiteState SiteState { get; set; } + + [Parameter] + public PageState PageState { get; set; } + + [Parameter] + public Module ModuleState { get; set; } +} From 2ad8413c6d0d6df9b82fcc8ca53f2c6e80407aca Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 21 Feb 2024 09:32:31 -0500 Subject: [PATCH 41/46] modified resx --- Oqtane.Client/Resources/Modules/Admin/Register/Index.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx index 77d44196..5a0af9ed 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx @@ -178,6 +178,6 @@ Username: - Already have account, Login? + Already have account? Login now. \ No newline at end of file From 7b38683985ecbae3df7cc5b75bc5c3d07bdce625 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 21 Feb 2024 09:39:06 -0500 Subject: [PATCH 42/46] remove Delete option from Scheduled Jobs - they are automatically created at startup --- Oqtane.Client/Modules/Admin/Jobs/Index.razor | 18 ------------------ .../Resources/Modules/Admin/Jobs/Index.resx | 12 ------------ 2 files changed, 30 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Jobs/Index.razor b/Oqtane.Client/Modules/Admin/Jobs/Index.razor index 6aeee7e7..bc5f5c60 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Index.razor @@ -17,7 +17,6 @@ else
-       @SharedLocalizer["Name"] @@ -28,7 +27,6 @@ else
- @context.Name @DisplayStatus(context.IsEnabled, context.IsExecuting) @@ -112,22 +110,6 @@ else return result; } - private async Task DeleteJob(Job job) - { - try - { - await JobService.DeleteJobAsync(job.JobId); - await logger.LogInformation("Job Deleted {Job}", job); - _jobs = await JobService.GetJobsAsync(); - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Deleting Job {Job} {Error}", job, ex.Message); - AddModuleMessage(Localizer["Error.Job.Delete"], MessageType.Error); - } - } - private async Task StartJob(int jobId) { try diff --git a/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx index 381bae6d..d4c6b52a 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx @@ -144,18 +144,9 @@ Month(s) - - Error Deleting Job - View Logs - - Delete Job - - - Are You Sure You Wish To Delete This Job? - Frequency @@ -165,9 +156,6 @@ Stop - - Delete - Edit From eac7fccbb4ddf70064eaa08705f502863f947bca Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 21 Feb 2024 10:19:55 -0500 Subject: [PATCH 43/46] fix #3827 - Cancel button should redirect to RedirectUrl (using PageState.ReturnUrl) --- Oqtane.Client/Modules/Admin/Login/Index.razor | 15 ++++----------- Oqtane.Client/Modules/Admin/Register/Index.razor | 6 +++--- .../Modules/Admin/UserProfile/Index.razor | 6 +++--- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index 61c9fe9a..f59834f8 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -89,8 +89,6 @@ private bool _alwaysremember = false; private string _code = string.Empty; - private string _returnUrl = string.Empty; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous; public override List Resources => new List() @@ -108,11 +106,6 @@ _togglepassword = SharedLocalizer["ShowPassword"]; - if (PageState.QueryString.ContainsKey("returnurl")) - { - _returnUrl = PageState.QueryString["returnurl"]; - } - if (PageState.QueryString.ContainsKey("name")) { _username = PageState.QueryString["name"]; @@ -213,12 +206,12 @@ // hybrid apps utilize an interactive login var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); authstateprovider.NotifyAuthenticationChanged(); - NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_returnUrl), true)); + NavigationManager.NavigateTo(NavigateUrl(PageState.ReturnUrl, true)); } else { // post back to the Login page so that the cookies are set correctly - var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl }; + var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = WebUtility.UrlEncode(PageState.ReturnUrl) }; string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/"); await interop.SubmitForm(url, fields); } @@ -260,7 +253,7 @@ private void Cancel() { - NavigationManager.NavigateTo(WebUtility.UrlDecode(_returnUrl)); + NavigationManager.NavigateTo(PageState.ReturnUrl); } private async Task Forgot() @@ -328,7 +321,7 @@ private void ExternalLogin() { - NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + _returnUrl), true); + NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + WebUtility.UrlEncode(PageState.ReturnUrl)), true); } } diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index 8526e02e..3835cfaf 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -128,9 +128,9 @@ else if (user != null) { await logger.LogInformation("User Created {Username} {Email}", _username, _email); - if (PageState.QueryString.ContainsKey("returnurl")) + if (!string.IsNullOrEmpty(PageState.ReturnUrl)) { - NavigationManager.NavigateTo(WebUtility.UrlDecode(PageState.QueryString["returnurl"])); + NavigationManager.NavigateTo(PageState.ReturnUrl); } else // legacy behavior { @@ -167,7 +167,7 @@ else private void Cancel() { - NavigationManager.NavigateTo(NavigateUrl(string.Empty)); + NavigationManager.NavigateTo(PageState.ReturnUrl); } private void TogglePassword() diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index 24d5cf6a..c67f3305 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -483,9 +483,9 @@ await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); await logger.LogInformation("User Profile Saved"); - if (PageState.QueryString.ContainsKey("returnurl")) + if (!string.IsNullOrEmpty(PageState.ReturnUrl)) { - NavigationManager.NavigateTo(WebUtility.UrlDecode(PageState.QueryString["returnurl"])); + NavigationManager.NavigateTo(PageState.ReturnUrl); } else // legacy behavior { @@ -551,7 +551,7 @@ private void Cancel() { - NavigationManager.NavigateTo(NavigateUrl(string.Empty)); + NavigationManager.NavigateTo(PageState.ReturnUrl); } private void ProfileChanged(ChangeEventArgs e, string SettingName) From 19ed98f2654d672f7c988235f27951abf60578ac Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 21 Feb 2024 10:50:10 -0500 Subject: [PATCH 44/46] update default site template with latest Blazor messaging --- .../Infrastructure/SiteTemplates/DefaultSiteTemplate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index e572c5f0..7cf27808 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -59,7 +59,7 @@ namespace Oqtane.SiteTemplates }, Content = "

Oqtane is an open source CMS and Application Framework that provides advanced functionality for developing web, mobile, and desktop applications on .NET. It leverages Blazor to compose a fully dynamic digital experience. Whether you are looking for a platform to accelerate your web development efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles and patterns.

" + "

Join Our Community  Clone Our Repo

" + - "

Blazor is an open source and cross-platform web UI framework for building single-page applications using .NET and C#. Blazor applications can be hosted in a variety of ways. Blazor Server uses SignalR (WebSockets) to host your application on a web server and provide a responsive and robust development experience. Blazor WebAssembly relies on Wasm, an open web standard that does not require plugins in order for applications to run natively in a web browser. Blazor Hybrid is part of .NET MAUI and uses a Web View to render components natively on mobile and desktop devices. Razor components can be shared across all of the hosting models without any modification.

" + + "

Blazor is a modern front-end web framework based on HTML, CSS, and C# that helps you build web applications faster. Blazor provides a component-based architecture with server-side rendering and full client-side interactivity in a single solution, where you can switch between server-side and client-side rendering modes and even mix them in the same web page. For desktop or mobile scenarios, Blazor Hybrid is part of .NET MAUI and uses a Web View to render components natively on all modern devices.

" + "

Blazor is a feature of ASP.NET, the popular cross platform development framework from Microsoft that provides powerful tools and libraries for building modern software applications.

" }, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = PaneNames.Default, From 8c5613b18255ce49caba3895c34441a85874f265 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 21 Feb 2024 13:42:15 -0500 Subject: [PATCH 45/46] update module and theme default templates to use ModuleBase methods rather than ModuleInstance methods --- Oqtane.Client/UI/ModuleInstance.razor | 6 +++--- .../Client/Modules/[Owner].Module.[Module]/Settings.razor | 4 ++-- .../External/Client/Containers/ContainerSettings.razor | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Oqtane.Client/UI/ModuleInstance.razor b/Oqtane.Client/UI/ModuleInstance.razor index e2dbbbb5..1504e0b1 100644 --- a/Oqtane.Client/UI/ModuleInstance.razor +++ b/Oqtane.Client/UI/ModuleInstance.razor @@ -21,7 +21,7 @@ else private Module ModuleState { get; set; } - [Obsolete("AddModuleMessage is deprecated. Use ModuleBase.AddModuleMessage instead.", false)] + [Obsolete("AddModuleMessage is deprecated. Use AddModuleMessage in ModuleBase instead.", false)] public void AddModuleMessage(string message, MessageType type) { } @@ -31,12 +31,12 @@ else { } - [Obsolete("ShowProgressIndicator is deprecated. Use ModuleBase.ShowProgressIndicator instead.", false)] + [Obsolete("ShowProgressIndicator is deprecated. Use ShowProgressIndicator in ModuleBase instead.", false)] public void ShowProgressIndicator() { } - [Obsolete("HideProgressIndicator is deprecated. Use ModuleBase.HideProgressIndicator instead.", false)] + [Obsolete("HideProgressIndicator is deprecated. Use HideProgressIndicator in ModuleBase instead.", false)] public void HideProgressIndicator() { } diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].Module.[Module]/Settings.razor b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].Module.[Module]/Settings.razor index a29842ff..3c4cae69 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].Module.[Module]/Settings.razor +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].Module.[Module]/Settings.razor @@ -27,7 +27,7 @@ } catch (Exception ex) { - ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error); + AddModuleMessage(ex.Message, MessageType.Error); } } @@ -41,7 +41,7 @@ } catch (Exception ex) { - ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error); + AddModuleMessage(ex.Message, MessageType.Error); } } } diff --git a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/Containers/ContainerSettings.razor b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/Containers/ContainerSettings.razor index 78cc0d50..0e8b12b4 100644 --- a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/Containers/ContainerSettings.razor +++ b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/Containers/ContainerSettings.razor @@ -30,7 +30,7 @@ } catch (Exception ex) { - ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error); + AddModuleMessage(ex.Message, MessageType.Error); } } @@ -44,7 +44,7 @@ } catch (Exception ex) { - ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error); + AddModuleMessage(ex.Message, MessageType.Error); } } } From e8a0c693c9e531a71ac342033b36b61507a54c53 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 21 Feb 2024 12:04:04 -0800 Subject: [PATCH 46/46] Always reload the page on PageSave() --- Oqtane.Client/Modules/Admin/Site/Index.razor | 23 +------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index dc0230cb..f4fa7988 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -579,9 +579,6 @@ var site = await SiteService.GetSiteAsync(PageState.Site.SiteId); if (site != null) { - bool refresh = false; - bool reload = false; - site.Name = _name; site.HomePageId = (_homepageid != "-" ? int.Parse(_homepageid) : null); site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted)); @@ -595,7 +592,6 @@ if (logofileid != _logofileid) { _logofileid = logofileid; - refresh = true; // needs to be refreshed on client } } int? faviconFieldId = _faviconfilemanager.GetFileId(); @@ -603,17 +599,14 @@ if (site.FaviconFileId != faviconFieldId) { site.FaviconFileId = faviconFieldId; - reload = true; // needs to be reloaded on server } if (site.DefaultThemeType != _themetype) { site.DefaultThemeType = _themetype; - refresh = true; // needs to be refreshed on client } if (site.DefaultContainerType != _containertype) { site.DefaultContainerType = _containertype; - refresh = true; // needs to be refreshed on client } site.AdminContainerType = _admincontainertype; @@ -621,33 +614,28 @@ if (site.HeadContent != _headcontent) { site.HeadContent = _headcontent; - reload = true; } if (site.BodyContent != _bodycontent) { site.BodyContent = _bodycontent; - reload = true; } // PWA if (site.PwaIsEnabled.ToString() != _pwaisenabled) { site.PwaIsEnabled = Boolean.Parse(_pwaisenabled); - reload = true; // needs to be reloaded on server } int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId(); if (pwaappiconfileid == -1) pwaappiconfileid = null; if (site.PwaAppIconFileId != pwaappiconfileid) { site.PwaAppIconFileId = pwaappiconfileid; - reload = true; // needs to be reloaded on server } int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId(); if (pwasplashiconfileid == -1) pwasplashiconfileid = null; if (site.PwaSplashIconFileId != pwasplashiconfileid) { site.PwaSplashIconFileId = pwasplashiconfileid; - reload = true; // needs to be reloaded on server } // hosting model @@ -659,7 +647,6 @@ site.Runtime = _runtime; site.Prerender = bool.Parse(_prerender); site.Hybrid = bool.Parse(_hybrid); - reload = true; // needs to be reloaded on serve } } @@ -686,15 +673,7 @@ await logger.LogInformation("Site Settings Saved {Site}", site); - if (refresh || reload) - { - NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload - } - else - { - AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success); - await ScrollToPageTop(); - } + NavigationManager.NavigateTo(NavigateUrl(), true); // reload } } else