diff --git a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor index bcb500ff..26d96688 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor @@ -56,7 +56,7 @@
- +
@@ -69,7 +69,7 @@
- +
@@ -82,7 +82,7 @@
- +
@@ -176,10 +176,18 @@ { job.Interval = int.Parse(_interval); } - job.StartDate = LocalToUtc(_startDate.Value.Date.Add(_startTime.Value.TimeOfDay)); - job.EndDate = LocalToUtc(_endDate.Value.Date.Add(_endTime.Value.TimeOfDay)); - job.RetentionHistory = int.Parse(_retentionHistory); - job.NextExecution = LocalToUtc(_nextDate.Value.Date.Add(_nextTime.Value.TimeOfDay)); + job.StartDate = _startDate.HasValue && _startTime.HasValue + ? LocalToUtc(_startDate.GetValueOrDefault().Date.Add(_startTime.GetValueOrDefault().TimeOfDay)) + : null; + + job.EndDate = _endDate.HasValue && _endTime.HasValue + ? LocalToUtc(_endDate.GetValueOrDefault().Date.Add(_endTime.GetValueOrDefault().TimeOfDay)) + : null; + + job.NextExecution = _nextDate.HasValue && _nextTime.HasValue + ? LocalToUtc(_nextDate.GetValueOrDefault().Date.Add(_nextTime.GetValueOrDefault().TimeOfDay)) + : null; + job.RetentionHistory = int.Parse(_retentionHistory); try { @@ -198,5 +206,4 @@ AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning); } } - } diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 39681067..a066d159 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -500,32 +500,56 @@ namespace Oqtane.Modules }; } - // date methods + // date conversion methods public DateTime? UtcToLocal(DateTime? datetime) { + // Early return if input is null + if (datetime == null) + return null; + TimeZoneInfo timezone = null; - if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId)) + try { - timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.User.TimeZoneId); + if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId)) + { + timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.User.TimeZoneId); + } + else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId)) + { + timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.Site.TimeZoneId); + } } - else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId)) + catch { - timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.Site.TimeZoneId); + // The time zone ID was not found on the local computer } + return Utilities.UtcAsLocalDateTime(datetime, timezone); } public DateTime? LocalToUtc(DateTime? datetime) { + // Early return if input is null + if (datetime == null) + return null; + TimeZoneInfo timezone = null; - if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId)) + try { - timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.User.TimeZoneId); + if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId)) + { + timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.User.TimeZoneId); + } + else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId)) + { + timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.Site.TimeZoneId); + } } - else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId)) + catch { - timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.Site.TimeZoneId); + // The time zone ID was not found on the local computer } + return Utilities.LocalDateAndTimeAsUtc(datetime, timezone); } diff --git a/Oqtane.Client/Themes/Controls/Container/ModuleActions.razor b/Oqtane.Client/Themes/Controls/Container/ModuleActions.razor index 4b973c48..8672c85a 100644 --- a/Oqtane.Client/Themes/Controls/Container/ModuleActions.razor +++ b/Oqtane.Client/Themes/Controls/Container/ModuleActions.razor @@ -20,6 +20,7 @@ protected override void OnParametersSet() { // trim PageState to mitigate page bloat caused by Blazor serializing/encrypting state when crossing render mode boundaries + // only include properties required by the ModuleActionsInteractive component _pageState = new PageState { Alias = PageState.Alias, diff --git a/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor b/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor index 717cc31e..f38a66c3 100644 --- a/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor @@ -91,6 +91,7 @@ } // trim PageState to mitigate page bloat caused by Blazor serializing/encrypting state when crossing render mode boundaries + // only include properties required by the ControlPanelInteractive component _pageState = new PageState { Alias = PageState.Alias, diff --git a/Oqtane.Client/Themes/Controls/Theme/CookieConsent.razor b/Oqtane.Client/Themes/Controls/Theme/CookieConsent.razor index 66626cbc..207de910 100644 --- a/Oqtane.Client/Themes/Controls/Theme/CookieConsent.razor +++ b/Oqtane.Client/Themes/Controls/Theme/CookieConsent.razor @@ -103,6 +103,9 @@ { var cookieConsentSetting = SettingService.GetSetting(PageState.Site.Settings, "CookieConsent", string.Empty); _enabled = !string.IsNullOrEmpty(cookieConsentSetting); + + if (!_enabled) return; + _optout = cookieConsentSetting == "optout"; _actioned = await CookieConsentService.IsActionedAsync(); @@ -164,4 +167,4 @@ StateHasChanged(); } } -} \ No newline at end of file +} diff --git a/Oqtane.Client/UI/ContainerBuilder.razor b/Oqtane.Client/UI/ContainerBuilder.razor index 6a769aed..76565c6b 100644 --- a/Oqtane.Client/UI/ContainerBuilder.razor +++ b/Oqtane.Client/UI/ContainerBuilder.razor @@ -6,7 +6,7 @@ @if (ComponentType != null && _visible) { - + @if (_useadminborder) {
diff --git a/Oqtane.Client/UI/ModuleInstance.razor b/Oqtane.Client/UI/ModuleInstance.razor index e9a634d3..0fda480f 100644 --- a/Oqtane.Client/UI/ModuleInstance.razor +++ b/Oqtane.Client/UI/ModuleInstance.razor @@ -10,11 +10,11 @@ @((MarkupString)_comment) @if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Static) { - + } else { - + } } @if (PageState.ModuleId == -1) @@ -32,6 +32,7 @@ private bool _prerender; private string _comment; + private PageState _pageState; protected override void OnParametersSet() { @@ -48,11 +49,12 @@ } _comment += " -->"; + _pageState = PageState.Clone(); if (PageState.RenderMode == RenderModes.Static && ModuleState.RenderMode == RenderModes.Interactive) { // trim PageState to mitigate page bloat caused by Blazor serializing/encrypting state when crossing render mode boundaries - // please note that this performance optimization results in the PageState.Pages property not being available for use in Interactive components - PageState.Site.Pages = new List(); + // please note that this performance optimization results in the PageState.Pages property not being available for use in downstream Interactive components + _pageState.Site.Pages = new List(); } } diff --git a/Oqtane.Client/UI/PageState.cs b/Oqtane.Client/UI/PageState.cs index a038a81e..91cf158c 100644 --- a/Oqtane.Client/UI/PageState.cs +++ b/Oqtane.Client/UI/PageState.cs @@ -37,5 +37,34 @@ namespace Oqtane.UI { get { return Site?.Languages; } } + + public PageState Clone() + { + return new PageState + { + Alias = Alias, + Site = Site, + Page = Page, + Modules = Modules, + User = User, + Uri = Uri, + Route = Route, + QueryString = QueryString, + UrlParameters = UrlParameters, + ModuleId = ModuleId, + Action = Action, + EditMode = EditMode, + LastSyncDate = LastSyncDate, + RenderMode = RenderMode, + Runtime = Runtime, + VisitorId = VisitorId, + RemoteIPAddress = RemoteIPAddress, + ReturnUrl = ReturnUrl, + IsInternalNavigation = IsInternalNavigation, + RenderId = RenderId, + Refresh = Refresh, + AllowCookies = AllowCookies + }; + } } } diff --git a/Oqtane.Client/UI/Pane.razor b/Oqtane.Client/UI/Pane.razor index 70226c2e..633cd4fd 100644 --- a/Oqtane.Client/UI/Pane.razor +++ b/Oqtane.Client/UI/Pane.razor @@ -29,7 +29,7 @@ else RenderFragment DynamicComponent { get; set; } protected override void OnParametersSet() - { + { if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList) && PageState.Action == Constants.DefaultAction) { _useadminborder = true; @@ -45,12 +45,6 @@ else { foreach (Module module in PageState.Modules) { - // set renderid - this allows the framework to determine which components should be rendered when PageState changes - if (module.RenderId != PageState.RenderId) - { - module.RenderId = PageState.RenderId; - } - var pane = module.Pane; if (module.ModuleId == PageState.ModuleId && PageState.Action != Constants.DefaultAction) { @@ -101,7 +95,7 @@ else if (authorized) { - CreateComponent(builder, module, module.PageModuleId); + CreateComponent(builder, module); } } } @@ -112,7 +106,7 @@ else // check if user is authorized to view module if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList)) { - CreateComponent(builder, module, -1); + CreateComponent(builder, module); } } } @@ -121,14 +115,11 @@ else }; } - private void CreateComponent(RenderTreeBuilder builder, Module module, int key) + private void CreateComponent(RenderTreeBuilder builder, Module module) { builder.OpenComponent(0, typeof(ContainerBuilder)); builder.AddAttribute(1, "ModuleState", module); - if (key != -1) - { - builder.SetKey(module.PageModuleId); - } + builder.SetKey(module.PageModuleId); builder.CloseComponent(); } } diff --git a/Oqtane.Client/UI/RenderModeBoundary.razor b/Oqtane.Client/UI/RenderModeBoundary.razor index 2c451f29..dbaac7f5 100644 --- a/Oqtane.Client/UI/RenderModeBoundary.razor +++ b/Oqtane.Client/UI/RenderModeBoundary.razor @@ -4,8 +4,8 @@ @inject ILogService LoggingService @inherits ErrorBoundary - - + + @if (CurrentException is null) { @if (ModuleType != null) diff --git a/Oqtane.Client/UI/Routes.razor b/Oqtane.Client/UI/Routes.razor index 20dc0a09..6081966c 100644 --- a/Oqtane.Client/UI/Routes.razor +++ b/Oqtane.Client/UI/Routes.razor @@ -48,12 +48,18 @@ private bool _initialized = false; private bool _installed = false; - private string _display = "display: none;"; + private string _display = ""; private PageState _pageState { get; set; } protected override async Task OnParametersSetAsync() { + if (PageState != null && PageState.RenderMode == RenderModes.Interactive && PageState.Site.Prerender) + { + // prevents flash on initial interactive page load when using prerendering + _display = "display: none;"; + } + SiteState.AntiForgeryToken = AntiForgeryToken; SiteState.AuthorizationToken = AuthorizationToken; SiteState.Platform = Platform; @@ -61,7 +67,7 @@ if (Runtime == Runtimes.Hybrid) { - var installation = await InstallationService.IsInstalled(); + var installation = await InstallationService.IsInstalled(); _installed = installation.Success; if (installation.Alias != null) { @@ -73,8 +79,8 @@ if (PageState != null) { _pageState = PageState; - SiteState.Alias = PageState.Alias; - SiteState.RemoteIPAddress = (PageState != null) ? PageState.RemoteIPAddress : ""; + SiteState.Alias = _pageState.Alias; + SiteState.RemoteIPAddress = _pageState.RemoteIPAddress; _installed = true; } } @@ -85,9 +91,7 @@ { if (firstRender) { - // prevents flash on initial interactive page load _display = ""; - StateHasChanged(); } } diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 3c3c81fe..a345a7f0 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -71,7 +71,7 @@ { if (PageState == null || PageState.Refresh) { - await Refresh(); + await Refresh(false); } } @@ -79,7 +79,7 @@ { _absoluteUri = args.Location; _isInternalNavigation = true; - await Refresh(); + await Refresh(true); } Task IHandleAfterRender.OnAfterRenderAsync() @@ -93,7 +93,7 @@ } [SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")] - private async Task Refresh() + private async Task Refresh(bool locationChanged) { Site site = null; Page page = null; @@ -103,6 +103,7 @@ var refresh = false; var lastsyncdate = DateTime.MinValue; var visitorId = -1; + var renderid = Guid.Empty; _error = ""; Route route = new Route(_absoluteUri, SiteState.Alias.Path); @@ -288,11 +289,21 @@ modules = PageState.Modules; } + // renderid allows the framework to determine which module components should be rendered on a page + if (PageState == null || locationChanged) + { + renderid = Guid.NewGuid(); + } + else + { + renderid = PageState.RenderId; + } + // load additional metadata for current page page = ProcessPage(page, site, user, SiteState.Alias, action); // load additional metadata for modules - (page, modules) = ProcessModules(site, page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType, SiteState.Alias); + (page, modules) = ProcessModules(site, page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType, SiteState.Alias, renderid); //cookie consent var _allowCookies = PageState?.AllowCookies; @@ -324,7 +335,7 @@ RemoteIPAddress = SiteState.RemoteIPAddress, ReturnUrl = returnurl, IsInternalNavigation = _isInternalNavigation, - RenderId = Guid.NewGuid(), + RenderId = renderid, Refresh = false, AllowCookies = _allowCookies.GetValueOrDefault(true) }; @@ -447,7 +458,7 @@ return page; } - private (Page Page, List Modules) ProcessModules(Site site, Page page, List modules, int moduleid, string action, string defaultcontainertype, Alias alias) + private (Page Page, List Modules) ProcessModules(Site site, Page page, List modules, int moduleid, string action, string defaultcontainertype, Alias alias, Guid renderid) { var paneindex = new Dictionary(); @@ -592,6 +603,8 @@ { module.ContainerType = defaultcontainertype; } + + module.RenderId = renderid; } foreach (Module module in modules.Where(item => item.PageId == page.PageId)) diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 2068c0ea..56a682a9 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -46,9 +46,9 @@ - + - +