using Oqtane.Models; using System.Threading.Tasks; using System.Linq; using System.Collections.Generic; using System; using Oqtane.Documentation; using Microsoft.Extensions.Caching.Memory; using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; using Microsoft.AspNetCore.Http; using Oqtane.Enums; using Oqtane.Shared; using System.Globalization; using Oqtane.Extensions; namespace Oqtane.Services { [PrivateApi("Don't show in the documentation, as everything should use the Interface")] public class ServerSiteService : ISiteService { private readonly ISiteRepository _sites; private readonly IPageRepository _pages; private readonly IThemeRepository _themes; private readonly IPageModuleRepository _pageModules; private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly ILanguageRepository _languages; private readonly IUserPermissions _userPermissions; private readonly ISettingRepository _settings; private readonly ITenantManager _tenantManager; private readonly ISyncManager _syncManager; private readonly IConfigManager _configManager; private readonly ILogManager _logger; private readonly IMemoryCache _cache; private readonly IHttpContextAccessor _accessor; private readonly string _private = "[PRIVATE]"; public ServerSiteService(ISiteRepository sites, IPageRepository pages, IThemeRepository themes, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, IConfigManager configManager, ILogManager logger, IMemoryCache cache, IHttpContextAccessor accessor) { _sites = sites; _pages = pages; _themes = themes; _pageModules = pageModules; _moduleDefinitions = moduleDefinitions; _languages = languages; _userPermissions = userPermissions; _settings = settings; _tenantManager = tenantManager; _syncManager = syncManager; _configManager = configManager; _logger = logger; _cache = cache; _accessor = accessor; } public Task> GetSitesAsync() { List sites = new List(); if (_accessor.HttpContext.User.IsInRole(RoleNames.Host)) { sites = _sites.GetSites().ToList(); } return Task.FromResult(sites); } public Task GetSiteAsync(int siteId) { var alias = _tenantManager.GetAlias(); var site = _cache.GetOrCreate($"site:{alias.SiteKey}", entry => { entry.SlidingExpiration = TimeSpan.FromMinutes(30); return GetSite(siteId); }); // clone object so that cache is not mutated site = site.Clone(); // trim site settings based on user permissions site.Settings = site.Settings .Where(item => !item.Value.StartsWith(_private) || _accessor.HttpContext.User.IsInRole(RoleNames.Admin)) .ToDictionary(setting => setting.Key, setting => setting.Value.Replace(_private, "")); // trim pages based on user permissions var pages = new List(); foreach (Page page in site.Pages) { if (!page.IsDeleted && _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.View, page.PermissionList) && (Utilities.IsEffectiveAndNotExpired(page.EffectiveDate, page.ExpiryDate) || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, page.PermissionList))) { page.Settings = page.Settings .Where(item => !item.Value.StartsWith(_private) || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, page.PermissionList)) .ToDictionary(setting => setting.Key, setting => setting.Value.Replace(_private, "")); pages.Add(page); } } site.Pages = pages; // get language display name for user foreach (Language language in site.Languages) { language.Name = CultureInfo.GetCultureInfo(language.Code).DisplayName; } site.Languages = site.Languages.OrderBy(item => item.Name).ToList(); return Task.FromResult(site); } private Site GetSite(int siteid) { var alias = _tenantManager.GetAlias(); var site = _sites.GetSite(siteid); if (site != null && site.SiteId == alias.SiteId) { // site settings site.Settings = _settings.GetSettings(EntityNames.Site, site.SiteId) .ToDictionary(setting => setting.SettingName, setting => (setting.IsPrivate ? _private : "") + setting.SettingValue); // populate file extensions site.ImageFiles = site.Settings.ContainsKey("ImageFiles") && !string.IsNullOrEmpty(site.Settings["ImageFiles"]) ? site.Settings["ImageFiles"] : Constants.ImageFiles; site.UploadableFiles = site.Settings.ContainsKey("UploadableFiles") && !string.IsNullOrEmpty(site.Settings["UploadableFiles"]) ? site.Settings["UploadableFiles"] : Constants.UploadableFiles; // pages List settings = _settings.GetSettings(EntityNames.Page).ToList(); site.Pages = new List(); foreach (Page page in _pages.GetPages(site.SiteId)) { page.Settings = settings.Where(item => item.EntityId == page.PageId) .ToDictionary(setting => setting.SettingName, setting => (setting.IsPrivate ? _private : "") + setting.SettingValue); site.Pages.Add(page); } // framework modules var modules = GetPageModules(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(); var defaultCulture = CultureInfo.GetCultureInfo(Constants.DefaultCulture); if (!site.Languages.Exists(item => item.Code == defaultCulture.Name)) { site.Languages.Add(new Language { Code = defaultCulture.Name, Name = "", Version = Constants.Version, IsDefault = !site.Languages.Any(l => l.IsDefault) }); } // themes site.Themes = _themes.FilterThemes(_themes.GetThemes().ToList()); // installation date used for fingerprinting static assets site.Fingerprint = Utilities.GenerateSimpleHash(_configManager.GetSetting("InstallationDate", DateTime.UtcNow.ToString("yyyyMMddHHmm"))); } else { if (site != null) { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Get Attempt {SiteId}", siteid); site = null; } } return site; } public Task AddSiteAsync(Site site) { if (_accessor.HttpContext.User.IsInRole(RoleNames.Host)) { site = _sites.AddSite(site); _syncManager.AddSyncEvent(_tenantManager.GetAlias(), EntityNames.Site, site.SiteId, SyncEventActions.Create); _logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Create, "Site Added {Site}", site); } else { site = null; } return Task.FromResult(site); } public Task UpdateSiteAsync(Site site) { 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) { site = _sites.UpdateSite(site); _syncManager.AddSyncEvent(alias, 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, EntityNames.Site, site.SiteId, action); _logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Update, "Site Updated {Site}", site); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Put Attempt {Site}", site); site = null; } } else { site = null; } return Task.FromResult(site); } public Task DeleteSiteAsync(int siteId) { if (_accessor.HttpContext.User.IsInRole(RoleNames.Host)) { var alias = _tenantManager.GetAlias(); var site = _sites.GetSite(siteId); if (site != null && site.SiteId == alias.SiteId) { _sites.DeleteSite(siteId); _syncManager.AddSyncEvent(alias, EntityNames.Site, site.SiteId, SyncEventActions.Delete); _syncManager.AddSyncEvent(alias, EntityNames.Site, site.SiteId, SyncEventActions.Refresh); _logger.Log(siteId, LogLevel.Information, this, LogFunction.Delete, "Site Deleted {SiteId}", siteId); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Delete Attempt {SiteId}", siteId); } } return Task.CompletedTask; } public Task> GetModulesAsync(int siteId, int pageId) { var alias = _tenantManager.GetAlias(); var modules = _cache.GetOrCreate($"modules:{alias.SiteKey}", entry => { entry.SlidingExpiration = TimeSpan.FromMinutes(30); return GetPageModules(siteId); }); // clone object so that cache is not mutated modules = modules.ConvertAll(module => module.Clone()); // trim modules for current page based on user permissions var pagemodules = new List(); foreach (Module module in modules.Where(item => (item.PageId == pageId || pageId == -1) && !item.IsDeleted && _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.View, item.PermissionList))) { if (Utilities.IsEffectiveAndNotExpired(module.EffectiveDate, module.ExpiryDate) || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, module.PermissionList)) { module.Settings = module.Settings .Where(item => !item.Value.StartsWith(_private) || _userPermissions.IsAuthorized(_accessor.HttpContext.User, PermissionNames.Edit, module.PermissionList)) .ToDictionary(setting => setting.Key, setting => setting.Value.Replace(_private, "")); pagemodules.Add(module); } } return Task.FromResult(pagemodules); } private List GetPageModules(int siteId) { List moduledefinitions = _moduleDefinitions.GetModuleDefinitions(siteId).ToList(); var settings = _settings.GetSettings(EntityNames.Module).ToList(); var modules = new List(); 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) .ToDictionary(setting => setting.SettingName, setting => (setting.IsPrivate ? _private : "") + 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)] public void SetAlias(Alias alias) { } } }