diff --git a/Oqtane.Client/Modules/Admin/Jobs/Index.razor b/Oqtane.Client/Modules/Admin/Jobs/Index.razor index ee9fb6fe..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) @@ -49,17 +47,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) @@ -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/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index 64c1d93f..f59834f8 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"] + } + } } @@ -84,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() @@ -103,11 +106,6 @@ _togglepassword = SharedLocalizer["ShowPassword"]; - if (PageState.QueryString.ContainsKey("returnurl")) - { - _returnUrl = PageState.QueryString["returnurl"]; - } - if (PageState.QueryString.ContainsKey("name")) { _username = PageState.QueryString["name"]; @@ -208,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); } @@ -255,7 +253,7 @@ private void Cancel() { - NavigationManager.NavigateTo(WebUtility.UrlDecode(_returnUrl)); + NavigationManager.NavigateTo(PageState.ReturnUrl); } private async Task Forgot() @@ -323,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/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..a2cc9396 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); }); @@ -630,11 +631,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 diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index 98fc932f..1239a512 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -62,6 +62,11 @@
+ @if (_allowsitelogin) + { +

+ @Localizer["Login"] + } @@ -84,12 +89,14 @@ else private string _email = string.Empty; private string _displayname = string.Empty; private bool _userCreated = false; + 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() @@ -158,7 +165,7 @@ else private void Cancel() { - NavigationManager.NavigateTo(NavigateUrl(string.Empty)); + NavigationManager.NavigateTo(PageState.ReturnUrl); } private void TogglePassword() 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 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) diff --git a/Oqtane.Client/Modules/Controls/ModuleMessage.razor b/Oqtane.Client/Modules/Controls/ModuleMessage.razor index 82034165..76b6b642 100644 --- a/Oqtane.Client/Modules/Controls/ModuleMessage.razor +++ b/Oqtane.Client/Modules/Controls/ModuleMessage.razor @@ -6,20 +6,24 @@ { } @code { private string _message = string.Empty; private string _classname = string.Empty; + private string _formname = "ModuleMessageForm"; [Parameter] public string Message { get; set; } @@ -27,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; diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 1bb76dca..24d407b9 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 { @@ -90,7 +89,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/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 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..5a0af9ed 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 now. + \ No newline at end of file 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; 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 /// /// /// 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 { 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 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"]; + } } } diff --git a/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs b/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs index 49f21038..92bd9afe 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-X/YkDZyjTf4wyc2Vy16YGCPHwAY8rZJY+POgokZjQB2mhIRFJCckEGc6YyX9eNsPfn0PzThEuNs+uaomE5CO6A==", CrossOrigin = "anonymous" } } }; } diff --git a/Oqtane.Client/UI/Head.razor b/Oqtane.Client/UI/Head.razor index 75a6101f..7929e506 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)) { @@ -17,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; @@ -47,7 +55,7 @@ private string RemoveScripts(string headcontent) { - if (!string.IsNullOrEmpty(headcontent)) + if (!string.IsNullOrEmpty(headcontent) && RenderMode == RenderModes.Interactive) { var index = headcontent.IndexOf("= 0) diff --git a/Oqtane.Client/UI/InteractiveRenderMode.cs b/Oqtane.Client/UI/InteractiveRenderMode.cs index 1094aafb..7f21f673 100644 --- a/Oqtane.Client/UI/InteractiveRenderMode.cs +++ b/Oqtane.Client/UI/InteractiveRenderMode.cs @@ -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 new InteractiveServerRenderMode(prerender: prerender); } } } diff --git a/Oqtane.Client/UI/ModuleInstance.razor b/Oqtane.Client/UI/ModuleInstance.razor index 1aea64b6..1504e0b1 100644 --- a/Oqtane.Client/UI/ModuleInstance.razor +++ b/Oqtane.Client/UI/ModuleInstance.razor @@ -1,18 +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 so that the state can be used by downstream interactive components + // it passes state as serializable parameters across the boundary [CascadingParameter] protected PageState PageState { get; set; } @@ -20,7 +20,8 @@ else [CascadingParameter] 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) { } @@ -30,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.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.Client/UI/RenderModeBoundary.razor b/Oqtane.Client/UI/RenderModeBoundary.razor index 71ca8f3f..56833e47 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,19 +23,22 @@ { } - - - } -} -else -{ - @if (!string.IsNullOrEmpty(_error)) - { - - } -} + } + } + else + { + @if (!string.IsNullOrEmpty(_error)) + { + + } + } + + @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/Routes.razor b/Oqtane.Client/UI/Routes.razor index 5abd9703..f4e63cb1 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 the content in static rendering *@ +@attribute [StreamRendering] @if (_initialized) { diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 752ff996..fcff50b3 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(); @@ -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 = ""; @@ -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.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; } +} diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index ccb80cf2..1ecba14b 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 => @@ -96,7 +72,7 @@ while (index >= 0) { var element = content.Substring(index, content.IndexOf(">", index) - index + 1); - if (!string.IsNullOrEmpty(element) && !element.ToLower().StartsWith("(); foreach (var attribute in attributes) { if (attribute.Contains("=")) @@ -190,6 +167,12 @@ case "type": type = value[1]; break; + default: + if(!string.IsNullOrWhiteSpace(value[0]) && value[0].StartsWith("data-")) + { + dataAttributes.Add(value[0], value[1]); + } + break; } } } @@ -197,7 +180,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 { @@ -217,4 +200,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.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 @@ - + diff --git a/Oqtane.Maui/Head.razor b/Oqtane.Maui/Head.razor index e94898e5..43c73f47 100644 --- a/Oqtane.Maui/Head.razor +++ b/Oqtane.Maui/Head.razor @@ -1,6 +1,16 @@ - +@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 e8103ca2..567db715 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -21,17 +21,15 @@ @inject IConfigManager ConfigManager @inject ITenantManager TenantManager @inject ISiteService SiteService -@inject IPageRepository PageRepository @inject IThemeRepository ThemeRepository @inject ILanguageRepository LanguageRepository -@inject IServerStateManager ServerStateManager @inject ILocalizationManager LocalizationManager @inject IAliasRepository AliasRepository @inject IUrlMappingRepository UrlMappingRepository @inject IVisitorRepository VisitorRepository @inject IJwtManager JwtManager -@if (_pageState != null) +@if (_initialized) { @@ -51,11 +49,11 @@ @if (_renderMode == RenderModes.Static) { - + } else { - + } @((MarkupString)_headResources) @@ -93,6 +91,7 @@ } @code { + private bool _initialized = false; private string _renderMode = RenderModes.Interactive; private string _runtime = Runtimes.Server; private bool _prerender = true; @@ -139,12 +138,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); @@ -161,34 +167,15 @@ CreateJwtToken(alias); } - // stylesheets - 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); + // 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); } - - // scripts if (_renderMode == RenderModes.Interactive && _runtime == Runtimes.Server) { _reconnectScript = CreateReconnectScript(); @@ -199,22 +186,16 @@ } _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]; 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 { @@ -266,6 +247,7 @@ _message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name"; } } + _initialized = true; } private void HandleDefaultAliasRedirect(Alias alias, string url) @@ -562,7 +544,111 @@ CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture))); } - private void ManageStyleSheets(List resources, Alias alias, string name) + 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)); + 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); + } + } + } + } + + // 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; + } + + 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.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())) + { + 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 + }); + } + } + } + return pageresources; + } + + private void ManageStyleSheets(List resources) { if (resources != null) { @@ -570,21 +656,9 @@ 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(name) + "/").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; } } } 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/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; 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/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/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, 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.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/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 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.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.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/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; 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%; 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); } } } diff --git a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/ThemeInfo.cs b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/ThemeInfo.cs index cac21823..f56ef74c 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-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.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" } } }; 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%; 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 + } } } } 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 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(); + } +} diff --git a/Oqtane.Shared/Models/Route.cs b/Oqtane.Shared/Models/Route.cs index 56dcc3b1..b5a608f3 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)) @@ -49,7 +49,7 @@ namespace Oqtane.Models pos = PagePath.IndexOf("/" + Constants.ModuleDelimiter + "/"); if (pos != -1) { - ModuleId = PagePath.Substring(pos + 3); + ModuleId = PagePath.Substring(pos + 3); PagePath = PagePath.Substring(0, pos); } if (ModuleId.Length != 0) @@ -57,8 +57,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("/")) 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";