From 35b9b9e89b273a09d53bbe46b74c6568b5f2c597 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 16 Sep 2019 16:14:17 -0400 Subject: [PATCH] Optimized page reloading --- Oqtane.Client/Modules/Admin/Login/Index.razor | 3 +- .../Modules/Admin/ModuleDefinitions/Add.razor | 3 +- .../Modules/Admin/ModuleSettings/Index.razor | 3 +- Oqtane.Client/Modules/Admin/Pages/Add.razor | 3 +- .../Modules/Admin/Pages/Delete.razor | 10 +--- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 3 +- Oqtane.Client/Modules/Admin/Themes/Add.razor | 50 +++++++++++++++++ .../Modules/Admin/Themes/Index.razor | 3 +- Oqtane.Client/Modules/HtmlText/Edit.razor | 3 +- .../HtmlText/Services/HtmlTextService.cs | 13 ++++- Oqtane.Client/Modules/ModuleBase.cs | 19 ++++++- .../Services/Interfaces/IThemeService.cs | 1 + Oqtane.Client/Services/ThemeService.cs | 5 ++ Oqtane.Client/Shared/PageState.cs | 1 - Oqtane.Client/Shared/Reload.cs | 10 ++++ Oqtane.Client/Shared/SiteRouter.razor | 56 ++++++++++--------- Oqtane.Client/Shared/Utilities.cs | 8 ++- Oqtane.Client/Themes/ContainerBase.cs | 20 ++++++- .../Themes/Controls/ControlPanel.razor | 6 +- Oqtane.Client/Themes/Controls/Login.razor | 3 +- .../Themes/Controls/ModuleActions.razor | 3 +- Oqtane.Client/Themes/ThemeBase.cs | 19 ++++++- Oqtane.Client/Themes/ThemeObjectBase.cs | 19 ++++++- Oqtane.Server/Controllers/ThemeController.cs | 50 ++++++++++++++++- Oqtane.Server/Repository/ThemeRepository.cs | 2 +- Oqtane.Server/Scripts/00.00.00.sql | 25 +++++---- Oqtane.Server/Startup.cs | 33 +++++++++-- Oqtane.Shared/Models/Alias.cs | 18 +++++- Oqtane.Shared/Models/Role.cs | 1 + 29 files changed, 304 insertions(+), 89 deletions(-) create mode 100644 Oqtane.Client/Modules/Admin/Themes/Add.razor create mode 100644 Oqtane.Client/Shared/Reload.cs diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index b8a77ec6..06157646 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -89,8 +89,7 @@ if (user.IsAuthenticated) { authstateprovider.NotifyAuthenticationChanged(); - PageState.Reload = Constants.ReloadSite; - NavigationManager.NavigateTo(NavigateUrl(ReturnUrl)); + NavigationManager.NavigateTo(NavigateUrl(ReturnUrl, Reload.Site)); } else { diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index 4789f600..b9a6ea41 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -45,7 +45,6 @@ else private async Task InstallFile() { await ModuleDefinitionService.InstallModulesAsync(); - PageState.Reload = Constants.ReloadApplication; - NavigationManager.NavigateTo(NavigateUrl()); + NavigationManager.NavigateTo(NavigateUrl(Reload.Application)); } } diff --git a/Oqtane.Client/Modules/Admin/ModuleSettings/Index.razor b/Oqtane.Client/Modules/Admin/ModuleSettings/Index.razor index 26c7ebfe..51869930 100644 --- a/Oqtane.Client/Modules/Admin/ModuleSettings/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleSettings/Index.razor @@ -121,8 +121,7 @@ moduleType.GetMethod("UpdateSettings").Invoke(settings, null); // method must be public in settings component } - PageState.Reload = Constants.ReloadPage; - NavigationManager.NavigateTo(NavigateUrl()); + NavigationManager.NavigateTo(NavigateUrl(Reload.Page)); } } diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index bcf77cf5..2a105bcf 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -278,8 +278,7 @@ await PageService.AddPageAsync(page); await PageService.UpdatePageOrderAsync(page.SiteId, page.ParentId); - PageState.Reload = Constants.ReloadSite; - NavigationManager.NavigateTo(NavigateUrl(page.Path)); + NavigationManager.NavigateTo(NavigateUrl(page.Path, Reload.Site)); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/Pages/Delete.razor b/Oqtane.Client/Modules/Admin/Pages/Delete.razor index 37075775..7a4fc272 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Delete.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Delete.razor @@ -179,15 +179,7 @@ try { await PageService.DeletePageAsync(Int32.Parse(PageState.QueryString["id"])); - PageState.Reload = Constants.ReloadSite; - if (PageState.Page.Name == "Page Management") - { - NavigationManager.NavigateTo(NavigateUrl()); - } - else - { - NavigationManager.NavigateTo(NavigateUrl("")); - } + NavigationManager.NavigateTo(NavigateUrl("", Reload.Site)); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 65107ecc..80cca79d 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -354,8 +354,7 @@ } } - PageState.Reload = Constants.ReloadSite; - NavigationManager.NavigateTo(NavigateUrl(page.Path)); + NavigationManager.NavigateTo(NavigateUrl(page.Path, Reload.Site)); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor new file mode 100644 index 00000000..6e9d80c5 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -0,0 +1,50 @@ +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Oqtane.Modules.Controls +@using Oqtane.Modules +@using Oqtane.Services +@using Oqtane.Shared +@namespace Oqtane.Modules.Admin.Themes +@inherits ModuleBase +@inject NavigationManager NavigationManager +@inject IFileService FileService +@inject IThemeService ThemeService + + + + + + +
+ + + +
+@if (uploaded) +{ + +} +else +{ + +} +Cancel + +@code { + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + + bool uploaded = false; + + private async Task UploadFile() + { + await FileService.UploadFilesAsync("Themes"); + uploaded = true; + StateHasChanged(); + } + + private async Task InstallFile() + { + await ThemeService.InstallThemesAsync(); + NavigationManager.NavigateTo(NavigateUrl(Reload.Application)); + } +} diff --git a/Oqtane.Client/Modules/Admin/Themes/Index.razor b/Oqtane.Client/Modules/Admin/Themes/Index.razor index f272dba6..d1c188fb 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Index.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Index.razor @@ -2,9 +2,9 @@ @using Oqtane.Services @using Oqtane.Models @using Oqtane.Modules +@using Oqtane.Modules.Controls @namespace Oqtane.Modules.Admin.Themes @inherits ModuleBase - @inject IThemeService ThemeService @if (Themes == null) @@ -13,6 +13,7 @@ } else { + diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index 887bd056..aa3a20ae 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -80,8 +80,7 @@ htmltext.Content = content; await htmltextservice.AddHtmlTextAsync(htmltext); } - PageState.Reload = Constants.ReloadPage; - NavigationManager.NavigateTo(NavigateUrl()); + NavigationManager.NavigateTo(NavigateUrl(Reload.Page)); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs index d3c602c7..47244ed3 100644 --- a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs +++ b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Components; using Oqtane.Services; using Oqtane.Modules.HtmlText.Models; using Oqtane.Shared; +using System.Text.Json; namespace Oqtane.Modules.HtmlText.Services { @@ -29,7 +30,17 @@ namespace Oqtane.Modules.HtmlText.Services public async Task GetHtmlTextAsync(int ModuleId) { - return await http.GetJsonAsync(apiurl + "/" + ModuleId.ToString() + "?entityid=" + ModuleId.ToString()); + HtmlTextInfo htmltext; + try + { + // exception handling is required because GetJsonAsync() returns an error if no content exists for the ModuleId ( https://github.com/aspnet/AspNetCore/issues/14041 ) + htmltext = await http.GetJsonAsync(apiurl + "/" + ModuleId.ToString() + "?entityid=" + ModuleId.ToString()); + } + catch + { + htmltext = null; + } + return htmltext; } public async Task AddHtmlTextAsync(HtmlTextInfo htmltext) diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 6f176c38..0f2fba26 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -25,14 +25,29 @@ namespace Oqtane.Modules return NavigateUrl(PageState.Page.Path); } + public string NavigateUrl(Reload reload) + { + return NavigateUrl(PageState.Page.Path, reload); + } + public string NavigateUrl(string path) { - return NavigateUrl(path, ""); + return NavigateUrl(path, "", Reload.None); + } + + public string NavigateUrl(string path, Reload reload) + { + return NavigateUrl(path, "", reload); } public string NavigateUrl(string path, string parameters) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, Reload.None); + } + + public string NavigateUrl(string path, string parameters, Reload reload) + { + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, reload); } public string EditUrl(string action) diff --git a/Oqtane.Client/Services/Interfaces/IThemeService.cs b/Oqtane.Client/Services/Interfaces/IThemeService.cs index dcc6f674..8ed4db1e 100644 --- a/Oqtane.Client/Services/Interfaces/IThemeService.cs +++ b/Oqtane.Client/Services/Interfaces/IThemeService.cs @@ -10,5 +10,6 @@ namespace Oqtane.Services Dictionary GetThemeTypes(List themes); Dictionary GetPaneLayoutTypes(List themes); Dictionary GetContainerTypes(List themes); + Task InstallThemesAsync(); } } diff --git a/Oqtane.Client/Services/ThemeService.cs b/Oqtane.Client/Services/ThemeService.cs index 46a75c9e..670372f1 100644 --- a/Oqtane.Client/Services/ThemeService.cs +++ b/Oqtane.Client/Services/ThemeService.cs @@ -99,5 +99,10 @@ namespace Oqtane.Services } return selectableContainers; } + + public async Task InstallThemesAsync() + { + await http.GetJsonAsync>(apiurl + "/install"); + } } } diff --git a/Oqtane.Client/Shared/PageState.cs b/Oqtane.Client/Shared/PageState.cs index 37721465..f8dfe93e 100644 --- a/Oqtane.Client/Shared/PageState.cs +++ b/Oqtane.Client/Shared/PageState.cs @@ -21,6 +21,5 @@ namespace Oqtane.Shared public string Control { get; set; } public bool EditMode { get; set; } public bool DesignMode { get; set; } - public int Reload { get; set; } } } diff --git a/Oqtane.Client/Shared/Reload.cs b/Oqtane.Client/Shared/Reload.cs new file mode 100644 index 00000000..18e8f448 --- /dev/null +++ b/Oqtane.Client/Shared/Reload.cs @@ -0,0 +1,10 @@ +namespace Oqtane.Shared +{ + public enum Reload + { + None, + Page, + Site, + Application + } +} diff --git a/Oqtane.Client/Shared/SiteRouter.razor b/Oqtane.Client/Shared/SiteRouter.razor index e4c9d1d1..c32d15d1 100644 --- a/Oqtane.Client/Shared/SiteRouter.razor +++ b/Oqtane.Client/Shared/SiteRouter.razor @@ -90,16 +90,34 @@ string control = ""; bool editmode = false; bool designmode = false; - int reload = 0; + Reload reload = Reload.None; + + // get Url path and querystring ( and remove anchors ) + string path = new Uri(_absoluteUri).PathAndQuery.Substring(1); + if (path.IndexOf("#") != -1) + { + path = path.Substring(0, path.IndexOf("#")); + } + + // parse querystring and remove + Dictionary querystring = new Dictionary(); + if (path.IndexOf("?") != -1) + { + querystring = ParseQueryString(path); + path = path.Substring(0, path.IndexOf("?")); + } + if (querystring.ContainsKey("reload")) + { + reload = (Reload)int.Parse(querystring["reload"]); + } if (PageState != null) { - reload = PageState.Reload; editmode = PageState.EditMode; designmode = PageState.DesignMode; } - if (PageState == null || reload == Constants.ReloadApplication) + if (PageState == null || reload == Reload.Application) { moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(); themes = await ThemeService.GetThemesAsync(); @@ -119,9 +137,9 @@ { alias = GetAlias(_absoluteUri, aliases); SiteState.Alias = alias; // set state for services - reload = Constants.ReloadSite; + reload = Reload.Site; } - if (PageState == null || reload <= Constants.ReloadSite) + if (PageState == null || reload <= Reload.Site) { site = await SiteService.GetSiteAsync(alias.SiteId); } @@ -131,7 +149,7 @@ } if (site != null) { - if (PageState == null || reload >= Constants.ReloadSite) + if (PageState == null || reload >= Reload.Site) { pages = await PageService.GetPagesAsync(site.SiteId); } @@ -140,21 +158,6 @@ pages = PageState.Pages; } - // get Url path and querystring ( and remove anchors ) - string path = new Uri(_absoluteUri).PathAndQuery.Substring(1); - if (path.IndexOf("#") != -1) - { - path = path.Substring(0, path.IndexOf("#")); - } - - // parse querystring and remove - Dictionary querystring = new Dictionary(); - if (path.IndexOf("?") != -1) - { - querystring = ParseQueryString(path); - path = path.Substring(0, path.IndexOf("?")); - } - // format path and remove alias path = path.Replace("//", "/"); if (!path.EndsWith("/")) { path += "/"; } @@ -185,7 +188,7 @@ // remove trailing slash so it can be used as a key for Pages if (path.EndsWith("/")) path = path.Substring(0, path.Length - 1); - if (PageState == null || reload >= Constants.ReloadPage) + if (PageState == null || reload >= Reload.Page) { page = pages.Where(item => item.Path == path).FirstOrDefault(); } @@ -204,13 +207,13 @@ if (page.Path != path) { page = pages.Where(item => item.Path == path).FirstOrDefault(); - reload = Constants.ReloadPage; + reload = Reload.Page; editmode = page.EditMode; designmode = false; } user = null; - if (PageState == null || reload >= Constants.ReloadPage) + if (PageState == null || reload >= Reload.Page) { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); if (authState.User.Identity.IsAuthenticated) @@ -244,10 +247,10 @@ if (PageState != null && (PageState.ModuleId != pagestate.ModuleId || PageState.Control != pagestate.Control)) { - reload = Constants.ReloadPage; + reload = Reload.Page; } - if (PageState == null || reload >= Constants.ReloadPage) + if (PageState == null || reload >= Reload.Page) { modules = await ModuleService.GetModulesAsync(page.PageId); modules = ProcessModules(modules, moduledefinitions, pagestate.Control, page.Panes); @@ -259,7 +262,6 @@ pagestate.Modules = modules; pagestate.EditMode = editmode; pagestate.DesignMode = designmode; - pagestate.Reload = Constants.ReloadReset; OnStateChange?.Invoke(pagestate); } diff --git a/Oqtane.Client/Shared/Utilities.cs b/Oqtane.Client/Shared/Utilities.cs index b4775f6d..cd2af1d3 100644 --- a/Oqtane.Client/Shared/Utilities.cs +++ b/Oqtane.Client/Shared/Utilities.cs @@ -6,7 +6,7 @@ namespace Oqtane.Shared public class Utilities { - public static string NavigateUrl(string alias, string path, string parameters) + public static string NavigateUrl(string alias, string path, string parameters, Reload reload) { string url = ""; if (alias != "") @@ -25,6 +25,10 @@ namespace Oqtane.Shared { url += "?" + parameters; } + if (reload != Reload.None) + { + url += ((string.IsNullOrEmpty(parameters)) ? "?" : "&") + "reload=" + ((int)reload).ToString(); + } if (!url.StartsWith("/")) { url = "/" + url; @@ -34,7 +38,7 @@ namespace Oqtane.Shared public static string EditUrl(string alias, string path, int moduleid, string action, string parameters) { - string url = NavigateUrl(alias, path, ""); + string url = NavigateUrl(alias, path, "", Reload.None); if (url == "/") url = ""; if (moduleid != -1) { diff --git a/Oqtane.Client/Themes/ContainerBase.cs b/Oqtane.Client/Themes/ContainerBase.cs index 6a939f5b..d234002d 100644 --- a/Oqtane.Client/Themes/ContainerBase.cs +++ b/Oqtane.Client/Themes/ContainerBase.cs @@ -19,15 +19,31 @@ namespace Oqtane.Themes return NavigateUrl(PageState.Page.Path); } + public string NavigateUrl(Reload reload) + { + return NavigateUrl(PageState.Page.Path, reload); + } + public string NavigateUrl(string path) { - return NavigateUrl(path, ""); + return NavigateUrl(path, "", Reload.None); + } + + public string NavigateUrl(string path, Reload reload) + { + return NavigateUrl(path, "", reload); } public string NavigateUrl(string path, string parameters) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, Reload.None); } + + public string NavigateUrl(string path, string parameters, Reload reload) + { + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, reload); + } + public string EditUrl(string action, string parameters) { return EditUrl(ModuleState.ModuleId, action, parameters); diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index 3d95a44f..0ad52951 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -164,8 +164,7 @@ await PageModuleService.AddPageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); - PageState.Reload = Constants.ReloadPage; - NavigationManager.NavigateTo(NavigateUrl()); + NavigationManager.NavigateTo(NavigateUrl(Reload.Page)); } } @@ -204,8 +203,7 @@ PageState.EditMode = true; PageState.DesignMode = true; } - PageState.Reload = Constants.ReloadPage; - NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + PageState.EditMode.ToString().ToLower())); + NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + PageState.EditMode.ToString().ToLower(), Reload.Page)); } } } \ No newline at end of file diff --git a/Oqtane.Client/Themes/Controls/Login.razor b/Oqtane.Client/Themes/Controls/Login.razor index 83106017..14aa4f75 100644 --- a/Oqtane.Client/Themes/Controls/Login.razor +++ b/Oqtane.Client/Themes/Controls/Login.razor @@ -53,8 +53,7 @@ { // client-side Blazor authstateprovider.NotifyAuthenticationChanged(); - PageState.Reload = Constants.ReloadSite; - NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "logout")); + NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "logout", Reload.Site)); } } } diff --git a/Oqtane.Client/Themes/Controls/ModuleActions.razor b/Oqtane.Client/Themes/Controls/ModuleActions.razor index a1f3d7d1..eb8b698e 100644 --- a/Oqtane.Client/Themes/Controls/ModuleActions.razor +++ b/Oqtane.Client/Themes/Controls/ModuleActions.razor @@ -65,7 +65,7 @@ { PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); - string url = NavigateUrl(); + string url = NavigateUrl(Reload.Page); switch (action) { case "<<": @@ -104,7 +104,6 @@ await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pane); break; } - PageState.Reload = Constants.ReloadPage; NavigationManager.NavigateTo(url); } } diff --git a/Oqtane.Client/Themes/ThemeBase.cs b/Oqtane.Client/Themes/ThemeBase.cs index 2ad2956c..dd539a39 100644 --- a/Oqtane.Client/Themes/ThemeBase.cs +++ b/Oqtane.Client/Themes/ThemeBase.cs @@ -15,14 +15,29 @@ namespace Oqtane.Themes return NavigateUrl(PageState.Page.Path); } + public string NavigateUrl(Reload reload) + { + return NavigateUrl(PageState.Page.Path, reload); + } + public string NavigateUrl(string path) { - return NavigateUrl(path, ""); + return NavigateUrl(path, "", Reload.None); + } + + public string NavigateUrl(string path, Reload reload) + { + return NavigateUrl(path, "", reload); } public string NavigateUrl(string path, string parameters) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, Reload.None); + } + + public string NavigateUrl(string path, string parameters, Reload reload) + { + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, reload); } } diff --git a/Oqtane.Client/Themes/ThemeObjectBase.cs b/Oqtane.Client/Themes/ThemeObjectBase.cs index efa59f63..e5284b7f 100644 --- a/Oqtane.Client/Themes/ThemeObjectBase.cs +++ b/Oqtane.Client/Themes/ThemeObjectBase.cs @@ -13,14 +13,29 @@ namespace Oqtane.Themes return NavigateUrl(PageState.Page.Path); } + public string NavigateUrl(Reload reload) + { + return NavigateUrl(PageState.Page.Path, reload); + } + public string NavigateUrl(string path) { - return NavigateUrl(path, ""); + return NavigateUrl(path, "", Reload.None); + } + + public string NavigateUrl(string path, Reload reload) + { + return NavigateUrl(path, "", reload); } public string NavigateUrl(string path, string parameters) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, Reload.None); + } + + public string NavigateUrl(string path, string parameters, Reload reload) + { + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, reload); } public string EditUrl(int moduleid, string action) diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index 8cb7afe9..c8e409fb 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -2,6 +2,13 @@ using Microsoft.AspNetCore.Mvc; using Oqtane.Repository; using Oqtane.Models; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Hosting; +using Oqtane.Shared; +using System.IO; +using Microsoft.AspNetCore.Hosting; +using System.Reflection; +using System.IO.Compression; namespace Oqtane.Controllers { @@ -9,10 +16,14 @@ namespace Oqtane.Controllers public class ThemeController : Controller { private readonly IThemeRepository Themes; + private readonly IHostApplicationLifetime HostApplicationLifetime; + private readonly IWebHostEnvironment environment; - public ThemeController(IThemeRepository Themes) + public ThemeController(IThemeRepository Themes, IHostApplicationLifetime HostApplicationLifetime, IWebHostEnvironment environment) { this.Themes = Themes; + this.HostApplicationLifetime = HostApplicationLifetime; + this.environment = environment; } // GET: api/ @@ -21,5 +32,42 @@ namespace Oqtane.Controllers { return Themes.GetThemes(); } + + [HttpGet("install")] + [Authorize(Roles = Constants.HostRole)] + public void InstallThemes() + { + bool install = false; + string themefolder = Path.Combine(environment.WebRootPath, "Themes"); + string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + + // iterate through theme packages + foreach (string packagename in Directory.GetFiles(themefolder, "*.nupkg")) + { + // iterate through files and deploy to appropriate locations + using (ZipArchive archive = ZipFile.OpenRead(packagename)) + { + foreach (ZipArchiveEntry entry in archive.Entries) + { + string filename = Path.GetFileName(entry.FullName); + switch (Path.GetExtension(filename)) + { + case ".dll": + entry.ExtractToFile(Path.Combine(binfolder, filename)); + break; + } + } + } + // remove theme package + System.IO.File.Delete(packagename); + install = true; + } + + if (install) + { + // restart application + HostApplicationLifetime.StopApplication(); + } + } } } diff --git a/Oqtane.Server/Repository/ThemeRepository.cs b/Oqtane.Server/Repository/ThemeRepository.cs index 5cac43fc..b9547e93 100644 --- a/Oqtane.Server/Repository/ThemeRepository.cs +++ b/Oqtane.Server/Repository/ThemeRepository.cs @@ -24,7 +24,7 @@ namespace Oqtane.Repository // iterate through Oqtane theme assemblies Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Theme.")).ToArray(); - foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) + foreach (Assembly assembly in assemblies) { Themes = LoadThemesFromAssembly(Themes, assembly); } diff --git a/Oqtane.Server/Scripts/00.00.00.sql b/Oqtane.Server/Scripts/00.00.00.sql index 3c6c6b7f..153357f7 100644 --- a/Oqtane.Server/Scripts/00.00.00.sql +++ b/Oqtane.Server/Scripts/00.00.00.sql @@ -116,6 +116,7 @@ CREATE TABLE [dbo].[Role]( [Name] [nvarchar](256) NOT NULL, [Description] [nvarchar](50) NOT NULL, [IsAutoAssigned] [bit] NOT NULL, + [IsSystem] [bit] NOT NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, @@ -361,23 +362,23 @@ GO SET IDENTITY_INSERT [dbo].[Role] ON GO -INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (-1, null, N'All Users', N'All Users', 0, '', getdate(), '', getdate()) +INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [IsSystem], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) +VALUES (-1, null, N'All Users', N'All Users', 0, 1, '', getdate(), '', getdate()) GO -INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (0, null, N'Host Users', N'Host Users', 0, '', getdate(), '', getdate()) +INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [IsSystem], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) +VALUES (0, null, N'Host Users', N'Host Users', 0, 1, '', getdate(), '', getdate()) GO -INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (1, 1, N'Administrators', N'Site Administrators', 0, '', getdate(), '', getdate()) +INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [IsSystem], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) +VALUES (1, 1, N'Administrators', N'Site Administrators', 0, 1, '', getdate(), '', getdate()) GO -INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (2, 1, N'Registered Users', N'Registered Users', 1, '', getdate(), '', getdate()) +INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [IsSystem], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) +VALUES (2, 1, N'Registered Users', N'Registered Users', 1, 1, '', getdate(), '', getdate()) GO -INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (3, 2, N'Administrators', N'Site Administrators', 0, '', getdate(), '', getdate()) +INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [IsSystem], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) +VALUES (3, 2, N'Administrators', N'Site Administrators', 0, 1, '', getdate(), '', getdate()) GO -INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (4, 2, N'Registered Users', N'Registered Users', 1, '', getdate(), '', getdate()) +INSERT [dbo].[Role] ([RoleId], [SiteId], [Name], [Description], [IsAutoAssigned], [IsSystem], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) +VALUES (4, 2, N'Registered Users', N'Registered Users', 1, 1, '', getdate(), '', getdate()) GO SET IDENTITY_INSERT [dbo].[Role] OFF GO diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 9f7a77bf..8eac0f46 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -146,11 +146,11 @@ namespace Oqtane.Server // get list of loaded assemblies Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - - // iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process ) string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); DirectoryInfo folder = new DirectoryInfo(path); List moduleassemblies = new List(); + + // iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process ) foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll")) { // check if assembly is already loaded @@ -163,6 +163,18 @@ namespace Oqtane.Server } } + // iterate through Oqtane theme assemblies in /bin ( filter is narrow to optimize loading process ) + foreach (FileInfo file in folder.EnumerateFiles("*.Theme.*.dll")) + { + // check if assembly is already loaded + Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault(); + if (assembly == null) + { + // load assembly ( as long as dependencies are in /bin they will load as well ) + assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName); + } + } + services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson(); // register singleton scoped core services @@ -313,11 +325,11 @@ namespace Oqtane.Server // get list of loaded assemblies Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - - // iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process ) string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); DirectoryInfo folder = new DirectoryInfo(path); List moduleassemblies = new List(); + + // iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process ) foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll")) { // check if assembly is already loaded @@ -330,6 +342,18 @@ namespace Oqtane.Server } } + // iterate through Oqtane theme assemblies in /bin ( filter is narrow to optimize loading process ) + foreach (FileInfo file in folder.EnumerateFiles("*.Theme.*.dll")) + { + // check if assembly is already loaded + Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault(); + if (assembly == null) + { + // load assembly ( as long as dependencies are in /bin they will load as well ) + assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName); + } + } + services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson(); // register singleton scoped core services @@ -399,6 +423,7 @@ namespace Oqtane.Server } app.UseClientSideBlazorFiles(); + app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); diff --git a/Oqtane.Shared/Models/Alias.cs b/Oqtane.Shared/Models/Alias.cs index f22648b8..92442601 100644 --- a/Oqtane.Shared/Models/Alias.cs +++ b/Oqtane.Shared/Models/Alias.cs @@ -28,6 +28,20 @@ namespace Oqtane.Models } } + [NotMapped] + public string BaseUrl + { + get + { + string name = Name; + if (name.Contains("/")) + { + name = name.Substring(0, name.IndexOf("/")); + } + return Scheme + "://" + name; + } + } + [NotMapped] public string Path { @@ -58,7 +72,7 @@ namespace Oqtane.Models { get { - return Url + "/Tenants/" + TenantId.ToString() + "/"; + return BaseUrl + "/Tenants/" + TenantId.ToString() + "/"; } } @@ -76,7 +90,7 @@ namespace Oqtane.Models { get { - return Url + "/Tenants/" + TenantId.ToString() + "/Sites/" + SiteId.ToString() + "/"; + return BaseUrl + "/Tenants/" + TenantId.ToString() + "/Sites/" + SiteId.ToString() + "/"; } } } diff --git a/Oqtane.Shared/Models/Role.cs b/Oqtane.Shared/Models/Role.cs index 03da1f77..c9a316b5 100644 --- a/Oqtane.Shared/Models/Role.cs +++ b/Oqtane.Shared/Models/Role.cs @@ -9,6 +9,7 @@ namespace Oqtane.Models public string Name { get; set; } public string Description { get; set; } public bool IsAutoAssigned { get; set; } + public bool IsSystem { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; }