diff --git a/Oqtane.Client/UI/PageState.cs b/Oqtane.Client/UI/PageState.cs index a4316e85..613cacde 100644 --- a/Oqtane.Client/UI/PageState.cs +++ b/Oqtane.Client/UI/PageState.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Xml.Linq; using Oqtane.Models; namespace Oqtane.UI @@ -8,11 +9,8 @@ namespace Oqtane.UI { public Alias Alias { get; set; } public Site Site { get; set; } - public List Languages { get; set; } - public List Pages { get; set; } public Page Page { get; set; } public User User { get; set; } - public List Modules { get; set; } public Uri Uri { get; set; } public Dictionary QueryString { get; set; } public string UrlParameters { get; set; } @@ -24,5 +22,18 @@ namespace Oqtane.UI public int VisitorId { get; set; } public string RemoteIPAddress { get; set; } public string ReturnUrl { get; set; } + + public List Pages + { + get { return Site.Pages; } + } + public List Modules + { + get { return Site.Modules; } + } + public List Languages + { + get { return Site.Languages; } + } } } diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index c3e5dc28..7b572213 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -7,10 +7,8 @@ @inject INavigationInterception NavigationInterception @inject ISyncService SyncService @inject ISiteService SiteService -@inject ILanguageService LanguageService @inject IPageService PageService @inject IUserService UserService -@inject IModuleService ModuleService @inject IUrlMappingService UrlMappingService @inject ILogService LogService @inject IJSRuntime JSRuntime @@ -181,14 +179,13 @@ if (PageState == null || refresh == UI.Refresh.Site) { - languages = await LanguageService.GetLanguagesAsync(site.SiteId); - pages = await PageService.GetPagesAsync(site.SiteId); - pages = pages.Where(item => !item.IsDeleted).ToList(); + pages = site.Pages.Where(item => !item.IsDeleted).ToList(); + languages = site.Languages; } else { - languages = PageState.Languages; pages = PageState.Pages; + languages = PageState.Languages; } if (PageState == null || refresh == UI.Refresh.Site) @@ -226,8 +223,7 @@ if (PageState == null || refresh == UI.Refresh.Site) { - modules = await ModuleService.GetModulesAsync(site.SiteId); - modules = modules.Where(item => !item.IsDeleted).ToList(); + modules = site.Modules.Where(item => !item.IsDeleted).ToList(); } else { @@ -240,11 +236,8 @@ { Alias = SiteState.Alias, Site = site, - Languages = languages, - Pages = pages, Page = page, User = user, - Modules = modules, Uri = new Uri(_absoluteUri, UriKind.Absolute), QueryString = querystring, UrlParameters = route.UrlParameters, diff --git a/Oqtane.Server/Controllers/LanguageController.cs b/Oqtane.Server/Controllers/LanguageController.cs index 7e33c2ee..bce3db06 100644 --- a/Oqtane.Server/Controllers/LanguageController.cs +++ b/Oqtane.Server/Controllers/LanguageController.cs @@ -24,9 +24,9 @@ namespace Oqtane.Controllers private readonly ILogManager _logger; private readonly Alias _alias; - public LanguageController(ILanguageRepository language, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager) + public LanguageController(ILanguageRepository languages, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager) { - _languages = language; + _languages = languages; _syncManager = syncManager; _logger = logger; _alias = tenantManager.GetAlias(); diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index 9a50b91f..2f50b195 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -8,6 +8,11 @@ using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Repository; using System.Net; +using Oqtane.Security; +using System.Globalization; +using Microsoft.Extensions.Caching.Memory; +using Oqtane.Extensions; +using System; namespace Oqtane.Controllers { @@ -15,17 +20,31 @@ namespace Oqtane.Controllers public class SiteController : Controller { private readonly ISiteRepository _sites; + private readonly IPageRepository _pages; + private readonly IModuleRepository _modules; + private readonly IPageModuleRepository _pageModules; + private readonly IModuleDefinitionRepository _moduleDefinitions; + private readonly ILanguageRepository _languages; + private readonly IUserPermissions _userPermissions; private readonly ISettingRepository _settings; private readonly ISyncManager _syncManager; private readonly ILogManager _logger; + private readonly IMemoryCache _cache; private readonly Alias _alias; - public SiteController(ISiteRepository sites, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger) + public SiteController(ISiteRepository sites, IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IMemoryCache cache) { _sites = sites; + _pages = pages; + _modules = modules; + _pageModules = pageModules; + _moduleDefinitions = moduleDefinitions; + _languages = languages; + _userPermissions = userPermissions; _settings = settings; _syncManager = syncManager; _logger = logger; + _cache = cache; _alias = tenantManager.GetAlias(); } @@ -41,17 +60,92 @@ namespace Oqtane.Controllers [HttpGet("{id}")] public Site Get(int id) { - var site = _sites.GetSite(id); + if (!User.Identity.IsAuthenticated) + { + return _cache.GetOrCreate($"site:{HttpContext.GetAlias().SiteKey}", entry => + { + entry.SlidingExpiration = TimeSpan.FromMinutes(30); + return GetSite(id); + }); + } + else + { + return GetSite(id); + } + } + + private Site GetSite(int siteid) + { + var site = _sites.GetSite(siteid); if (site.SiteId == _alias.SiteId) { + // site settings site.Settings = _settings.GetSettings(EntityNames.Site, site.SiteId) .Where(item => !item.IsPrivate || User.IsInRole(RoleNames.Admin)) .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); + + // pages + List settings = _settings.GetSettings(EntityNames.Page).ToList(); + site.Pages = new List(); + foreach (Page page in _pages.GetPages(site.SiteId)) + { + if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions)) + { + page.Settings = settings.Where(item => item.EntityId == page.PageId) + .Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions)) + .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); + site.Pages.Add(page); + } + } + + // 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)) + { + if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions)) + { + Module module = new Module(); + module.SiteId = pagemodule.Module.SiteId; + module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName; + module.AllPages = pagemodule.Module.AllPages; + module.Permissions = pagemodule.Module.Permissions; + 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.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; + + module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName); + module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId) + .Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions)) + .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); + + site.Modules.Add(module); + } + } + + // languages + site.Languages = _languages.GetLanguages(site.SiteId).ToList(); + var defaultCulture = CultureInfo.GetCultureInfo(Constants.DefaultCulture); + site.Languages.Add(new Language { Code = defaultCulture.Name, Name = defaultCulture.DisplayName, Version = Constants.Version, IsDefault = !site.Languages.Any(l => l.IsDefault) }); + return site; } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Get Attempt {SiteId}", id); + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Get Attempt {SiteId}", siteid); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return null; } diff --git a/Oqtane.Server/Infrastructure/SyncManager.cs b/Oqtane.Server/Infrastructure/SyncManager.cs index b4f4c551..b5ac1dc5 100644 --- a/Oqtane.Server/Infrastructure/SyncManager.cs +++ b/Oqtane.Server/Infrastructure/SyncManager.cs @@ -1,17 +1,21 @@ +using Microsoft.Extensions.Caching.Memory; using Oqtane.Models; +using Oqtane.Shared; using System; using System.Collections.Generic; using System.Linq; -using Oqtane.Repository; +using System.Reflection; namespace Oqtane.Infrastructure { public class SyncManager : ISyncManager { + private readonly IMemoryCache _cache; private List SyncEvents { get; set; } - public SyncManager() + public SyncManager(IMemoryCache cache) { + _cache = cache; SyncEvents = new List(); } @@ -28,6 +32,10 @@ namespace Oqtane.Infrastructure public void AddSyncEvent(int tenantId, string entityName, int entityId, bool reload) { SyncEvents.Add(new SyncEvent { TenantId = tenantId, EntityName = entityName, EntityId = entityId, Reload = reload, ModifiedOn = DateTime.UtcNow }); + if (entityName == EntityNames.Site) +{ + _cache.Remove($"site:{tenantId}:{entityId}"); + } // trim sync events SyncEvents.RemoveAll(item => item.ModifiedOn < DateTime.UtcNow.AddHours(-1)); } diff --git a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs index fdd32df4..21527009 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs @@ -1,6 +1,9 @@ using System.Linq; using Oqtane.Documentation; using System.Collections.Generic; +using Microsoft.Extensions.Caching.Memory; +using Oqtane.Infrastructure; +using System; namespace Oqtane.Modules.HtmlText.Repository { @@ -8,15 +11,23 @@ namespace Oqtane.Modules.HtmlText.Repository public class HtmlTextRepository : IHtmlTextRepository, ITransientService { private readonly HtmlTextContext _db; + private readonly IMemoryCache _cache; + private readonly SiteState _siteState; - public HtmlTextRepository(HtmlTextContext context) + public HtmlTextRepository(HtmlTextContext context, IMemoryCache cache, SiteState siteState) { _db = context; + _cache = cache; + _siteState = siteState; } public IEnumerable GetHtmlTexts(int moduleId) { - return _db.HtmlText.Where(item => item.ModuleId == moduleId); + return _cache.GetOrCreate($"HtmlText:{_siteState.Alias.SiteKey}:{moduleId}", entry => + { + entry.SlidingExpiration = TimeSpan.FromMinutes(30); + return _db.HtmlText.Where(item => item.ModuleId == moduleId).ToList(); + }); } public Models.HtmlText GetHtmlText(int htmlTextId) @@ -28,6 +39,7 @@ namespace Oqtane.Modules.HtmlText.Repository { _db.HtmlText.Add(htmlText); _db.SaveChanges(); + ClearCache(htmlText.ModuleId); return htmlText; } @@ -35,7 +47,13 @@ namespace Oqtane.Modules.HtmlText.Repository { Models.HtmlText htmlText = _db.HtmlText.FirstOrDefault(item => item.HtmlTextId == htmlTextId); if (htmlText != null) _db.HtmlText.Remove(htmlText); + ClearCache(htmlText.ModuleId); _db.SaveChanges(); } + + private void ClearCache(int moduleId) + { + _cache.Remove($"HtmlText:{_siteState.Alias.SiteKey}:{moduleId}"); + } } } diff --git a/Oqtane.Server/Repository/PermissionRepository.cs b/Oqtane.Server/Repository/PermissionRepository.cs index 1941f85f..3028488e 100644 --- a/Oqtane.Server/Repository/PermissionRepository.cs +++ b/Oqtane.Server/Repository/PermissionRepository.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.Json; -using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Oqtane.Extensions; using Oqtane.Models; diff --git a/Oqtane.Shared/Models/Module.cs b/Oqtane.Shared/Models/Module.cs index 472ddcbc..0e44db71 100644 --- a/Oqtane.Shared/Models/Module.cs +++ b/Oqtane.Shared/Models/Module.cs @@ -109,7 +109,6 @@ namespace Oqtane.Models #endregion #region IModuleControl properties - // TODO: unclear why these are IModuleControl properties - there is no such interface [NotMapped] public SecurityAccessLevel SecurityAccessLevel { get; set; } [NotMapped] diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs index d66f2a18..9b04cac0 100644 --- a/Oqtane.Shared/Models/Site.cs +++ b/Oqtane.Shared/Models/Site.cs @@ -83,6 +83,18 @@ namespace Oqtane.Models /// public string Version { get; set; } + [NotMapped] + public Dictionary Settings { get; set; } + + [NotMapped] + public List Pages { get; set; } + + [NotMapped] + public List Modules { get; set; } + + [NotMapped] + public List Languages { get; set; } + #region IAuditable Properties /// @@ -101,17 +113,16 @@ namespace Oqtane.Models public string DeletedBy { get; set; } public DateTime? DeletedOn { get; set; } public bool IsDeleted { get; set; } - + #endregion - + [NotMapped] public string SiteTemplateType { get; set; } - [NotMapped] - public Dictionary Settings { get; set; } - + #region Obsolete properties [NotMapped] [Obsolete("This property is deprecated.", false)] public string DefaultLayoutType { get; set; } + #endregion } }