scalability improvements

This commit is contained in:
sbwalker 2024-05-31 16:23:36 -04:00
parent 9a7a534051
commit 06f0cc70b8
12 changed files with 348 additions and 144 deletions

View File

@ -46,6 +46,14 @@ namespace Oqtane.Services
/// <returns></returns>
Task DeleteSiteAsync(int siteId);
/// <summary>
/// Returns a list of modules
/// </summary>
/// <param name="siteId"></param>
/// <param name="pageId"></param>
/// <returns></returns>
Task<List<Module>> GetModulesAsync(int siteId, int pageId);
[PrivateApi]
[Obsolete("This method is deprecated.", false)]
void SetAlias(Alias alias);

View File

@ -41,6 +41,11 @@ namespace Oqtane.Services
await DeleteAsync($"{Apiurl}/{siteId}");
}
public async Task<List<Module>> GetModulesAsync(int siteId, int pageId)
{
return await GetJsonAsync<List<Module>>($"{Apiurl}/modules/{siteId}/{pageId}");
}
[Obsolete("This method is deprecated.", false)]
public void SetAlias(Alias alias)
{

View File

@ -482,27 +482,19 @@
private void Navigate(string location)
{
Module module;
int moduleId;
switch (location)
{
case "Admin":
// get admin dashboard moduleid
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule);
if (module != null)
{
NavigationManager.NavigateTo(Utilities.EditUrl(PageState.Alias.Path, "admin", module.ModuleId, "Index", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
}
moduleId = int.Parse(PageState.Site.Settings[Constants.AdminDashboardModule]);
NavigationManager.NavigateTo(Utilities.EditUrl(PageState.Alias.Path, "admin", moduleId, "Index", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
break;
case "Add":
case "Edit":
string url = "";
// get page management moduleid
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.PageManagementModule);
if (module != null)
{
url = Utilities.EditUrl(PageState.Alias.Path, "admin/pages", module.ModuleId, location, $"id={PageState.Page.PageId}&returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}");
NavigationManager.NavigateTo(url);
}
moduleId = int.Parse(PageState.Site.Settings[Constants.PageManagementModule]);
NavigationManager.NavigateTo(Utilities.EditUrl(PageState.Alias.Path, "admin/pages", moduleId, location, $"id={PageState.Page.PageId}&returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}"));
break;
}
}

View File

@ -9,6 +9,7 @@ namespace Oqtane.UI
public Alias Alias { get; set; }
public Site Site { get; set; }
public Page Page { get; set; }
public List<Module> Modules { get; set; }
public User User { get; set; }
public Uri Uri { get; set; }
public Route Route { get; set; }
@ -31,10 +32,6 @@ namespace Oqtane.UI
{
get { return Site.Pages; }
}
public List<Module> Modules
{
get { return Site.Modules; }
}
public List<Language> Languages
{
get { return Site.Languages; }

View File

@ -96,6 +96,7 @@
{
Site site = null;
Page page = null;
List<Module> modules = null;
User user = null;
var editmode = false;
var refresh = false;
@ -273,11 +274,21 @@
}
}
// get modules for current page
if (PageState.Modules == null || PageState.Modules.First().PageId != page.PageId)
{
modules = await SiteService.GetModulesAsync(site.SiteId, page.PageId);
}
else
{
modules = PageState.Modules;
}
// 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);
(page, modules) = ProcessModules(page, 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
@ -285,6 +296,7 @@
Alias = SiteState.Alias,
Site = site,
Page = page,
Modules = modules,
User = user,
Uri = new Uri(_absoluteUri, UriKind.Absolute),
Route = route,

View File

@ -132,6 +132,7 @@
_renderMode = site.RenderMode;
_runtime = site.Runtime;
_prerender = site.Prerender;
var modules = new List<Module>();
Route route = new Route(url, alias.Path);
var page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
@ -156,6 +157,10 @@
{
HandlePageNotFound(site, page, route);
}
else
{
modules = await SiteService.GetModulesAsync(site.SiteId, page.PageId);
}
if (site.VisitorTracking)
{
@ -169,7 +174,7 @@
}
// includes resources
var resources = GetPageResources(alias, site, page, int.Parse(route.ModuleId, CultureInfo.InvariantCulture), route.Action);
var resources = await GetPageResources(alias, site, page, modules, int.Parse(route.ModuleId, CultureInfo.InvariantCulture), route.Action);
ManageStyleSheets(resources);
ManageScripts(resources, alias);
@ -215,12 +220,13 @@
_language = _language.Replace("c=", "");
}
// create initial PageState
// create initial PageState
_pageState = new PageState
{
Alias = alias,
Site = site,
Page = page,
Modules = modules,
User = null,
Uri = new Uri(url, UriKind.Absolute),
Route = route,
@ -591,7 +597,7 @@
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)));
}
private List<Resource> GetPageResources(Alias alias, Site site, Page page, int moduleid, string action)
private async Task<List<Resource>> GetPageResources(Alias alias, Site site, Page page, List<Module> modules, int moduleid, string action)
{
var resources = new List<Resource>();
@ -617,7 +623,7 @@
}
}
foreach (Module module in site.Modules.Where(item => item.PageId == page.PageId || item.ModuleId == moduleid))
foreach (Module module in modules.Where(item => item.PageId == page.PageId || item.ModuleId == moduleid))
{
var typename = "";
if (module.ModuleDefinition != null)
@ -683,13 +689,16 @@
}
}
// site level resources for modules in site
var modules = site.Modules.GroupBy(item => item.ModuleDefinition?.ModuleDefinitionName).Select(group => group.First()).ToList();
foreach (var module in modules)
if (site.RenderMode == RenderModes.Interactive)
{
if (module.ModuleDefinition?.Resources != null)
// site level resources for modules in site
var sitemodules = await SiteService.GetModulesAsync(site.SiteId, -1);
foreach (var module in sitemodules.GroupBy(item => item.ModuleDefinition?.ModuleDefinitionName).Select(group => group.First()).ToList())
{
resources = AddResources(resources, module.ModuleDefinition.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Site).ToList(), ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName), site.RenderMode);
if (module.ModuleDefinition?.Resources != null)
{
resources = AddResources(resources, module.ModuleDefinition.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Site).ToList(), ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName), site.RenderMode);
}
}
}

View File

@ -9,7 +9,6 @@ using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Security;
using System.Net;
using System.Security.Policy;
namespace Oqtane.Controllers
{

View File

@ -81,5 +81,12 @@ namespace Oqtane.Controllers
{
await _siteService.DeleteSiteAsync(id);
}
// GET api/<controller>/modules/5/6
[HttpGet("modules/{siteId}/{pageId}")]
public async Task<IEnumerable<Module>> GetModules(int siteId, int pageId)
{
return await _siteService.GetModulesAsync(siteId, pageId);
}
}
}

View File

@ -19,12 +19,8 @@ namespace Oqtane.Infrastructure.EventSubscribers
// when site entities change (ie. site, pages, modules, etc...) a site refresh event is raised and the site cache item needs to be refreshed
if (syncEvent.EntityName == EntityNames.Site && syncEvent.Action == SyncEventActions.Refresh)
{
_cache.Remove($"site:{syncEvent.TenantId}:{syncEvent.EntityId}*", true);
}
// when user is modified (ie. roles) a a site reload event is raised and the site cache item for the user needs to be refreshed
if (syncEvent.EntityName == EntityNames.User && syncEvent.Action == SyncEventActions.Reload)
{
_cache.Remove($"site:{syncEvent.TenantId}:{syncEvent.SiteId}:{syncEvent.EntityId}", true);
_cache.Remove($"site:{syncEvent.TenantId}:{syncEvent.EntityId}");
_cache.Remove($"modules:{syncEvent.TenantId}:{syncEvent.EntityId}");
}
// when a site entity is updated, the hosting model may have changed so the client assemblies cache items need to be refreshed

View File

@ -0,0 +1,162 @@
using Oqtane.Models;
using Oqtane.Infrastructure;
using System.Collections.Generic;
using Oqtane.Repository;
using Microsoft.AspNetCore.Hosting;
using Oqtane.Shared;
using System.IO;
using System;
namespace Oqtane.SiteTemplates
{
public class LoadTestingSiteTemplate : ISiteTemplate
{
private readonly IWebHostEnvironment _environment;
private readonly ISiteRepository _siteRepository;
private readonly IFolderRepository _folderRepository;
private readonly IFileRepository _fileRepository;
int _pages = 5; // root level navigation items
int _children = 10; // submenu items
int _hidden = 150; // hidden pages not part of navigation
int _modules = 10; // modules per page
int _panes = 10; // panes per page (22 max in default theme)
string[] _content = new string[5]; // random content
string[] _panenames = new string[5]; // default theme panes
public LoadTestingSiteTemplate(IWebHostEnvironment environment, ISiteRepository siteRepository, IFolderRepository folderRepository, IFileRepository fileRepository)
{
_environment = environment;
_siteRepository = siteRepository;
_folderRepository = folderRepository;
_fileRepository = fileRepository;
_content[0] = "<p>Lorem ipsum dolor sit amet. Nam atque accusantium vel omnis obcaecati nam magnam fugit aut omnis repudiandae. Non reiciendis inventore sit voluptas vero et modi distinctio qui voluptate corrupti qui neque minima aut culpa rerum? Cum dolorem cupiditate ut voluptatem tempore aut sunt dolor aut facilis veniam. Sit culpa sapiente est consequatur saepe ut dolore quasi id fugit totam non debitis natus ea quis autem?</p><p>Et quasi veritatis ad aliquam beatae in voluptatem galisum in sunt ducimus. Sed corporis autem ut voluptatum cumque aut nisi expedita et nulla aliquam qui placeat aperiam? Est blanditiis nostrum et laborum mollitia non consequatur molestiae. Sed iste ducimus est eaque animi 33 autem quaerat.</p>";
_content[1] = "<p>Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ornare aenean euismod elementum nisi quis eleifend quam. Mauris pharetra et ultrices neque ornare aenean euismod. Amet consectetur adipiscing elit duis tristique. Elit scelerisque mauris pellentesque pulvinar pellentesque. Tellus in metus vulputate eu. Risus nec feugiat in fermentum posuere urna nec tincidunt. Porttitor leo a diam sollicitudin tempor id eu. Elit sed vulputate mi sit. Aliquet bibendum enim facilisis gravida. Dictum varius duis at consectetur lorem donec massa sapien. Ipsum nunc aliquet bibendum enim facilisis. In eu mi bibendum neque egestas congue quisque. Eget duis at tellus at. Nunc sed velit dignissim sodales ut. Elementum curabitur vitae nunc sed velit. Lacinia quis vel eros donec ac odio tempor orci. Id volutpat lacus laoreet non curabitur gravida arcu ac tortor. Facilisis mauris sit amet massa vitae tortor.</p>";
_content[2] = "<p>Rhoncus dolor purus non enim praesent elementum facilisis leo. Aenean pharetra magna ac placerat vestibulum lectus mauris. Facilisis leo vel fringilla est ullamcorper eget nulla facilisi. Elementum nibh tellus molestie nunc. Id leo in vitae turpis massa sed elementum. Feugiat in ante metus dictum at tempor. Elit sed vulputate mi sit amet mauris commodo quis imperdiet. Non quam lacus suspendisse faucibus interdum posuere lorem ipsum. Mi quis hendrerit dolor magna eget est lorem. Integer malesuada nunc vel risus commodo viverra maecenas accumsan. Odio ut enim blandit volutpat. Nec sagittis aliquam malesuada bibendum arcu vitae elementum. Volutpat maecenas volutpat blandit aliquam etiam erat velit scelerisque in. Egestas integer eget aliquet nibh. Pellentesque habitant morbi tristique senectus et. Fermentum leo vel orci porta non pulvinar neque. Ut sem nulla pharetra diam sit amet. Proin nibh nisl condimentum id venenatis.</p>";
_content[3] = "<p>Pellentesque habitant morbi tristique senectus. In metus vulputate eu scelerisque felis imperdiet proin fermentum. Dolor purus non enim praesent. Sed adipiscing diam donec adipiscing tristique risus nec feugiat. Elementum nisi quis eleifend quam adipiscing. Dapibus ultrices in iaculis nunc sed augue lacus viverra. Enim blandit volutpat maecenas volutpat. Ut placerat orci nulla pellentesque dignissim enim sit amet venenatis. Eget gravida cum sociis natoque penatibus et magnis dis parturient. In hac habitasse platea dictumst quisque sagittis purus. Scelerisque varius morbi enim nunc faucibus a pellentesque sit. Pretium viverra suspendisse potenti nullam ac tortor vitae purus. Sagittis orci a scelerisque purus semper eget duis at tellus. Sed vulputate mi sit amet mauris commodo. Tincidunt praesent semper feugiat nibh sed. Condimentum vitae sapien pellentesque habitant morbi tristique senectus et. Elementum eu facilisis sed odio morbi quis. Mauris in aliquam sem fringilla ut morbi tincidunt augue. Diam quam nulla porttitor massa id neque.</p>";
_content[4] = "<p>Est nemo nemo hic quisquam dolores et dignissimos voluptas? Est adipisci quos et omnis dolorum qui galisum vero ut galisum accusantium vel accusamus odit ab fuga voluptatem. At officiis illo quo quia modi eos dicta dolor sed voluptates quis id eligendi distinctio sed ratione consequatur. Et voluptatibus consequatur quo voluptatem architecto est sapiente voluptas.</p>";
_panenames = (PaneNames.Default + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width,Footer").Split(',');
}
public string Name
{
get { return "Load Testing Site Template"; }
}
public List<PageTemplate> CreateSite(Site site)
{
List<PageTemplate> _pageTemplates = new List<PageTemplate>();
// pages
for (int page = 1; page <= _pages; page++)
{
_pageTemplates.Add(new PageTemplate
{
Name = $"Page{page}",
Parent = "",
Path = (page == 1) ? "/" : $"page{page}",
Order = (page * 2) - 1 + 10,
Icon = "oi oi-home",
IsNavigation = true,
IsClickable = true,
IsPersonalizable = false,
PermissionList = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
},
PageTemplateModules = GetPageTemplateModules()
});
for (int child = 1; child <= _children; child++)
{
_pageTemplates.Add(new PageTemplate
{
Name = $"Child{child}",
Parent = (page == 1) ? "/" : $"page{page}",
Path = (page == 1) ? "" : $"page{page}/child{child}",
Order = (child * 2) - 1,
Icon = "oi oi-caret-right",
IsNavigation = true,
IsClickable = true,
IsPersonalizable = false,
PermissionList = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
},
PageTemplateModules = GetPageTemplateModules()
});
}
}
// hidden pages
for (int hidden = 1; hidden <= _hidden; hidden++)
{
_pageTemplates.Add(new PageTemplate
{
Name = $"Hidden{hidden}",
Parent = "",
Path = $"hidden{hidden}",
Order = (hidden * 2) - 1 + 10,
Icon = "",
IsNavigation = false,
IsClickable = false,
IsPersonalizable = false,
PermissionList = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
},
PageTemplateModules = GetPageTemplateModules()
});
}
if (System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "images", "logo-white.png")))
{
string folderpath = Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(), "Sites", site.SiteId.ToString(), Path.DirectorySeparatorChar.ToString());
Directory.CreateDirectory(folderpath);
if (!System.IO.File.Exists(Path.Combine(folderpath, "logo-white.png")))
{
System.IO.File.Copy(Path.Combine(_environment.WebRootPath, "images", "logo-white.png"), Path.Combine(folderpath, "logo-white.png"));
}
Folder folder = _folderRepository.GetFolder(site.SiteId, "");
Oqtane.Models.File file = _fileRepository.AddFile(new Oqtane.Models.File { FolderId = folder.FolderId, Name = "logo-white.png", Extension = "png", Size = 8192, ImageHeight = 80, ImageWidth = 250 });
site.LogoFileId = file.FileId;
_siteRepository.UpdateSite(site);
}
return _pageTemplates;
}
private List<PageTemplateModule> GetPageTemplateModules()
{
Random rnd = new Random();
var _pageTemplateModules = new List<PageTemplateModule>();
for (int module = 1; module <= _modules; module++)
{
_pageTemplateModules.Add(new PageTemplateModule
{
ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client",
Title = $"Module{module}",
Pane = _panenames[rnd.Next(0, _panes - 1)],
Order = (module* 2) - 1,
PermissionList = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
},
Content = _content[rnd.Next(0, 4)]
});
}
return _pageTemplateModules;
}
}
}

View File

@ -62,35 +62,22 @@ namespace Oqtane.Services
public async Task<Site> GetSiteAsync(int siteId)
{
if (!_accessor.HttpContext.User.Identity.IsAuthenticated)
var site = await _cache.GetOrCreateAsync($"site:{_accessor.HttpContext.GetAlias().SiteKey}", async entry =>
{
// unauthenticated
return await _cache.GetOrCreateAsync($"site:{_accessor.HttpContext.GetAlias().SiteKey}", async entry =>
{
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
return await GetSite(siteId);
}, true);
}
else // authenticated
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
return await GetSite(siteId);
});
var pages = new List<Page>();
foreach (Page page in site.Pages)
{
// is only in registered users role - cache by role
if (_accessor.HttpContext.User.IsOnlyInRole(RoleNames.Registered))
if (!page.IsDeleted && _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.View, page.PermissionList) && (Utilities.IsPageModuleVisible(page.EffectiveDate, page.ExpiryDate) || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, page.PermissionList)))
{
return await _cache.GetOrCreateAsync($"site:{_accessor.HttpContext.GetAlias().SiteKey}:{RoleNames.Registered}", async entry =>
{
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
return await GetSite(siteId);
}, true);
}
else // cache by user
{
return await _cache.GetOrCreateAsync($"site:{_accessor.HttpContext.GetAlias().SiteKey}:{_accessor.HttpContext.User.UserId()}", async entry =>
{
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
return await GetSite(siteId);
}, true);
pages.Add(page);
}
}
return site;
}
private async Task<Site> GetSite(int siteid)
@ -115,62 +102,17 @@ namespace Oqtane.Services
site.Pages = new List<Page>();
foreach (Page page in _pages.GetPages(site.SiteId))
{
if (!page.IsDeleted && _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.View, page.PermissionList) && (Utilities.IsPageModuleVisible(page.EffectiveDate, page.ExpiryDate) || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, page.PermissionList)))
{
page.Settings = settings.Where(item => item.EntityId == page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, page.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
site.Pages.Add(page);
}
page.Settings = settings.Where(item => item.EntityId == page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, page.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
site.Pages.Add(page);
}
site.Pages = GetPagesHierarchy(site.Pages);
// modules
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(site.SiteId).ToList();
settings = _settings.GetSettings(EntityNames.Module).ToList();
site.Modules = new List<Module>();
foreach (PageModule pagemodule in _pageModules.GetPageModules(site.SiteId).Where(pm => !pm.IsDeleted && _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.View, pm.Module.PermissionList)))
{
if (Utilities.IsPageModuleVisible(pagemodule.EffectiveDate, pagemodule.ExpiryDate) || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, pagemodule.Module.PermissionList))
{
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,
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,
ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == pagemodule.Module.ModuleDefinitionName)),
Settings = settings
.Where(item => item.EntityId == pagemodule.ModuleId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, pagemodule.Module.PermissionList))
.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();
// framework modules
var modules = await GetModulesAsync(site.SiteId);
site.Settings.Add(Constants.AdminDashboardModule, modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule).ModuleId.ToString());
site.Settings.Add(Constants.PageManagementModule, modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.PageManagementModule).ModuleId.ToString());
// languages
site.Languages = _languages.GetLanguages(site.SiteId).ToList();
@ -191,6 +133,46 @@ namespace Oqtane.Services
return site;
}
private static List<Page> GetPagesHierarchy(List<Page> pages)
{
List<Page> hierarchy = new List<Page>();
Action<List<Page>, Page> getPath = null;
getPath = (pageList, page) =>
{
IEnumerable<Page> children;
int level;
if (page == null)
{
level = -1;
children = pages.Where(item => item.ParentId == null);
}
else
{
level = page.Level;
children = pages.Where(item => item.ParentId == page.PageId);
}
foreach (Page child in children)
{
child.Level = level + 1;
child.HasChildren = pages.Any(item => item.ParentId == child.PageId && !item.IsDeleted && item.IsNavigation);
hierarchy.Add(child);
getPath(pageList, child);
}
};
pages = pages.OrderBy(item => item.Order).ToList();
getPath(pages, null);
// add any non-hierarchical items to the end of the list
foreach (Page page in pages)
{
if (hierarchy.Find(item => item.PageId == page.PageId) == null)
{
hierarchy.Add(page);
}
}
return hierarchy;
}
public async Task<Site> AddSiteAsync(Site site)
{
if (_accessor.HttpContext.User.IsInRole(RoleNames.Host))
@ -256,44 +238,79 @@ namespace Oqtane.Services
}
}
private static List<Page> GetPagesHierarchy(List<Page> pages)
public async Task<List<Module>> GetModulesAsync(int siteId, int pageId)
{
List<Page> hierarchy = new List<Page>();
Action<List<Page>, Page> getPath = null;
getPath = (pageList, page) =>
var sitemodules = await _cache.GetOrCreateAsync($"modules:{_accessor.HttpContext.GetAlias().SiteKey}", async entry =>
{
IEnumerable<Page> children;
int level;
if (page == null)
{
level = -1;
children = pages.Where(item => item.ParentId == null);
}
else
{
level = page.Level;
children = pages.Where(item => item.ParentId == page.PageId);
}
foreach (Page child in children)
{
child.Level = level + 1;
child.HasChildren = pages.Any(item => item.ParentId == child.PageId && !item.IsDeleted && item.IsNavigation);
hierarchy.Add(child);
getPath(pageList, child);
}
};
pages = pages.OrderBy(item => item.Order).ToList();
getPath(pages, null);
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
return await GetModulesAsync(siteId);
});
// add any non-hierarchical items to the end of the list
foreach (Page page in pages)
var modules = new List<Module>();
foreach (Module module in sitemodules.Where(item => (item.PageId == pageId || pageId == -1) && !item.IsDeleted && _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.View, item.PermissionList)))
{
if (hierarchy.Find(item => item.PageId == page.PageId) == null)
if (Utilities.IsPageModuleVisible(module.EffectiveDate, module.ExpiryDate) || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, module.PermissionList))
{
hierarchy.Add(page);
modules.Add(module);
}
}
return hierarchy;
return modules;
}
public async Task<List<Module>> GetModulesAsync(int siteId)
{
return await _cache.GetOrCreateAsync($"modules:{_accessor.HttpContext.GetAlias().SiteKey}", async entry =>
{
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
return await GetModules(siteId);
});
}
private async Task<List<Module>> GetModules(int siteId)
{
await Task.Yield(); // force method to async
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(siteId).ToList();
var settings = _settings.GetSettings(EntityNames.Module).ToList();
var modules = new List<Module>();
foreach (PageModule pagemodule in _pageModules.GetPageModules(siteId))
{
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,
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,
ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == pagemodule.Module.ModuleDefinitionName)),
Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, pagemodule.Module.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue)
};
modules.Add(module);
}
return modules.OrderBy(item => item.PageId).ThenBy(item => item.Pane).ThenBy(item => item.Order).ToList();
}
[Obsolete("This method is deprecated.", false)]

View File

@ -126,8 +126,8 @@ namespace Oqtane.Models
[NotMapped]
public List<Page> Pages { get; set; }
[NotMapped]
public List<Module> Modules { get; set; }
//[NotMapped]
//public List<Module> Modules { get; set; }
[NotMapped]
public List<Language> Languages { get; set; }