diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index 7f4f35f1..225a6e72 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -48,6 +48,18 @@ +
+ +
+ +
+
+
+ +
+ +
+
@@ -114,7 +126,7 @@ -@code { + @code { public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; public override string Title => "Module Settings"; @@ -141,7 +153,8 @@ private DateTime createdon; private string modifiedby; private DateTime modifiedon; - + private DateTime? _effectivedate = null; + private DateTime? _expirydate = null; protected override void OnInitialized() { _module = ModuleState.ModuleDefinition.Name; @@ -156,6 +169,8 @@ createdon = ModuleState.CreatedOn; modifiedby = ModuleState.ModifiedBy; modifiedon = ModuleState.ModifiedOn; + _effectivedate = ModuleState.EffectiveDate; + _expirydate = ModuleState.ExpiryDate; if (ModuleState.ModuleDefinition != null) { @@ -214,12 +229,20 @@ var interop = new Interop(JSRuntime); if (await interop.FormValid(form)) { + if (!string.IsNullOrEmpty(_title)) { + if (!ValidateEffectiveExpiryDates(_effectivedate, _expirydate)) + { + AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning); + return; + } var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); pagemodule.PageId = int.Parse(_pageId); pagemodule.Title = _title; pagemodule.Pane = _pane; + pagemodule.EffectiveDate = _effectivedate; + pagemodule.ExpiryDate = _expirydate; pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty; if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType) { @@ -269,5 +292,33 @@ AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning); } } + private bool ValidateEffectiveExpiryDates(DateTime? effectiveDate, DateTime? expiryDate) + { + // Check if both dates are null, in which case the validation passes + if (effectiveDate == DateTime.MinValue && expiryDate == DateTime.MinValue) + { + return true; + } + // Check if EffectiveDate is not null and ExpiryDate is null + if (effectiveDate != DateTime.MinValue && expiryDate == DateTime.MinValue) + { + return true; + } + + // Check if EffectiveDate is null and ExpiryDate is not null + if (effectiveDate == DateTime.MinValue && expiryDate != DateTime.MinValue) + { + return true; + } + + // Check if ExpiryDate is not null and EffectiveDate is after ExpiryDate + if (expiryDate != DateTime.MinValue && effectiveDate != DateTime.MinValue && effectiveDate > expiryDate) + { + return false; + } + + // If none of the above conditions are met, validation passes + return true; + } } diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 6e95fdf3..c03a7e0a 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -119,6 +119,18 @@
+
+ +
+ +
+
+
+ +
+ +
+
@@ -233,6 +245,8 @@ protected Page _parent = null; protected Dictionary _icons; private string _iconresources = ""; + private DateTime? _effectivedate = null; + private DateTime? _expirydate = null; protected override async Task OnInitializedAsync() { @@ -265,6 +279,8 @@ _children.Add(p); } } + _effectivedate = PageState.Page.EffectiveDate; + _expirydate = PageState.Page.ExpiryDate; ThemeSettings(); _initialized = true; } @@ -274,18 +290,18 @@ AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error); } } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Page {Error}", ex.Message); - AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error); - } - } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Page {Error}", ex.Message); + AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error); + } + } - private async void ParentChanged(ChangeEventArgs e) - { - try - { - _parentid = (string)e.Value; + private async void ParentChanged(ChangeEventArgs e) + { + try + { + _parentid = (string)e.Value; _children = new List(); foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid)))) { @@ -295,13 +311,13 @@ } } StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message); - AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error); - } - } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message); + AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error); + } + } private void ThemeChanged(ChangeEventArgs e) { @@ -318,109 +334,116 @@ } } - private void ThemeSettings() - { - _themeSettingsType = null; + private void ThemeSettings() + { + _themeSettingsType = 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 => - { - builder.OpenComponent(0, _themeSettingsType); - builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); }); - builder.CloseComponent(); - }; - } - _refresh = true; - } - } + if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType)) + { + _themeSettingsType = Type.GetType(theme.ThemeSettingsType); + if (_themeSettingsType != null) + { + ThemeSettingsComponent = builder => + { + builder.OpenComponent(0, _themeSettingsType); + builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); }); + builder.CloseComponent(); + }; + } + _refresh = true; + } + } - private async Task SavePage() - { - validated = true; - var interop = new Interop(JSRuntime); - if (await interop.FormValid(form)) - { - Page page = null; - try - { + private async Task SavePage() + { + validated = true; + var interop = new Interop(JSRuntime); + if (await interop.FormValid(form)) + { + Page page = null; + try + { + if (!ValidateEffectiveExpiryDates(_effectivedate, _expirydate)) + { + AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning); + return; + } if (!string.IsNullOrEmpty(_themetype) && !string.IsNullOrEmpty(_containertype)) - { - page = new Page(); - page.SiteId = PageState.Page.SiteId; - page.Name = _name; + { + page = new Page(); + page.SiteId = PageState.Page.SiteId; + page.Name = _name; - if (string.IsNullOrEmpty(_path)) - { - _path = _name; - } - if (_path.Contains("/")) - { - if (_path.EndsWith("/") && _path != "/") - { - _path = _path.Substring(0, _path.Length - 1); - } - _path = _path.Substring(_path.LastIndexOf("/") + 1); - } + if (string.IsNullOrEmpty(_path)) + { + _path = _name; + } + if (_path.Contains("/")) + { + if (_path.EndsWith("/") && _path != "/") + { + _path = _path.Substring(0, _path.Length - 1); + } + _path = _path.Substring(_path.LastIndexOf("/") + 1); + } - if (_parentid == "-1") - { - page.ParentId = null; - page.Path = Utilities.GetFriendlyUrl(_path); - } - else - { - page.ParentId = Int32.Parse(_parentid); - var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault(); - if (parent.Path == string.Empty) - { - page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path); - } - else - { - page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); - } - } + if (_parentid == "-1") + { + page.ParentId = null; + page.Path = Utilities.GetFriendlyUrl(_path); + } + else + { + page.ParentId = Int32.Parse(_parentid); + var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault(); + if (parent.Path == string.Empty) + { + page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path); + } + else + { + page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); + } + } - var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId); - if (_pages.Any(item => item.Path == page.Path)) - { - AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning); - return; - } + var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId); + if (_pages.Any(item => item.Path == page.Path)) + { + AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning); + return; + } - if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower())) - { - AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning); - return; - } + if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower())) + { + AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning); + return; + } - Page child; - switch (_insert) - { - case "<<": - page.Order = 0; - break; - case "<": - child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault(); - page.Order = child.Order - 1; - break; - case ">": - child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault(); - page.Order = child.Order + 1; - break; - case ">>": - page.Order = int.MaxValue; - break; - } + Page child; + switch (_insert) + { + case "<<": + page.Order = 0; + break; + case "<": + child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault(); + page.Order = child.Order - 1; + break; + case ">": + child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault(); + page.Order = child.Order + 1; + break; + case ">>": + page.Order = int.MaxValue; + break; + } - page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation)); - page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable)); + page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation)); + page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable)); page.Url = _url; page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable)); + page.EffectiveDate = _effectivedate; + page.ExpiryDate = _expirydate; page.UserId = null; // appearance @@ -490,4 +513,33 @@ { _icon = NewIcon; } + private bool ValidateEffectiveExpiryDates(DateTime? effectiveDate, DateTime? expiryDate) + { + // Check if both dates are null, in which case the validation passes + if (effectiveDate == DateTime.MinValue && expiryDate == DateTime.MinValue) + { + return true; + } + + // Check if EffectiveDate is not null and ExpiryDate is null + if (effectiveDate != DateTime.MinValue && expiryDate == DateTime.MinValue) + { + return true; + } + + // Check if EffectiveDate is null and ExpiryDate is not null + if (effectiveDate == DateTime.MinValue && expiryDate != DateTime.MinValue) + { + return true; + } + + // Check if ExpiryDate is not null and EffectiveDate is after ExpiryDate + if (expiryDate != DateTime.MinValue && effectiveDate != DateTime.MinValue && effectiveDate > expiryDate) + { + return false; + } + + // If none of the above conditions are met, validation passes + return true; + } } diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 222d0119..3b64188b 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -134,6 +134,18 @@
+
+ +
+ +
+
+
+ +
+ +
+
@@ -322,6 +334,8 @@ protected Page _parent = null; protected Dictionary _icons; private string _iconresources = ""; + private DateTime? _effectivedate = null; + private DateTime? _expirydate = null; protected override async Task OnInitializedAsync() { @@ -370,6 +384,8 @@ } _url = _page.Url; _icon = _page.Icon; + _effectivedate = _page.EffectiveDate; + _expirydate = _page.ExpiryDate; _ispersonalizable = _page.IsPersonalizable.ToString(); // appearance @@ -487,6 +503,11 @@ { try { + if (!ValidateEffectiveExpiryDates(_effectivedate, _expirydate)) + { + AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning); + return; + } if (!string.IsNullOrEmpty(_themetype) && _containertype != "-") { string currentPath = _page.Path; @@ -563,6 +584,8 @@ _page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable)); _page.Url = _url; _page.Icon = _icon ?? string.Empty; + _page.EffectiveDate = _effectivedate; + _page.ExpiryDate = _expirydate; _page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable)); // appearance @@ -666,4 +689,33 @@ { _icon = NewIcon; } + private bool ValidateEffectiveExpiryDates(DateTime? effectiveDate, DateTime? expiryDate) + { + // Check if both dates are null, in which case the validation passes + if (effectiveDate == DateTime.MinValue && expiryDate == DateTime.MinValue) + { + return true; + } + + // Check if EffectiveDate is not null and ExpiryDate is null + if (effectiveDate != DateTime.MinValue && expiryDate == DateTime.MinValue) + { + return true; + } + + // Check if EffectiveDate is null and ExpiryDate is not null + if (effectiveDate == DateTime.MinValue && expiryDate != DateTime.MinValue) + { + return true; + } + + // Check if ExpiryDate is not null and EffectiveDate is after ExpiryDate + if (expiryDate != DateTime.MinValue && effectiveDate != DateTime.MinValue && effectiveDate > expiryDate) + { + return false; + } + + // If none of the above conditions are met, validation passes + return true; + } } diff --git a/Oqtane.Client/Modules/Admin/Users/Roles.razor b/Oqtane.Client/Modules/Admin/Users/Roles.razor index 8766c46a..990aea43 100644 --- a/Oqtane.Client/Modules/Admin/Users/Roles.razor +++ b/Oqtane.Client/Modules/Admin/Users/Roles.razor @@ -34,13 +34,13 @@ else
- +
- +
@@ -75,8 +75,8 @@ else private string name = string.Empty; private List roles; private int roleid = -1; - private DateTime? effectivedate = null; - private DateTime? expirydate = null; + private DateTime? _effectivedate = null; + private DateTime? _expirydate = null; private List userroles; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; @@ -92,7 +92,7 @@ else if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) { roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true); - roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated); + roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated); } else { @@ -127,11 +127,16 @@ else { if (roleid != -1) { + if (!ValidateEffectiveExpiryDates(_effectivedate,_expirydate)) + { + AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning); + return; + } var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault(); if (userrole != null) { - userrole.EffectiveDate = effectivedate; - userrole.ExpiryDate = expirydate; + userrole.EffectiveDate = _effectivedate; + userrole.ExpiryDate = _expirydate; await UserRoleService.UpdateUserRoleAsync(userrole); } else @@ -139,15 +144,15 @@ else userrole = new UserRole(); userrole.UserId = userid; userrole.RoleId = roleid; - userrole.EffectiveDate = effectivedate; - userrole.ExpiryDate = expirydate; + userrole.EffectiveDate = _effectivedate; + userrole.ExpiryDate = _expirydate; await UserRoleService.AddUserRoleAsync(userrole); } await logger.LogInformation("User Assigned To Role {UserRole}", userrole); AddModuleMessage(Localizer["Success.User.AssignRole"], MessageType.Success); await GetUserRoles(); - StateHasChanged(); + StateHasChanged(); } else { @@ -177,4 +182,34 @@ else AddModuleMessage(Localizer["Error.User.RemoveRole"], MessageType.Error); } } + + private bool ValidateEffectiveExpiryDates(DateTime? effectiveDate, DateTime? expiryDate) + { + // Check if both dates are null, in which case the validation passes + if (effectiveDate == DateTime.MinValue && expiryDate == DateTime.MinValue) + { + return true; + } + + // Check if EffectiveDate is not null and ExpiryDate is null + if (effectiveDate != DateTime.MinValue && expiryDate == DateTime.MinValue) + { + return true; + } + + // Check if EffectiveDate is null and ExpiryDate is not null + if (effectiveDate == DateTime.MinValue && expiryDate != DateTime.MinValue) + { + return true; + } + + // Check if ExpiryDate is not null and EffectiveDate is after ExpiryDate + if (expiryDate != DateTime.MinValue && effectiveDate != DateTime.MinValue && effectiveDate > expiryDate) + { + return false; + } + + // If none of the above conditions are met, validation passes + return true; + } } diff --git a/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx b/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx index 5b7849ec..722fa2f1 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx @@ -165,4 +165,16 @@ Pane: + + The date that this module is active + + + Effective Date: + + + The date that this module expires + + + Expiry Date: + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx b/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx index 62972b72..9ebed89a 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx @@ -255,4 +255,16 @@ Theme Settings + + The date that this page is active + + + Effective Date: + + + The date that this page expires + + + Expiry Date: + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx b/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx index 984e6461..478e7616 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx @@ -285,4 +285,16 @@ Please Note That Overriding The Default Site Theme With An Unrelated Page Theme May Result In Compatibility Issues For Your Site + + The date that this page is active + + + Effective Date: + + + The date that this page expires + + + Expiry Date: + \ No newline at end of file diff --git a/Oqtane.Client/Resources/SharedResources.resx b/Oqtane.Client/Resources/SharedResources.resx index b00c7ce9..6152a066 100644 --- a/Oqtane.Client/Resources/SharedResources.resx +++ b/Oqtane.Client/Resources/SharedResources.resx @@ -438,4 +438,7 @@ Test + + Effective Date cannot be after Expiry Date. + \ No newline at end of file diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 51877aa0..51805407 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -258,42 +258,51 @@ } } } + + bool isAdminOrHost = false; + if(user != null) + { + isAdminOrHost = user.Roles.Contains(RoleNames.Host) || user.Roles.Contains(RoleNames.Admin); + } - if (page != null) + if (page != null && (isAdminOrHost || IsPageModuleVisible(page.EffectiveDate, page.ExpiryDate))) { // check if user is authorized to view page if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.PermissionList)) { - // load additional metadata for current page - page = ProcessPage(page, site, user, SiteState.Alias); - - // load additional metadata for modules - (page, site.Modules) = ProcessModules(page, site.Modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType, SiteState.Alias); - - // populate page state (which acts as a client-side cache for subsequent requests) - _pagestate = new PageState + if (isAdminOrHost || IsPageModuleVisible(page.EffectiveDate, page.ExpiryDate)) { - Alias = SiteState.Alias, - Site = site, - Page = page, - User = user, - Uri = new Uri(_absoluteUri, UriKind.Absolute), - Route = route, - QueryString = querystring, - UrlParameters = route.UrlParameters, - ModuleId = moduleid, - Action = action, - EditMode = editmode, - LastSyncDate = lastsyncdate, - Runtime = runtime, - VisitorId = VisitorId, - RemoteIPAddress = SiteState.RemoteIPAddress, - ReturnUrl = returnurl, - IsInternalNavigation = _isInternalNavigation - }; + // load additional metadata for current page + page = ProcessPage(page, site, user, SiteState.Alias); - OnStateChange?.Invoke(_pagestate); - await ScrollToFragment(_pagestate.Uri); + // load additional metadata for modules + (page, site.Modules) = ProcessModules(page, site.Modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType, SiteState.Alias); + + // populate page state (which acts as a client-side cache for subsequent requests) + _pagestate = new PageState + { + Alias = SiteState.Alias, + Site = site, + Page = page, + User = user, + Uri = new Uri(_absoluteUri, UriKind.Absolute), + Route = route, + QueryString = querystring, + UrlParameters = route.UrlParameters, + ModuleId = moduleid, + Action = action, + EditMode = editmode, + LastSyncDate = lastsyncdate, + Runtime = runtime, + VisitorId = VisitorId, + RemoteIPAddress = SiteState.RemoteIPAddress, + ReturnUrl = returnurl, + IsInternalNavigation = _isInternalNavigation + }; + + OnStateChange?.Invoke(_pagestate); + await ScrollToFragment(_pagestate.Uri); + } } } else // page not found @@ -307,7 +316,7 @@ } else // not mapped { - if (user == null) + if (user == null && IsPageModuleVisible(page.EffectiveDate, page.ExpiryDate)) { // redirect to login page if user not logged in as they may need to be authenticated NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "login", "?returnurl=" + WebUtility.UrlEncode(route.PathAndQuery))); @@ -578,4 +587,29 @@ } } } + private bool IsPageModuleVisible(DateTime? effectiveDate, DateTime? expiryDate) + { + DateTime currentUtcTime = DateTime.UtcNow; + + // Check if either effectiveDate or expiryDate is provided + if (effectiveDate.HasValue && expiryDate.HasValue) + { + return currentUtcTime >= effectiveDate.Value && currentUtcTime <= expiryDate.Value; + } + // Check if only effectiveDate is provided + else if (effectiveDate.HasValue) + { + return currentUtcTime >= effectiveDate.Value; + } + // Check if only expiryDate is provided + else if (expiryDate.HasValue) + { + return currentUtcTime <= expiryDate.Value; + } + // If neither effectiveDate nor expiryDate is provided, consider the page/module visible + else + { + return true; + } + } } diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index f8172db1..8acde463 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -75,6 +75,8 @@ namespace Oqtane.Controllers module.Pane = pagemodule.Pane; module.Order = pagemodule.Order; module.ContainerType = pagemodule.ContainerType; + module.EffectiveDate = pagemodule.EffectiveDate; + module.ExpiryDate = pagemodule.ExpiryDate; module.ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName)); @@ -169,7 +171,7 @@ namespace Oqtane.Controllers { if (!pageModules.Exists(item => item.ModuleId == module.ModuleId && item.PageId == page.PageId) && !page.Path.StartsWith("admin/")) { - _pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType }); + _pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType, EffectiveDate = pageModule.EffectiveDate, ExpiryDate = pageModule.ExpiryDate }); } } } diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index 4d1b7184..3092297d 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -13,6 +13,8 @@ using System.Globalization; using Microsoft.Extensions.Caching.Memory; using Oqtane.Extensions; using System; +using Oqtane.UI; +using Microsoft.EntityFrameworkCore.Metadata.Internal; namespace Oqtane.Controllers { @@ -92,59 +94,71 @@ namespace Oqtane.Controllers site.UploadableFiles = site.Settings.ContainsKey("UploadableFiles") && !string.IsNullOrEmpty(site.Settings["UploadableFiles"]) ? site.Settings["UploadableFiles"] : Constants.UploadableFiles; + var modelsUser = _userPermissions.GetUser(User); + var isAdminOrHost = modelsUser.Roles.Contains(RoleNames.Host) || modelsUser.Roles.Contains(RoleNames.Admin); + // pages List settings = _settings.GetSettings(EntityNames.Page).ToList(); site.Pages = new List(); - foreach (Page page in _pages.GetPages(site.SiteId)) + foreach (Page page in _pages.GetPages(site.SiteId).Where(p => !p.IsDeleted && _userPermissions.IsAuthorized(User, PermissionNames.View, p.PermissionList))) { - if (!page.IsDeleted && _userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList)) + if (isAdminOrHost || IsPageModuleVisible(page.EffectiveDate, page.ExpiryDate)) { - page.Settings = settings.Where(item => item.EntityId == page.PageId) + page.Settings = settings + .Where(item => item.EntityId == page.PageId) .Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.PermissionList)) .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); + site.Pages.Add(page); } } + site.Pages = GetPagesHierarchy(site.Pages); // modules List moduledefinitions = _moduleDefinitions.GetModuleDefinitions(site.SiteId).ToList(); settings = _settings.GetSettings(EntityNames.Module).ToList(); site.Modules = new List(); - foreach (PageModule pagemodule in _pageModules.GetPageModules(site.SiteId)) + foreach (PageModule pagemodule in _pageModules.GetPageModules(site.SiteId).Where(pm => !pm.IsDeleted && _userPermissions.IsAuthorized(User, PermissionNames.View, pm.Module.PermissionList))) { - if (!pagemodule.IsDeleted && _userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.PermissionList)) + if (isAdminOrHost || IsPageModuleVisible(pagemodule.EffectiveDate, pagemodule.ExpiryDate)) { - Module module = new Module(); - module.SiteId = pagemodule.Module.SiteId; - module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName; - module.AllPages = pagemodule.Module.AllPages; - module.PermissionList = pagemodule.Module.PermissionList; - module.CreatedBy = pagemodule.Module.CreatedBy; - module.CreatedOn = pagemodule.Module.CreatedOn; - module.ModifiedBy = pagemodule.Module.ModifiedBy; - module.ModifiedOn = pagemodule.Module.ModifiedOn; - module.DeletedBy = pagemodule.DeletedBy; - module.DeletedOn = pagemodule.DeletedOn; - module.IsDeleted = pagemodule.IsDeleted; + Module module = new Module + { + SiteId = pagemodule.Module.SiteId, + ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName, + AllPages = pagemodule.Module.AllPages, + PermissionList = pagemodule.Module.PermissionList, + CreatedBy = pagemodule.Module.CreatedBy, + CreatedOn = pagemodule.Module.CreatedOn, + ModifiedBy = pagemodule.Module.ModifiedBy, + ModifiedOn = pagemodule.Module.ModifiedOn, + DeletedBy = pagemodule.DeletedBy, + DeletedOn = pagemodule.DeletedOn, + IsDeleted = pagemodule.IsDeleted, - module.PageModuleId = pagemodule.PageModuleId; - module.ModuleId = pagemodule.ModuleId; - module.PageId = pagemodule.PageId; - module.Title = pagemodule.Title; - module.Pane = pagemodule.Pane; - module.Order = pagemodule.Order; - module.ContainerType = pagemodule.ContainerType; + PageModuleId = pagemodule.PageModuleId, + ModuleId = pagemodule.ModuleId, + PageId = pagemodule.PageId, + Title = pagemodule.Title, + Pane = pagemodule.Pane, + Order = pagemodule.Order, + ContainerType = pagemodule.ContainerType, + EffectiveDate = pagemodule.EffectiveDate, + ExpiryDate = pagemodule.ExpiryDate, - module.ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName)); + ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == pagemodule.Module.ModuleDefinitionName)), - module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId) + Settings = settings + .Where(item => item.EntityId == pagemodule.ModuleId) .Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.PermissionList)) - .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); + .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue) + }; site.Modules.Add(module); } } + site.Modules = site.Modules.OrderBy(item => item.PageId).ThenBy(item => item.Pane).ThenBy(item => item.Order).ToList(); // languages @@ -277,5 +291,30 @@ namespace Oqtane.Controllers } return hierarchy; } + private bool IsPageModuleVisible(DateTime? effectiveDate, DateTime? expiryDate) + { + DateTime currentUtcTime = DateTime.UtcNow; + + // Check if either effectiveDate or expiryDate is provided + if (effectiveDate.HasValue && expiryDate.HasValue) + { + return currentUtcTime >= effectiveDate.Value && currentUtcTime <= expiryDate.Value; + } + // Check if only effectiveDate is provided + else if (effectiveDate.HasValue) + { + return currentUtcTime >= effectiveDate.Value; + } + // Check if only expiryDate is provided + else if (expiryDate.HasValue) + { + return currentUtcTime <= expiryDate.Value; + } + // If neither effectiveDate nor expiryDate is provided, consider the page/module visible + else + { + return true; + } + } } } diff --git a/Oqtane.Server/Migrations/Tenant/05010001_AddPageEffectiveExpiryDate.cs b/Oqtane.Server/Migrations/Tenant/05010001_AddPageEffectiveExpiryDate.cs new file mode 100644 index 00000000..bed22cd2 --- /dev/null +++ b/Oqtane.Server/Migrations/Tenant/05010001_AddPageEffectiveExpiryDate.cs @@ -0,0 +1,32 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Databases.Interfaces; +using Oqtane.Migrations.EntityBuilders; +using Oqtane.Repository; + +namespace Oqtane.Migrations.Tenant +{ + [DbContext(typeof(TenantDBContext))] + [Migration("Tenant.05.01.00.01")] + public class AddPageEffectiveExpiryDate : MultiDatabaseMigration + { + public AddPageEffectiveExpiryDate(IDatabase database) : base(database) + { + } + + protected override void Up(MigrationBuilder migrationBuilder) + { + var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase);; + pageEntityBuilder.AddDateTimeColumn("EffectiveDate", true); + pageEntityBuilder.AddDateTimeColumn("ExpiryDate", true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase); + pageEntityBuilder.DropColumn("EffectiveDate"); + pageEntityBuilder.DropColumn("ExpiryDate"); + } + } +} diff --git a/Oqtane.Server/Migrations/Tenant/05010002_AddPageModuleEffectiveExpiryDate.cs b/Oqtane.Server/Migrations/Tenant/05010002_AddPageModuleEffectiveExpiryDate.cs new file mode 100644 index 00000000..f0b9147d --- /dev/null +++ b/Oqtane.Server/Migrations/Tenant/05010002_AddPageModuleEffectiveExpiryDate.cs @@ -0,0 +1,32 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Databases.Interfaces; +using Oqtane.Migrations.EntityBuilders; +using Oqtane.Repository; + +namespace Oqtane.Migrations.Tenant +{ + [DbContext(typeof(TenantDBContext))] + [Migration("Tenant.05.01.00.02")] + public class AddPageModuleEffectiveExpiryDate : MultiDatabaseMigration + { + public AddPageModuleEffectiveExpiryDate(IDatabase database) : base(database) + { + } + + protected override void Up(MigrationBuilder migrationBuilder) + { + var pageModuleEntityBuilder = new PageModuleEntityBuilder(migrationBuilder, ActiveDatabase);; + pageModuleEntityBuilder.AddDateTimeColumn("EffectiveDate", true); + pageModuleEntityBuilder.AddDateTimeColumn("ExpiryDate", true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + var pageModuleEntityBuilder = new PageModuleEntityBuilder(migrationBuilder, ActiveDatabase); + pageModuleEntityBuilder.DropColumn("EffectiveDate"); + pageModuleEntityBuilder.DropColumn("ExpiryDate"); + } + } +} diff --git a/Oqtane.Shared/Models/Module.cs b/Oqtane.Shared/Models/Module.cs index 07c2a507..5d03c405 100644 --- a/Oqtane.Shared/Models/Module.cs +++ b/Oqtane.Shared/Models/Module.cs @@ -75,10 +75,16 @@ namespace Oqtane.Models [NotMapped] public string ContainerType { get; set; } + [NotMapped] + public DateTime? EffectiveDate { get; set; } + + [NotMapped] + public DateTime? ExpiryDate { get; set; } + #endregion #region SiteRouter properties - + [NotMapped] public string ModuleType { get; set; } [NotMapped] diff --git a/Oqtane.Shared/Models/Page.cs b/Oqtane.Shared/Models/Page.cs index c4c74a9a..a1b6d370 100644 --- a/Oqtane.Shared/Models/Page.cs +++ b/Oqtane.Shared/Models/Page.cs @@ -82,6 +82,14 @@ namespace Oqtane.Models public bool IsNavigation { get; set; } public bool IsClickable { get; set; } public int? UserId { get; set; } + /// + /// Start of when this assignment is valid. See also + /// + public DateTime? EffectiveDate { get; set; } + /// + /// End of when this assignment is valid. See also + /// + public DateTime? ExpiryDate { get; set; } public bool IsPersonalizable { get; set; } #region IDeletable Properties diff --git a/Oqtane.Shared/Models/PageModule.cs b/Oqtane.Shared/Models/PageModule.cs index e8ff50e5..e3f0adb9 100644 --- a/Oqtane.Shared/Models/PageModule.cs +++ b/Oqtane.Shared/Models/PageModule.cs @@ -41,7 +41,14 @@ namespace Oqtane.Models /// Reference to a Razor Container which wraps this module instance. /// public string ContainerType { get; set; } - + /// + /// Start of when this assignment is valid. See also + /// + public DateTime? EffectiveDate { get; set; } + /// + /// End of when this assignment is valid. See also + /// + public DateTime? ExpiryDate { get; set; } #region IDeletable Properties public string DeletedBy { get; set; }