From 8deb119f36e6d345119050d4abb8ec6c66c9e9a3 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 24 May 2019 13:33:19 -0400 Subject: [PATCH] Performance improvements, refactoring of multi-tenant support, split Alias and Tenant entities for cleaner separation of concerns, create an additional site during installation for demonstratng multitenancy --- Oqtane.Client/Modules/Admin/Admin/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Login/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Pages/Add.razor | 8 +- .../Modules/Admin/Pages/Delete.razor | 10 +- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 7 +- Oqtane.Client/Modules/HtmlText/Edit.razor | 7 +- Oqtane.Client/Modules/HtmlText/Index.razor | 5 +- .../HtmlText/Services/HtmlTextService.cs | 12 ++- Oqtane.Client/Modules/ModuleBase.cs | 26 ++--- Oqtane.Client/Services/AliasService.cs | 53 ++++++++++ Oqtane.Client/Services/IAliasService.cs | 19 ++++ .../Services/ModuleDefinitionService.cs | 54 ++++++----- Oqtane.Client/Services/ModuleService.cs | 12 ++- Oqtane.Client/Services/PageModuleService.cs | 12 ++- Oqtane.Client/Services/PageService.cs | 12 ++- Oqtane.Client/Services/ServiceBase.cs | 18 ++-- Oqtane.Client/Services/SiteService.cs | 12 ++- Oqtane.Client/Services/TenantService.cs | 12 ++- Oqtane.Client/Services/ThemeService.cs | 55 +++++------ Oqtane.Client/Services/UserService.cs | 11 ++- Oqtane.Client/Shared/PageState.cs | 6 +- Oqtane.Client/Shared/SiteRouter.razor | 86 +++++++++++++---- Oqtane.Client/Shared/SiteState.cs | 11 +++ Oqtane.Client/Shared/Utilities.cs | 46 +++++++-- Oqtane.Client/Startup.cs | 11 +-- Oqtane.Client/Themes/AdminContainer.razor | 2 +- Oqtane.Client/Themes/ContainerBase.cs | 30 ++++++ .../Themes/Controls/ControlPanel.razor | 37 ++++--- Oqtane.Client/Themes/Controls/Login.razor | 4 +- Oqtane.Client/Themes/Controls/Logo.razor | 2 +- Oqtane.Client/Themes/Controls/Menu.razor | 4 +- Oqtane.Client/Themes/Controls/Mode.razor | 44 --------- .../Themes/Controls/ModuleActions.razor | 2 +- Oqtane.Client/Themes/ThemeBase.cs | 21 ++++ Oqtane.Client/Themes/ThemeObjectBase.cs | 20 ++++ Oqtane.Client/wwwroot/Sites/2/oqtane.png | Bin 0 -> 7963 bytes Oqtane.Client/wwwroot/index.html | 10 +- Oqtane.Client/wwwroot/js/site.js | 17 ---- Oqtane.Server/Controllers/AliasController.cs | 55 +++++++++++ Oqtane.Server/Controllers/TenantController.cs | 35 ++++++- .../HtmlText/Repository/HtmlTextContext.cs | 3 +- Oqtane.Server/Repository/AliasRepository.cs | 91 ++++++++++++++++++ Oqtane.Server/Repository/ContextBase.cs | 4 +- Oqtane.Server/Repository/HostContext.cs | 1 + Oqtane.Server/Repository/IAliasRepository.cs | 14 +++ Oqtane.Server/Repository/ITenantRepository.cs | 7 +- Oqtane.Server/Repository/ITenantResolver.cs | 9 ++ Oqtane.Server/Repository/TenantContext.cs | 4 +- Oqtane.Server/Repository/TenantRepository.cs | 76 +++++++++++---- Oqtane.Server/Repository/TenantResolver.cs | 48 +++++++++ Oqtane.Server/Scripts/Initialize.sql | 79 +++++++++++---- Oqtane.Server/Startup.cs | 9 +- Oqtane.Server/wwwroot/Sites/2/oqtane.png | Bin 0 -> 7963 bytes Oqtane.Server/wwwroot/favicon.ico | Bin 1150 -> 5430 bytes Oqtane.Shared/Models/Alias.cs | 41 ++++++++ Oqtane.Shared/Models/Page.cs | 4 +- Oqtane.Shared/Models/Tenant.cs | 3 +- 57 files changed, 878 insertions(+), 307 deletions(-) create mode 100644 Oqtane.Client/Services/AliasService.cs create mode 100644 Oqtane.Client/Services/IAliasService.cs create mode 100644 Oqtane.Client/Shared/SiteState.cs delete mode 100644 Oqtane.Client/Themes/Controls/Mode.razor create mode 100644 Oqtane.Client/wwwroot/Sites/2/oqtane.png create mode 100644 Oqtane.Server/Controllers/AliasController.cs create mode 100644 Oqtane.Server/Repository/AliasRepository.cs create mode 100644 Oqtane.Server/Repository/IAliasRepository.cs create mode 100644 Oqtane.Server/Repository/ITenantResolver.cs create mode 100644 Oqtane.Server/Repository/TenantResolver.cs create mode 100644 Oqtane.Server/wwwroot/Sites/2/oqtane.png create mode 100644 Oqtane.Shared/Models/Alias.cs diff --git a/Oqtane.Client/Modules/Admin/Admin/Index.razor b/Oqtane.Client/Modules/Admin/Admin/Index.razor index 62d3242f0..ddc6a87e6 100644 --- a/Oqtane.Client/Modules/Admin/Admin/Index.razor +++ b/Oqtane.Client/Modules/Admin/Admin/Index.razor @@ -12,7 +12,7 @@ { if (p.IsNavigation && UserService.IsAuthorized(PageState.User, p.ViewPermissions)) { - string url = PageState.Alias + p.Path; + string url = NavigateUrl(p.Path);
  • @p.Name diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index dcb9af1df..0afe0834c 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -39,7 +39,7 @@ { var interop = new Interop(jsRuntime); await interop.SetCookie("user", user.UserId.ToString(), 7); - UriHelper.NavigateTo(PageState.Alias, true); + UriHelper.NavigateTo(NavigateUrl(""), true); } else { diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 4a39046b0..bb6a93e32 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -3,7 +3,6 @@ @using Oqtane.Services @using Oqtane.Modules @using Oqtane.Shared -@using Oqtane.Client.Modules.Controls @inherits ModuleBase @inject IUriHelper UriHelper @inject IPageService PageService @@ -132,11 +131,10 @@ string viewpermissions = "All Users"; string editpermissions = "Administrators"; - protected override async Task OnInitAsync() + protected override void OnInit() { - var Themes = await ThemeService.GetThemesAsync(); - themes = ThemeService.GetThemeTypes(Themes); - panelayouts = ThemeService.GetPaneLayoutTypes(Themes); + themes = ThemeService.GetThemeTypes(PageState.Themes); + panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes); } private async Task SavePage() diff --git a/Oqtane.Client/Modules/Admin/Pages/Delete.razor b/Oqtane.Client/Modules/Admin/Pages/Delete.razor index e8464ff66..3c8feeb04 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Delete.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Delete.razor @@ -3,7 +3,6 @@ @using Oqtane.Services @using Oqtane.Modules @using Oqtane.Shared -@using Oqtane.Client.Modules.Controls @inherits ModuleBase @inject IUriHelper UriHelper @inject IPageService PageService @@ -133,11 +132,10 @@ string viewpermissions; string editpermissions; - protected override async Task OnInitAsync() + protected override void OnInit() { - List Themes = await ThemeService.GetThemesAsync(); - themes = ThemeService.GetThemeTypes(Themes); - panelayouts = ThemeService.GetPaneLayoutTypes(Themes); + themes = ThemeService.GetThemeTypes(PageState.Themes); + panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes); PageId = Int32.Parse(PageState.QueryString["id"]); Page p = PageState.Pages.Where(item => item.PageId == PageId).FirstOrDefault(); @@ -159,6 +157,6 @@ private async Task DeletePage() { await PageService.DeletePageAsync(Int32.Parse(PageState.QueryString["id"])); - UriHelper.NavigateTo(PageState.Alias); + UriHelper.NavigateTo(NavigateUrl("", true)); } } diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index fb381e39f..698dc3de9 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -133,11 +133,10 @@ string viewpermissions; string editpermissions; - protected override async Task OnInitAsync() + protected override void OnInit() { - List Themes = await ThemeService.GetThemesAsync(); - themes = ThemeService.GetThemeTypes(Themes); - panelayouts = ThemeService.GetPaneLayoutTypes(Themes); + themes = ThemeService.GetThemeTypes(PageState.Themes); + panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes); PageId = Int32.Parse(PageState.QueryString["id"]); Page p = PageState.Pages.Where(item => item.PageId == PageId).FirstOrDefault(); diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index e6c74b7d6..de6c70e17 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -3,10 +3,11 @@ @using Oqtane.Client.Modules.HtmlText.Services @using Oqtane.Shared.Modules.HtmlText.Models @using System.Net.Http; -@using Oqtane.Client.Modules.Controls +@using Oqtane.Shared; @inherits ModuleBase @inject IUriHelper UriHelper @inject HttpClient http +@inject SiteState sitestate
    @@ -32,7 +33,7 @@ protected override async Task OnInitAsync() { - HtmlTextService htmltextservice = new HtmlTextService(http, UriHelper); + HtmlTextService htmltextservice = new HtmlTextService(http, sitestate); List htmltextlist = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId); if (htmltextlist != null) { @@ -43,7 +44,7 @@ private async Task SaveContent() { - HtmlTextService htmltextservice = new HtmlTextService(http, UriHelper); + HtmlTextService htmltextservice = new HtmlTextService(http, sitestate); if (htmltext != null) { htmltext.Content = content; diff --git a/Oqtane.Client/Modules/HtmlText/Index.razor b/Oqtane.Client/Modules/HtmlText/Index.razor index a19ee82b5..a8fa3cd36 100644 --- a/Oqtane.Client/Modules/HtmlText/Index.razor +++ b/Oqtane.Client/Modules/HtmlText/Index.razor @@ -3,9 +3,10 @@ @using Oqtane.Shared.Modules.HtmlText.Models @using System.Net.Http; @using Oqtane.Client.Modules.Controls +@using Oqtane.Shared; @inherits ModuleBase @inject HttpClient http -@inject IUriHelper UriHelper +@inject SiteState sitestate @((MarkupString)content) @@ -16,7 +17,7 @@ protected override async Task OnInitAsync() { - HtmlTextService htmltextservice = new HtmlTextService(http, UriHelper); + HtmlTextService htmltextservice = new HtmlTextService(http, sitestate); List htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId); if (htmltext != null) { diff --git a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs index fb4097514..c6f2443b7 100644 --- a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs +++ b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs @@ -5,18 +5,24 @@ using Microsoft.AspNetCore.Components; using Oqtane.Services; using Oqtane.Shared.Modules.HtmlText.Models; +using Oqtane.Shared; namespace Oqtane.Client.Modules.HtmlText.Services { public class HtmlTextService : ServiceBase, IHtmlTextService { private readonly HttpClient http; - private readonly string apiurl; + private readonly SiteState sitestate; - public HtmlTextService(HttpClient http, IUriHelper urihelper) + public HtmlTextService(HttpClient http, SiteState sitestate) { this.http = http; - apiurl = CreateApiUrl(urihelper.GetAbsoluteUri(), "HtmlText"); + this.sitestate = sitestate; + } + + private string apiurl + { + get { return CreateApiUrl(sitestate.Alias, "HtmlText"); } } public async Task> GetHtmlTextAsync(int ModuleId) diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index f81d3bced..80fc318b1 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -20,46 +20,32 @@ public class ModuleBase : ComponentBase, IModuleControl public string NavigateUrl() { - return NavigateUrl(PageState.Page.Path, false); + return Utilities.NavigateUrl(PageState); } public string NavigateUrl(bool reload) { - return NavigateUrl(PageState.Page.Path, reload); + return Utilities.NavigateUrl(PageState, reload); } public string NavigateUrl(string path) { - return NavigateUrl(path, false); + return Utilities.NavigateUrl(PageState, path); } public string NavigateUrl(string path, bool reload) { - string url = PageState.Alias + path; - if (reload) - { - url += "?reload=true"; - } - return url; + return Utilities.NavigateUrl(PageState, path, reload); } public string EditUrl(string action) { - return EditUrl(action, ""); + return Utilities.EditUrl(PageState, ModuleState, action, ""); } public string EditUrl(string action, string parameters) { - string url = PageState.Alias + PageState.Page.Path + "?mid=" + ModuleState.ModuleId.ToString(); - if (action != "") - { - url += "&ctl=" + action; - } - if (!string.IsNullOrEmpty(parameters)) - { - url += "&" + parameters; - } - return url; + return Utilities.EditUrl(PageState, ModuleState, action, parameters); } } } diff --git a/Oqtane.Client/Services/AliasService.cs b/Oqtane.Client/Services/AliasService.cs new file mode 100644 index 000000000..acc81b47c --- /dev/null +++ b/Oqtane.Client/Services/AliasService.cs @@ -0,0 +1,53 @@ +using Oqtane.Models; +using System.Threading.Tasks; +using System.Net.Http; +using System.Linq; +using Microsoft.AspNetCore.Components; +using System.Collections.Generic; +using Oqtane.Shared; + +namespace Oqtane.Services +{ + public class AliasService : ServiceBase, IAliasService + { + private readonly HttpClient http; + private readonly IUriHelper urihelper; + + public AliasService(HttpClient http, IUriHelper urihelper) + { + this.http = http; + this.urihelper = urihelper; + } + + private string apiurl + { + get { return CreateApiUrl(urihelper.GetAbsoluteUri(), "Alias"); } + } + + public async Task> GetAliasesAsync() + { + List aliases = await http.GetJsonAsync>(apiurl); + return aliases.OrderBy(item => item.Name).ToList(); + } + + public async Task GetAliasAsync(int AliasId) + { + List aliases = await http.GetJsonAsync>(apiurl); + return aliases.Where(item => item.AliasId == AliasId).FirstOrDefault(); + } + + public async Task AddAliasAsync(Alias alias) + { + await http.PostJsonAsync(apiurl, alias); + } + + public async Task UpdateAliasAsync(Alias alias) + { + await http.PutJsonAsync(apiurl + "/" + alias.AliasId.ToString(), alias); + } + public async Task DeleteAliasAsync(int AliasId) + { + await http.DeleteAsync(apiurl + "/" + AliasId.ToString()); + } + } +} diff --git a/Oqtane.Client/Services/IAliasService.cs b/Oqtane.Client/Services/IAliasService.cs new file mode 100644 index 000000000..c4fe200be --- /dev/null +++ b/Oqtane.Client/Services/IAliasService.cs @@ -0,0 +1,19 @@ +using Oqtane.Models; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Oqtane.Services +{ + public interface IAliasService + { + Task> GetAliasesAsync(); + + Task GetAliasAsync(int AliasId); + + Task AddAliasAsync(Alias alias); + + Task UpdateAliasAsync(Alias alias); + + Task DeleteAliasAsync(int AliasId); + } +} diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index baa039d4d..75b06b2f7 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -6,54 +6,56 @@ using Microsoft.AspNetCore.Components; using System; using System.Reflection; +using Oqtane.Shared; namespace Oqtane.Services { public class ModuleDefinitionService : ServiceBase, IModuleDefinitionService { private readonly HttpClient http; - private readonly string apiurl; + private readonly SiteState sitestate; - private List moduledefinitions; - - public ModuleDefinitionService(HttpClient http, IUriHelper urihelper) + public ModuleDefinitionService(HttpClient http, SiteState sitestate) { this.http = http; - apiurl = CreateApiUrl(urihelper.GetAbsoluteUri(), "ModuleDefinition"); + this.sitestate = sitestate; + } + + private string apiurl + { + get { return CreateApiUrl(sitestate.Alias, "ModuleDefinition"); } } public async Task> GetModuleDefinitionsAsync() { - if (moduledefinitions == null) - { - moduledefinitions = await http.GetJsonAsync>(apiurl); + List moduledefinitions = await http.GetJsonAsync>(apiurl); - // get list of loaded assemblies - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + // get list of loaded assemblies + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach (ModuleDefinition moduledefinition in moduledefinitions) + foreach (ModuleDefinition moduledefinition in moduledefinitions) + { + if (moduledefinition.Dependencies != "") { - if (moduledefinition.Dependencies != "") + foreach (string dependency in moduledefinition.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { - foreach (string dependency in moduledefinition.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + string assemblyname = dependency.Replace(".dll", ""); + if (assemblies.Where(item => item.FullName.StartsWith(assemblyname + ",")).FirstOrDefault() == null) { - string assemblyname = dependency.Replace(".dll", ""); - if (assemblies.Where(item => item.FullName.StartsWith(assemblyname + ",")).FirstOrDefault() == null) - { - // download assembly from server and load - var bytes = await http.GetByteArrayAsync("_framework/_bin/" + assemblyname + ".dll"); - Assembly.Load(bytes); - } + // download assembly from server and load + var bytes = await http.GetByteArrayAsync("_framework/_bin/" + assemblyname + ".dll"); + Assembly.Load(bytes); } } - if (assemblies.Where(item => item.FullName.StartsWith(moduledefinition.AssemblyName + ",")).FirstOrDefault() == null) - { - // download assembly from server and load - var bytes = await http.GetByteArrayAsync("_framework/_bin/" + moduledefinition.AssemblyName + ".dll"); - Assembly.Load(bytes); - } + } + if (assemblies.Where(item => item.FullName.StartsWith(moduledefinition.AssemblyName + ",")).FirstOrDefault() == null) + { + // download assembly from server and load + var bytes = await http.GetByteArrayAsync("_framework/_bin/" + moduledefinition.AssemblyName + ".dll"); + Assembly.Load(bytes); } } + return moduledefinitions.OrderBy(item => item.Name).ToList(); } } diff --git a/Oqtane.Client/Services/ModuleService.cs b/Oqtane.Client/Services/ModuleService.cs index a2fd4cef1..3b4b6d747 100644 --- a/Oqtane.Client/Services/ModuleService.cs +++ b/Oqtane.Client/Services/ModuleService.cs @@ -4,18 +4,24 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; +using Oqtane.Shared; namespace Oqtane.Services { public class ModuleService : ServiceBase, IModuleService { private readonly HttpClient http; - private readonly string apiurl; + private readonly SiteState sitestate; - public ModuleService(HttpClient http, IUriHelper urihelper) + public ModuleService(HttpClient http, SiteState sitestate) { this.http = http; - apiurl = CreateApiUrl(urihelper.GetAbsoluteUri(), "Module"); + this.sitestate = sitestate; + } + + private string apiurl + { + get { return CreateApiUrl(sitestate.Alias, "Module"); } } public async Task> GetModulesAsync(int PageId) diff --git a/Oqtane.Client/Services/PageModuleService.cs b/Oqtane.Client/Services/PageModuleService.cs index d017eb506..240b4c441 100644 --- a/Oqtane.Client/Services/PageModuleService.cs +++ b/Oqtane.Client/Services/PageModuleService.cs @@ -4,18 +4,24 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; +using Oqtane.Shared; namespace Oqtane.Services { public class PageModuleService : ServiceBase, IPageModuleService { private readonly HttpClient http; - private readonly string apiurl; + private readonly SiteState sitestate; - public PageModuleService(HttpClient http, IUriHelper urihelper) + public PageModuleService(HttpClient http, SiteState sitestate) { this.http = http; - apiurl = CreateApiUrl(urihelper.GetAbsoluteUri(), "PageModule"); + this.sitestate = sitestate; + } + + private string apiurl + { + get { return CreateApiUrl(sitestate.Alias, "PageModule"); } } public async Task> GetPageModulesAsync() diff --git a/Oqtane.Client/Services/PageService.cs b/Oqtane.Client/Services/PageService.cs index 26ed564f5..172d79509 100644 --- a/Oqtane.Client/Services/PageService.cs +++ b/Oqtane.Client/Services/PageService.cs @@ -4,18 +4,24 @@ using System.Net.Http; using Microsoft.AspNetCore.Components; using System.Collections.Generic; +using Oqtane.Shared; namespace Oqtane.Services { public class PageService : ServiceBase, IPageService { private readonly HttpClient http; - private readonly string apiurl; + private readonly SiteState sitestate; - public PageService(HttpClient http, IUriHelper urihelper) + public PageService(HttpClient http, SiteState sitestate) { this.http = http; - apiurl = CreateApiUrl(urihelper.GetAbsoluteUri(), "Page"); + this.sitestate = sitestate; + } + + private string apiurl + { + get { return CreateApiUrl(sitestate.Alias, "Page"); } } public async Task> GetPagesAsync(int SiteId) diff --git a/Oqtane.Client/Services/ServiceBase.cs b/Oqtane.Client/Services/ServiceBase.cs index 71a1b6114..ca7c588fe 100644 --- a/Oqtane.Client/Services/ServiceBase.cs +++ b/Oqtane.Client/Services/ServiceBase.cs @@ -1,20 +1,24 @@ using System; +using Oqtane.Models; using Oqtane.Shared; namespace Oqtane.Services { public class ServiceBase { + // method for alias agnostic api call public string CreateApiUrl(string absoluteUri, string serviceName) { Uri uri = new Uri(absoluteUri); - string apiurl = uri.Scheme + "://" + uri.Authority + "/"; - string alias = Utilities.GetAlias(absoluteUri); - if (alias != "") - { - apiurl += alias; - } - else + string apiurl = uri.Scheme + "://" + uri.Authority + "/~/api/" + serviceName; + return apiurl; + } + + // method for alias specific api call + public string CreateApiUrl(Alias alias, string serviceName) + { + string apiurl = alias.Url + "/"; + if (alias.Path == "") { apiurl += "~/"; } diff --git a/Oqtane.Client/Services/SiteService.cs b/Oqtane.Client/Services/SiteService.cs index 4289bdf54..7d7d135e5 100644 --- a/Oqtane.Client/Services/SiteService.cs +++ b/Oqtane.Client/Services/SiteService.cs @@ -4,18 +4,24 @@ using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; +using Oqtane.Shared; namespace Oqtane.Services { public class SiteService : ServiceBase, ISiteService { private readonly HttpClient http; - private readonly string apiurl; + private readonly SiteState sitestate; - public SiteService(HttpClient http, IUriHelper urihelper) + public SiteService(HttpClient http, SiteState sitestate) { this.http = http; - apiurl = CreateApiUrl(urihelper.GetAbsoluteUri(), "Site"); + this.sitestate = sitestate; + } + + private string apiurl + { + get { return CreateApiUrl(sitestate.Alias, "Site"); } } public async Task> GetSitesAsync() diff --git a/Oqtane.Client/Services/TenantService.cs b/Oqtane.Client/Services/TenantService.cs index 6e41be0a2..9904f6aa4 100644 --- a/Oqtane.Client/Services/TenantService.cs +++ b/Oqtane.Client/Services/TenantService.cs @@ -2,18 +2,24 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; +using Oqtane.Shared; namespace Oqtane.Services { public class TenantService : ServiceBase, ITenantService { private readonly HttpClient http; - private readonly string apiurl; + private readonly SiteState sitestate; - public TenantService(HttpClient http, IUriHelper urihelper) + public TenantService(HttpClient http, SiteState sitestate) { this.http = http; - apiurl = CreateApiUrl(urihelper.GetAbsoluteUri(), "Tenant"); + this.sitestate = sitestate; + } + + private string apiurl + { + get { return CreateApiUrl(sitestate.Alias, "Tenant"); } } public async Task GetTenantAsync() diff --git a/Oqtane.Client/Services/ThemeService.cs b/Oqtane.Client/Services/ThemeService.cs index 5ed20b459..b6c2201e5 100644 --- a/Oqtane.Client/Services/ThemeService.cs +++ b/Oqtane.Client/Services/ThemeService.cs @@ -13,48 +13,49 @@ namespace Oqtane.Services public class ThemeService : ServiceBase, IThemeService { private readonly HttpClient http; - private readonly string apiurl; + private readonly SiteState sitestate; - private List themes; - - public ThemeService(HttpClient http, IUriHelper urihelper) + public ThemeService(HttpClient http, SiteState sitestate) { this.http = http; - apiurl = CreateApiUrl(urihelper.GetAbsoluteUri(), "Theme"); + this.sitestate = sitestate; + } + + private string apiurl + { + get { return CreateApiUrl(sitestate.Alias, "Theme"); } } - //TODO: Implement Caching or otherwise on this, and other calls within this class + public async Task> GetThemesAsync() { - if (themes == null) - { - themes = await http.GetJsonAsync>(apiurl); + List themes = await http.GetJsonAsync>(apiurl); - // get list of loaded assemblies - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + // get list of loaded assemblies + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach (Theme theme in themes) + foreach (Theme theme in themes) + { + if (theme.Dependencies != "") { - if (theme.Dependencies != "") + foreach (string dependency in theme.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { - foreach (string dependency in theme.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + string assemblyname = dependency.Replace(".dll", ""); + if (assemblies.Where(item => item.FullName.StartsWith(assemblyname + ",")).FirstOrDefault() == null) { - string assemblyname = dependency.Replace(".dll", ""); - if (assemblies.Where(item => item.FullName.StartsWith(assemblyname + ",")).FirstOrDefault() == null) - { - // download assembly from server and load - var bytes = await http.GetByteArrayAsync("_framework/_bin/" + assemblyname + ".dll"); - Assembly.Load(bytes); - } + // download assembly from server and load + var bytes = await http.GetByteArrayAsync("_framework/_bin/" + assemblyname + ".dll"); + Assembly.Load(bytes); } } - if (assemblies.Where(item => item.FullName.StartsWith(theme.AssemblyName + ",")).FirstOrDefault() == null) - { - // download assembly from server and load - var bytes = await http.GetByteArrayAsync("_framework/_bin/" + theme.AssemblyName + ".dll"); - Assembly.Load(bytes); - } + } + if (assemblies.Where(item => item.FullName.StartsWith(theme.AssemblyName + ",")).FirstOrDefault() == null) + { + // download assembly from server and load + var bytes = await http.GetByteArrayAsync("_framework/_bin/" + theme.AssemblyName + ".dll"); + Assembly.Load(bytes); } } + return themes.OrderBy(item => item.Name).ToList(); } diff --git a/Oqtane.Client/Services/UserService.cs b/Oqtane.Client/Services/UserService.cs index 290f69f5e..bc00f00a1 100644 --- a/Oqtane.Client/Services/UserService.cs +++ b/Oqtane.Client/Services/UserService.cs @@ -12,12 +12,17 @@ namespace Oqtane.Services public class UserService : ServiceBase, IUserService { private readonly HttpClient http; - private readonly string apiurl; + private readonly SiteState sitestate; - public UserService(HttpClient http, IUriHelper urihelper) + public UserService(HttpClient http, SiteState sitestate) { this.http = http; - apiurl = CreateApiUrl(urihelper.GetAbsoluteUri(), "User"); + this.sitestate = sitestate; + } + + private string apiurl + { + get { return CreateApiUrl(sitestate.Alias, "User"); } } public async Task> GetUsersAsync() diff --git a/Oqtane.Client/Shared/PageState.cs b/Oqtane.Client/Shared/PageState.cs index 59f2306de..419b92adf 100644 --- a/Oqtane.Client/Shared/PageState.cs +++ b/Oqtane.Client/Shared/PageState.cs @@ -6,7 +6,10 @@ namespace Oqtane.Shared { public class PageState { - public string Alias { get; set; } + public List ModuleDefinitions { get; set; } + public List Themes { get; set; } + public List Aliases { get; set; } + public Alias Alias { get; set; } public Site Site { get; set; } public List Pages { get; set; } public Page Page { get; set; } @@ -16,6 +19,5 @@ public class PageState public Dictionary QueryString { get; set; } public int ModuleId { get; set; } public string Control { get; set; } - public string Mode { get; set; } } } diff --git a/Oqtane.Client/Shared/SiteRouter.razor b/Oqtane.Client/Shared/SiteRouter.razor index 6b2709fa5..89621362e 100644 --- a/Oqtane.Client/Shared/SiteRouter.razor +++ b/Oqtane.Client/Shared/SiteRouter.razor @@ -5,8 +5,10 @@ @using System.Collections.Generic @using Oqtane.Shared @using Microsoft.JSInterop +@inject SiteState SiteState @inject IUriHelper UriHelper @inject IJSRuntime jsRuntime +@inject IAliasService AliasService @inject ITenantService TenantService @inject ISiteService SiteService @inject IPageService PageService @@ -25,12 +27,6 @@ RenderFragment DynamicComponent { get; set; } private string _absoluteUri; - string alias; - Site site; - List pages; - Page page; - User user; - List modules; PageState pagestate; protected override void OnInit() @@ -63,33 +59,51 @@ private async Task Refresh() { - List moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(); - List themes = await ThemeService.GetThemesAsync(); + List moduledefinitions; + List themes; + List aliases; + Alias alias; + Site site; + List pages; + Page page; + User user; + List modules; bool reload = false; if (PageState == null) { - Tenant tenant = await TenantService.GetTenantAsync(); - site = await SiteService.GetSiteAsync(tenant.SiteId); - alias = Utilities.GetAlias(_absoluteUri); + aliases = await AliasService.GetAliasesAsync(); + alias = null; } else { - site = PageState.Site; + aliases = PageState.Aliases; alias = PageState.Alias; } - if (Utilities.GetAlias(_absoluteUri) != alias) + if (alias == null || GetAlias(_absoluteUri, aliases).Name != alias.Name) { - Tenant tenant = await TenantService.GetTenantAsync(); - site = await SiteService.GetSiteAsync(tenant.SiteId); - alias = Utilities.GetAlias(_absoluteUri); + alias = GetAlias(_absoluteUri, aliases); + SiteState.Alias = alias; // set state for services reload = true; } - if (site != null) + if (PageState == null || reload == true) + { + moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(); + themes = await ThemeService.GetThemesAsync(); + site = await SiteService.GetSiteAsync(alias.SiteId); + } + else + { + moduledefinitions = PageState.ModuleDefinitions; + themes = PageState.Themes; + site = PageState.Site; + } + if (site != null || reload == true) { var interop = new Interop(jsRuntime); string userid = await interop.GetCookie("user"); + user = null; if (PageState == null || reload == true) { if (!string.IsNullOrEmpty(userid)) @@ -120,9 +134,11 @@ } string path = new Uri(_absoluteUri).PathAndQuery.Substring(1); - if (alias != "") + if (path.EndsWith("/")) { path = path.Substring(0, path.Length - 1); } + if (alias.Path != "") { - path = path.Replace(alias, ""); + path = path.Replace(alias.Path, ""); + if (path.StartsWith("/")) { path = path.Substring(1); } } Dictionary querystring = ParseQueryString(path); @@ -165,6 +181,9 @@ if (UserService.IsAuthorized(user, page.ViewPermissions)) { pagestate = new PageState(); + pagestate.ModuleDefinitions = moduledefinitions; + pagestate.Themes = themes; + pagestate.Aliases = aliases; pagestate.Alias = alias; pagestate.Site = site; pagestate.Pages = pages; @@ -174,7 +193,6 @@ pagestate.QueryString = querystring; pagestate.ModuleId = -1; pagestate.Control = ""; - pagestate.Mode = "client"; if (querystring.ContainsKey("mid")) { @@ -309,4 +327,32 @@ return modules; } + private Alias GetAlias(string absoluteUri, List aliases) + { + + string aliasname; + Alias alias = null; + Uri uri = new Uri(absoluteUri); + + if (uri.Segments.Count() > 1) + { + // check if first path segment is an alias ( ie. a subfolder - www.domain.com/subfolder ) + aliasname = uri.Authority + "/" + uri.Segments[1]; + if (aliasname.EndsWith("/")) { aliasname = aliasname.Substring(0, aliasname.Length - 1); } + alias = aliases.Where(item => item.Name == aliasname).FirstOrDefault(); + } + if (alias == null) + { + aliasname = uri.Authority; + alias = aliases.Where(item => item.Name == aliasname).FirstOrDefault(); + } + if (alias == null && aliases.Count > 0) + { + // use first alias if Uri does not exist + alias = aliases.FirstOrDefault(); + } + alias.Scheme = uri.Scheme; + return alias; + } + } \ No newline at end of file diff --git a/Oqtane.Client/Shared/SiteState.cs b/Oqtane.Client/Shared/SiteState.cs new file mode 100644 index 000000000..89de8abe9 --- /dev/null +++ b/Oqtane.Client/Shared/SiteState.cs @@ -0,0 +1,11 @@ +using Oqtane.Models; + +namespace Oqtane.Shared +{ + // this class is used for passing state between Blazor components and Services + public class SiteState + { + public Alias Alias { get; set; } + + } +} diff --git a/Oqtane.Client/Shared/Utilities.cs b/Oqtane.Client/Shared/Utilities.cs index 043dac6cb..410e2b0ee 100644 --- a/Oqtane.Client/Shared/Utilities.cs +++ b/Oqtane.Client/Shared/Utilities.cs @@ -1,18 +1,52 @@ using System; +using Oqtane.Models; namespace Oqtane.Shared { public class Utilities { - public static string GetAlias(string absoluteUri) + public static string NavigateUrl(PageState pagestate) { - string alias = ""; - Uri uri = new Uri(absoluteUri); - if (uri.AbsolutePath.StartsWith("/~")) + return NavigateUrl(pagestate, pagestate.Page.Path, false); + } + + public static string NavigateUrl(PageState pagestate, bool reload) + { + return NavigateUrl(pagestate, pagestate.Page.Path, reload); + } + + public static string NavigateUrl(PageState pagestate, string path) + { + return NavigateUrl(pagestate, path, false); + } + + public static string NavigateUrl(PageState pagestate, string path, bool reload) + { + string url = pagestate.Alias.Path + "/" + path; + if (reload) + { + url += "?reload=true"; + } + return url; + } + + public static string EditUrl(PageState pagestate, Module modulestate, string action) + { + return EditUrl(pagestate, modulestate, action, ""); + } + + public static string EditUrl(PageState pagestate, Module modulestate, string action, string parameters) + { + string url = pagestate.Alias.Path + "/" + pagestate.Page.Path + "?mid=" + modulestate.ModuleId.ToString(); + if (action != "") + { + url += "&ctl=" + action; + } + if (!string.IsNullOrEmpty(parameters)) { - alias = uri.Segments[1]; + url += "&" + parameters; } - return alias; + return url; } public static string GetTypeNameClass(string typename) diff --git a/Oqtane.Client/Startup.cs b/Oqtane.Client/Startup.cs index 24e6152a2..7f1b70781 100644 --- a/Oqtane.Client/Startup.cs +++ b/Oqtane.Client/Startup.cs @@ -1,5 +1,3 @@ -#define ServerSideBlazor - using Microsoft.AspNetCore.Components.Builder; using Microsoft.Extensions.DependencyInjection; using System; @@ -9,6 +7,7 @@ using Microsoft.AspNetCore.Components; using System.Reflection; using Oqtane.Modules; +using Oqtane.Shared; namespace Oqtane.Client { @@ -28,11 +27,11 @@ public void Configure(IComponentsApplicationBuilder app) #if WASM public void ConfigureServices(IServiceCollection services) { - // register singleton core services - services.AddSingleton(); - services.AddSingleton(); - // register scoped core services + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/Oqtane.Client/Themes/AdminContainer.razor b/Oqtane.Client/Themes/AdminContainer.razor index 9f5937d32..ee62578d3 100644 --- a/Oqtane.Client/Themes/AdminContainer.razor +++ b/Oqtane.Client/Themes/AdminContainer.razor @@ -25,7 +25,7 @@ protected override void OnInit() { - closeurl = PageState.Alias + PageState.Page.Path; + closeurl = NavigateUrl(); } } diff --git a/Oqtane.Client/Themes/ContainerBase.cs b/Oqtane.Client/Themes/ContainerBase.cs index 31c3b9ccf..d476b7b71 100644 --- a/Oqtane.Client/Themes/ContainerBase.cs +++ b/Oqtane.Client/Themes/ContainerBase.cs @@ -13,5 +13,35 @@ public class ContainerBase : ComponentBase, IContainerControl protected Module ModuleState { get; set; } public virtual string Name { get; set; } + + public string NavigateUrl() + { + return Utilities.NavigateUrl(PageState); + } + + public string NavigateUrl(bool reload) + { + return Utilities.NavigateUrl(PageState, reload); + } + + public string NavigateUrl(string path) + { + return Utilities.NavigateUrl(PageState, path); + } + + public string NavigateUrl(string path, bool reload) + { + return Utilities.NavigateUrl(PageState, path, reload); + } + + public string EditUrl(string action) + { + return Utilities.EditUrl(PageState, ModuleState, action, ""); + } + + public string EditUrl(string action, string parameters) + { + return Utilities.EditUrl(PageState, ModuleState, action, parameters); + } } } diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index 70ca422cd..ec7f38916 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -74,7 +74,7 @@ string display = "display: none"; List moduledefinitions; Dictionary containers = new Dictionary(); - int pagemanagementmoduleid; + int pagemanagementmoduleid = -1; string moduledefinitionname; string pane; string title = ""; @@ -82,11 +82,13 @@ protected override async Task OnInitAsync() { - //TODO: Move this to shared component. This is used in this control Add, Edit, and Delete controls as well - moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(); - containers = ThemeService.GetContainerTypes(await ThemeService.GetThemesAsync()); + moduledefinitions = PageState.ModuleDefinitions; + containers = ThemeService.GetContainerTypes(PageState.Themes); List modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId, Constants.PageManagementModule); - pagemanagementmoduleid = modules.FirstOrDefault().ModuleId; + if (modules.Count > 0) + { + pagemanagementmoduleid = modules.FirstOrDefault().ModuleId; + } if (UserService.IsAuthorized(PageState.User, PageState.Page.EditPermissions)) { display = "display: inline"; @@ -117,23 +119,26 @@ pagemodule.Order = 0; pagemodule.ContainerType = containertype; await PageModuleService.AddPageModuleAsync(pagemodule); - UriHelper.NavigateTo(PageState.Alias + PageState.Page.Path + "?reload=true"); + UriHelper.NavigateTo(NavigateUrl(true)); } private string PageUrl(string action) { string url = ""; - switch (action) + if (pagemanagementmoduleid != -1) { - case "Add": - url = "admin/pages?mid=" + pagemanagementmoduleid.ToString() + "&ctl=" + action; - break; - case "Edit": - url = "admin/pages?mid=" + pagemanagementmoduleid.ToString() + "&ctl=" + action + "&id=" + PageState.Page.PageId.ToString(); - break; - case "Delete": - url = "admin/pages?mid=" + pagemanagementmoduleid.ToString() + "&ctl=" + action + "&id=" + PageState.Page.PageId.ToString(); - break; + switch (action) + { + case "Add": + url = "admin/pages?mid=" + pagemanagementmoduleid.ToString() + "&ctl=" + action; + break; + case "Edit": + url = "admin/pages?mid=" + pagemanagementmoduleid.ToString() + "&ctl=" + action + "&id=" + PageState.Page.PageId.ToString(); + break; + case "Delete": + url = "admin/pages?mid=" + pagemanagementmoduleid.ToString() + "&ctl=" + action + "&id=" + PageState.Page.PageId.ToString(); + break; + } } return url; } diff --git a/Oqtane.Client/Themes/Controls/Login.razor b/Oqtane.Client/Themes/Controls/Login.razor index a1e2cf5f8..718c71fa3 100644 --- a/Oqtane.Client/Themes/Controls/Login.razor +++ b/Oqtane.Client/Themes/Controls/Login.razor @@ -29,13 +29,13 @@ { if (name == "Login") { - UriHelper.NavigateTo(PageState.Alias + "login"); + UriHelper.NavigateTo(NavigateUrl("login")); } else { var interop = new Interop(jsRuntime); await interop.SetCookie("user", "", 7); - UriHelper.NavigateTo(PageState.Alias, true); + UriHelper.NavigateTo(NavigateUrl(""), true); } } } diff --git a/Oqtane.Client/Themes/Controls/Logo.razor b/Oqtane.Client/Themes/Controls/Logo.razor index 761776586..2c42061f7 100644 --- a/Oqtane.Client/Themes/Controls/Logo.razor +++ b/Oqtane.Client/Themes/Controls/Logo.razor @@ -1,5 +1,5 @@ @using Oqtane.Themes @inherits ThemeObjectBase - + diff --git a/Oqtane.Client/Themes/Controls/Menu.razor b/Oqtane.Client/Themes/Controls/Menu.razor index cd21562f0..3589bc8cd 100644 --- a/Oqtane.Client/Themes/Controls/Menu.razor +++ b/Oqtane.Client/Themes/Controls/Menu.razor @@ -9,7 +9,7 @@