From e1a7954307fc104208ad80eec63288bec3d25dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ton=C4=87i=20Vatavuk?= Date: Sat, 29 Jan 2022 06:45:51 +0100 Subject: [PATCH 1/4] fix #1272 - add support for refs folder in package installation --- Oqtane.Server/Infrastructure/InstallationManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index 22a8176d..41860c5e 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -125,6 +125,9 @@ namespace Oqtane.Infrastructure case "ref": // ref/net*/... filename = ExtractFile(entry, Path.Combine(binPath, "ref"), 2); break; + case "refs": // refs/net*/... + filename = ExtractFile(entry, Path.Combine(binPath, "refs"), 2); + break; } if (filename != "") From c635351a1281c0a8a412e31465c7f042fc514e61 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sun, 6 Feb 2022 12:19:42 -0500 Subject: [PATCH 2/4] resolved UI error when closing Event Log and Visitor Management, made button class consistent in Recycle Bin, refactored RichTextEditor, made use of ConfigManager consistently throughout framework, added support for deleted Sites, removed reference to Runtime in Startup as it is now set per Site, added versioning to Html/Text, added Meta tag support to Page Management --- Oqtane.Client/App.razor | 4 - Oqtane.Client/Modules/Admin/Logs/Detail.razor | 14 +- Oqtane.Client/Modules/Admin/Pages/Add.razor | 412 +++++++++--------- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 315 ++++++------- .../Modules/Admin/RecycleBin/Index.razor | 4 +- .../Modules/Admin/Visitors/Detail.razor | 14 +- .../Modules/Controls/RichTextEditor.razor | 153 +++---- Oqtane.Client/Modules/HtmlText/Edit.razor | 245 +++++++---- .../HtmlText/Services/HtmlTextService.cs | 21 +- .../HtmlText/Services/IHtmlTextService.cs | 11 +- .../Resources/Modules/Admin/Pages/Add.resx | 6 + .../Resources/Modules/Admin/Pages/Edit.resx | 6 + .../Resources/Modules/HtmlText/Edit.resx | 42 ++ .../OqtaneServiceCollectionExtensions.cs | 15 +- .../Infrastructure/DatabaseManager.cs | 4 +- Oqtane.Server/Infrastructure/LogManager.cs | 16 +- .../Middleware/TenantMiddleware.cs | 5 +- ...rrer.cs => 03000105_AddVisitorReferrer.cs} | 0 .../Tenant/03000301_AddMetaToPage.cs | 29 ++ .../Controllers/HtmlTextController.cs | 86 ++-- .../HtmlText/Manager/HtmlTextManager.cs | 17 +- .../HtmlText/Repository/HtmlTextRepository.cs | 20 +- .../Repository/IHtmlTextRepository.cs | 7 +- Oqtane.Server/Pages/_Host.cshtml | 63 +-- Oqtane.Server/Pages/_Host.cshtml.cs | 81 ++-- .../Repository/Context/MasterDBContext.cs | 19 +- Oqtane.Server/Security/PrincipalValidator.cs | 5 +- Oqtane.Server/Startup.cs | 4 +- Oqtane.Shared/Models/Page.cs | 7 +- 29 files changed, 923 insertions(+), 702 deletions(-) rename Oqtane.Server/Migrations/Tenant/{03000104_AddVisitorReferrer.cs => 03000105_AddVisitorReferrer.cs} (100%) create mode 100644 Oqtane.Server/Migrations/Tenant/03000301_AddMetaToPage.cs diff --git a/Oqtane.Client/App.razor b/Oqtane.Client/App.razor index 92b73433..f8366e1c 100644 --- a/Oqtane.Client/App.razor +++ b/Oqtane.Client/App.razor @@ -62,10 +62,6 @@ { SiteState.Alias = _installation.Alias; } - else - { - _installation.Message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name"; - } _initialized = true; } diff --git a/Oqtane.Client/Modules/Admin/Logs/Detail.razor b/Oqtane.Client/Modules/Admin/Logs/Detail.razor index 356ac98a..7111572e 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Detail.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Detail.razor @@ -110,7 +110,7 @@ } -@SharedLocalizer["Cancel"] +@SharedLocalizer["Cancel"] @code { private bool _initialized = false; @@ -188,4 +188,16 @@ AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error); } } + + private string CloseUrl() + { + if (!PageState.QueryString.ContainsKey("level")) + { + return NavigateUrl(); + } + else + { + return NavigateUrl(PageState.Page.Path, "level=" + PageState.QueryString["level"] + "&function=" + PageState.QueryString["function"] + "&rows=" + PageState.QueryString["rows"] + "&page=" + PageState.QueryString["page"]); + } + } } diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 36d45b47..863db4f6 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -95,6 +95,12 @@ +
+ +
+ +
+
@@ -156,220 +162,222 @@ @code { - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; - private List _themeList; - private List _themes = new List(); - private List _containers = new List(); - private List _pageList; - private string _name; - private string _title; - private string _path = string.Empty; - private string _parentid = "-1"; - private string _insert = ">>"; - private List _children; - private int _childid = -1; - private string _isnavigation = "True"; - private string _isclickable = "True"; - private string _url; - private string _ispersonalizable = "False"; - private string _themetype = string.Empty; - private string _containertype = string.Empty; - private string _icon = string.Empty; - private string _permissions = string.Empty; - private PermissionGrid _permissionGrid; - private Type _themeSettingsType; - private object _themeSettings; - private RenderFragment ThemeSettingsComponent { get; set; } - private bool _refresh = false; - private ElementReference form; - private bool validated = false; + private List _themeList; + private List _themes = new List(); + private List _containers = new List(); + private List _pageList; + private string _name; + private string _title; + private string _meta; + private string _path = string.Empty; + private string _parentid = "-1"; + private string _insert = ">>"; + private List _children; + private int _childid = -1; + private string _isnavigation = "True"; + private string _isclickable = "True"; + private string _url; + private string _ispersonalizable = "False"; + private string _themetype = string.Empty; + private string _containertype = string.Empty; + private string _icon = string.Empty; + private string _permissions = string.Empty; + private PermissionGrid _permissionGrid; + private Type _themeSettingsType; + private object _themeSettings; + private RenderFragment ThemeSettingsComponent { get; set; } + private bool _refresh = false; + private ElementReference form; + private bool validated = false; - protected override async Task OnInitializedAsync() - { - try - { - _themeList = await ThemeService.GetThemesAsync(); - _themes = ThemeService.GetThemeControls(_themeList); - _themetype = PageState.Site.DefaultThemeType; - _containers = ThemeService.GetContainerControls(_themeList, _themetype); - _containertype = PageState.Site.DefaultContainerType; - _pageList = PageState.Pages; - _children = PageState.Pages.Where(item => item.ParentId == null).ToList(); - _permissions = string.Empty; - ThemeSettings(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Initializing Page {Error}", ex.Message); - AddModuleMessage(Localizer["Error.Page.Initialize"], MessageType.Error); - } - } + protected override async Task OnInitializedAsync() + { + try + { + _themeList = await ThemeService.GetThemesAsync(); + _themes = ThemeService.GetThemeControls(_themeList); + _themetype = PageState.Site.DefaultThemeType; + _containers = ThemeService.GetContainerControls(_themeList, _themetype); + _containertype = PageState.Site.DefaultContainerType; + _pageList = PageState.Pages; + _children = PageState.Pages.Where(item => item.ParentId == null).ToList(); + _permissions = string.Empty; + ThemeSettings(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Initializing Page {Error}", ex.Message); + AddModuleMessage(Localizer["Error.Page.Initialize"], MessageType.Error); + } + } - private async void ParentChanged(ChangeEventArgs e) - { - try - { - _parentid = (string)e.Value; - _children = new List(); - if (_parentid == "-1") - { - foreach (Page p in PageState.Pages.Where(item => item.ParentId == null)) - { - if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) - { - _children.Add(p); - } - } - } - else - { - foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid))) - { - if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) - { - _children.Add(p); - } - } - } - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message); - AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error); - } - } + private async void ParentChanged(ChangeEventArgs e) + { + try + { + _parentid = (string)e.Value; + _children = new List(); + if (_parentid == "-1") + { + foreach (Page p in PageState.Pages.Where(item => item.ParentId == null)) + { + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) + { + _children.Add(p); + } + } + } + else + { + foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid))) + { + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) + { + _children.Add(p); + } + } + } + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message); + AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error); + } + } - private async void ThemeChanged(ChangeEventArgs e) - { - try - { - _themetype = (string)e.Value; - _containers = ThemeService.GetContainerControls(_themeList, _themetype); - _containertype = "-"; - ThemeSettings(); - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); - AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error); - } - } + private async void ThemeChanged(ChangeEventArgs e) + { + try + { + _themetype = (string)e.Value; + _containers = ThemeService.GetContainerControls(_themeList, _themetype); + _containertype = "-"; + ThemeSettings(); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); + AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error); + } + } - private void ThemeSettings() - { - _themeSettingsType = null; - var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype))); - if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType)) - { - _themeSettingsType = Type.GetType(theme.ThemeSettingsType); - if (_themeSettingsType != null) - { - ThemeSettingsComponent = builder => - { - builder.OpenComponent(0, _themeSettingsType); - builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); }); - builder.CloseComponent(); - }; - } - _refresh = true; - } - } + private void ThemeSettings() + { + _themeSettingsType = null; + var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype))); + if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType)) + { + _themeSettingsType = Type.GetType(theme.ThemeSettingsType); + if (_themeSettingsType != null) + { + ThemeSettingsComponent = builder => + { + builder.OpenComponent(0, _themeSettingsType); + builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); }); + builder.CloseComponent(); + }; + } + _refresh = true; + } + } - private async Task SavePage() - { - validated = true; - var interop = new Interop(JSRuntime); - if (await interop.FormValid(form)) - { - Page page = null; - try - { - if (!string.IsNullOrEmpty(_themetype) && _containertype != "-") - { - page = new Page(); - page.SiteId = PageState.Page.SiteId; - page.Name = _name; - page.Title = _title; + private async Task SavePage() + { + validated = true; + var interop = new Interop(JSRuntime); + if (await interop.FormValid(form)) + { + Page page = null; + try + { + if (!string.IsNullOrEmpty(_themetype) && _containertype != "-") + { + page = new Page(); + page.SiteId = PageState.Page.SiteId; + page.Name = _name; + page.Title = _title; - if (string.IsNullOrEmpty(_path)) - { - _path = _name; - } - if (_path.Contains("/")) - { - _path = _path.Substring(_path.LastIndexOf("/") + 1); - } + if (string.IsNullOrEmpty(_path)) + { + _path = _name; + } + if (_path.Contains("/")) + { + _path = _path.Substring(_path.LastIndexOf("/") + 1); + } - if (_parentid == "-1") - { - page.ParentId = null; - page.Path = Utilities.GetFriendlyUrl(_path); - } - else - { - page.ParentId = Int32.Parse(_parentid); - var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault(); - if (parent.Path == string.Empty) - { - page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path); - } - else - { - page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); - } - } + if (_parentid == "-1") + { + page.ParentId = null; + page.Path = Utilities.GetFriendlyUrl(_path); + } + else + { + page.ParentId = Int32.Parse(_parentid); + var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault(); + if (parent.Path == string.Empty) + { + page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path); + } + else + { + page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); + } + } - if(PagePathIsDeleted(page.Path, page.SiteId, _pageList)) - { - AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning); - return; - } + if(PagePathIsDeleted(page.Path, page.SiteId, _pageList)) + { + AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning); + return; + } - if (!PagePathIsUnique(page.Path, page.SiteId, _pageList)) - { - AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning); - return; - } + if (!PagePathIsUnique(page.Path, page.SiteId, _pageList)) + { + AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning); + return; + } - Page child; - switch (_insert) - { - case "<<": - page.Order = 0; - break; - case "<": - child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault(); - page.Order = child.Order - 1; - break; - case ">": - child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault(); - page.Order = child.Order + 1; - break; - case ">>": - page.Order = int.MaxValue; - break; - } + Page child; + switch (_insert) + { + case "<<": + page.Order = 0; + break; + case "<": + child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault(); + page.Order = child.Order - 1; + break; + case ">": + child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault(); + page.Order = child.Order + 1; + break; + case ">>": + page.Order = int.MaxValue; + break; + } - page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation)); - page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable)); - page.Url = _url; - page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; - if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType) - { - page.ThemeType = string.Empty; - } - page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty; - if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType) - { - page.DefaultContainerType = string.Empty; - } - page.Icon = (_icon == null ? string.Empty : _icon); - page.Permissions = _permissionGrid.GetPermissions(); - page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable)); - page.UserId = null; + page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation)); + page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable)); + page.Url = _url; + page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; + if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType) + { + page.ThemeType = string.Empty; + } + page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty; + if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType) + { + page.DefaultContainerType = string.Empty; + } + page.Icon = (_icon == null ? string.Empty : _icon); + page.Permissions = _permissionGrid.GetPermissions(); + page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable)); + page.UserId = null; + page.Meta = _meta; page = await PageService.AddPageAsync(page); await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId); diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index d2edd289..11533da8 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -102,6 +102,12 @@
+
+ +
+ +
+
@@ -200,6 +206,7 @@ private int _pageId; private string _name; private string _title; + private string _meta; private string _path; private string _currentparentid; private string _parentid = "-1"; @@ -243,6 +250,7 @@ { _name = page.Name; _title = page.Title; + _meta = page.Meta; _path = page.Path; _pageModules = PageState.Modules.Where(m => m.PageId == page.PageId && m.IsDeleted == false).ToList(); @@ -313,77 +321,77 @@ _pageModules.RemoveAll(item => item.PageModuleId == pagemodule.PageModuleId); StateHasChanged(); NavigationManager.NavigateTo(NavigationManager.Uri + "&tab=PageModules"); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Deleting Module {Title} {Error}", module.Title, ex.Message); - AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error); - } - } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting Module {Title} {Error}", module.Title, ex.Message); + AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error); + } + } - private async void ParentChanged(ChangeEventArgs e) - { - try - { - _parentid = (string)e.Value; - _children = new List(); - if (_parentid == "-1") - { - foreach (Page p in PageState.Pages.Where(item => item.ParentId == null)) - { - if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) - { - _children.Add(p); - } - } - } - else - { - foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid))) - { - if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) - { - _children.Add(p); - } - } - } - if (_parentid == _currentparentid) - { - _insert = "="; - } - else - { - _insert = ">>"; - } - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message); - AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error); - } - } + private async void ParentChanged(ChangeEventArgs e) + { + try + { + _parentid = (string)e.Value; + _children = new List(); + if (_parentid == "-1") + { + foreach (Page p in PageState.Pages.Where(item => item.ParentId == null)) + { + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) + { + _children.Add(p); + } + } + } + else + { + foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid))) + { + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) + { + _children.Add(p); + } + } + } + if (_parentid == _currentparentid) + { + _insert = "="; + } + else + { + _insert = ">>"; + } + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message); + AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error); + } + } - private async void ThemeChanged(ChangeEventArgs e) - { - try - { - _themetype = (string)e.Value; - _containers = ThemeService.GetContainerControls(_themeList, _themetype); - _containertype = "-"; - ThemeSettings(); - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); - AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error); - } - } + private async void ThemeChanged(ChangeEventArgs e) + { + try + { + _themetype = (string)e.Value; + _containers = ThemeService.GetContainerControls(_themeList, _themetype); + _containertype = "-"; + ThemeSettings(); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); + AddModuleMessage(Localizer["Error.Pane.Load"], MessageType.Error); + } + } - private void ThemeSettings() - { - _themeSettingsType = null; + private void ThemeSettings() + { + _themeSettingsType = null; if (PageState.QueryString.ContainsKey("cp")) // can only be displayed if invoked from Control Panel { var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype))); @@ -402,97 +410,98 @@ _refresh = true; } } - } + } - private async Task SavePage() - { - validated = true; - var interop = new Interop(JSRuntime); - if (await interop.FormValid(form)) - { - Page page = null; - try - { - if (!string.IsNullOrEmpty(_themetype) && _containertype != "-") - { - page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); - string currentPath = page.Path; + private async Task SavePage() + { + validated = true; + var interop = new Interop(JSRuntime); + if (await interop.FormValid(form)) + { + Page page = null; + try + { + if (!string.IsNullOrEmpty(_themetype) && _containertype != "-") + { + page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); + string currentPath = page.Path; - page.Name = _name; - page.Title = _title; + page.Name = _name; + page.Title = _title; - if (string.IsNullOrEmpty(_path)) - { - _path = _name; - } - if (_path.Contains("/")) - { - _path = _path.Substring(_path.LastIndexOf("/") + 1); - } + if (string.IsNullOrEmpty(_path)) + { + _path = _name; + } + if (_path.Contains("/")) + { + _path = _path.Substring(_path.LastIndexOf("/") + 1); + } - if (_parentid == "-1") - { - page.ParentId = null; - page.Path = Utilities.GetFriendlyUrl(_path); - } - else - { - page.ParentId = Int32.Parse(_parentid); - Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId); - if (parent.Path == string.Empty) - { - page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path); - } - else - { - page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); - } - } + if (_parentid == "-1") + { + page.ParentId = null; + page.Path = Utilities.GetFriendlyUrl(_path); + } + else + { + page.ParentId = Int32.Parse(_parentid); + Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId); + if (parent.Path == string.Empty) + { + page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path); + } + else + { + page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); + } + } - if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList)) - { - AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning); - return; - } + if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList)) + { + AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning); + return; + } - if (_insert != "=") - { - Page child; - switch (_insert) - { - case "<<": - page.Order = 0; - break; - case "<": - child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid); - if (child != null) page.Order = child.Order - 1; - break; - case ">": - child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid); - if (child != null) page.Order = child.Order + 1; - break; - case ">>": - page.Order = int.MaxValue; - break; - } - } - page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation)); - page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable)); - page.Url = _url; - page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; - if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType) - { - page.ThemeType = string.Empty; - } - page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty; - if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType) - { - page.DefaultContainerType = string.Empty; - } - page.Icon = _icon ?? string.Empty; - page.Permissions = _permissionGrid.GetPermissions(); - page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable)); - page.UserId = null; + if (_insert != "=") + { + Page child; + switch (_insert) + { + case "<<": + page.Order = 0; + break; + case "<": + child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid); + if (child != null) page.Order = child.Order - 1; + break; + case ">": + child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid); + if (child != null) page.Order = child.Order + 1; + break; + case ">>": + page.Order = int.MaxValue; + break; + } + } + page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation)); + page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable)); + page.Url = _url; + page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; + if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType) + { + page.ThemeType = string.Empty; + } + page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty; + if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType) + { + page.DefaultContainerType = string.Empty; + } + page.Icon = _icon ?? string.Empty; + page.Permissions = _permissionGrid.GetPermissions(); + page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable)); + page.UserId = null; + page.Meta = _meta; page = await PageService.UpdatePageAsync(page); await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId); diff --git a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor index 75273207..3e93cf7c 100644 --- a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor +++ b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor @@ -25,7 +25,7 @@ @Localizer["DeletedOn"] - + @context.Name @context.DeletedBy @@ -56,7 +56,7 @@ @Localizer["DeletedOn"] - + @PageState.Pages.Find(item => item.PageId == context.PageId).Name @context.Title diff --git a/Oqtane.Client/Modules/Admin/Visitors/Detail.razor b/Oqtane.Client/Modules/Admin/Visitors/Detail.razor index cff56939..3078eb1b 100644 --- a/Oqtane.Client/Modules/Admin/Visitors/Detail.razor +++ b/Oqtane.Client/Modules/Admin/Visitors/Detail.razor @@ -70,7 +70,7 @@
} -@SharedLocalizer["Cancel"] +@SharedLocalizer["Cancel"] @code { private bool _initialized = false; @@ -125,4 +125,16 @@ AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error); } } + + private string CloseUrl() + { + if (!PageState.QueryString.ContainsKey("type")) + { + return NavigateUrl(); + } + else + { + return NavigateUrl(PageState.Page.Path, "type=" + PageState.QueryString["type"] + "&days=" + PageState.QueryString["days"] + "&page=" + PageState.QueryString["page"]); + } + } } diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index 5eee80aa..6d2012c3 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -71,11 +71,11 @@
@if (ReadOnly) { - + } else { - + } @@ -83,109 +83,103 @@ @code { - private ElementReference _editorElement; - private ElementReference _toolBar; - private bool _filemanagervisible = false; - private FileManager _fileManager; - private string _content = string.Empty; - private string _original = string.Empty; - private string _message = string.Empty; + private ElementReference _editorElement; + private ElementReference _toolBar; + private bool _filemanagervisible = false; + private FileManager _fileManager; + private string _rawhtml = string.Empty; + private string _richhtml = string.Empty; + private string _original = string.Empty; + private string _message = string.Empty; - [Parameter] - public string Content { get; set; } + [Parameter] + public string Content { get; set; } - [Parameter] - public bool ReadOnly { get; set; } = false; + [Parameter] + public bool ReadOnly { get; set; } = false; - [Parameter] - public string Placeholder { get; set; } = "Enter Your Content..."; + [Parameter] + public string Placeholder { get; set; } = "Enter Your Content..."; - // parameters only applicable to rich text editor - [Parameter] - public RenderFragment ToolbarContent { get; set; } + // parameters only applicable to rich text editor + [Parameter] + public RenderFragment ToolbarContent { get; set; } - [Parameter] - public string Theme { get; set; } = "snow"; + [Parameter] + public string Theme { get; set; } = "snow"; - [Parameter] - public string DebugLevel { get; set; } = "info"; + [Parameter] + public string DebugLevel { get; set; } = "info"; - [Parameter] - public bool AllowFileManagement { get; set; } = true; + [Parameter] + public bool AllowFileManagement { get; set; } = true; - public override List Resources => new List() + public override List Resources => new List() { new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" }, new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" }, new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" } }; - protected override async Task OnParametersSetAsync() - { - _content = Content; // raw HTML - await RefreshRichText(); - } + protected override void OnParametersSet() + { + _rawhtml = Content; + _richhtml = Content; + _original = _rawhtml; // preserve original content for comparison + } - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - var interop = new RichTextEditorInterop(JSRuntime); + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); - await base.OnAfterRenderAsync(firstRender); + var interop = new RichTextEditorInterop(JSRuntime); - await interop.CreateEditor( - _editorElement, - _toolBar, - ReadOnly, - Placeholder, - Theme, - DebugLevel); + if (firstRender) + { + await interop.CreateEditor( + _editorElement, + _toolBar, + ReadOnly, + Placeholder, + Theme, + DebugLevel); + } - await interop.LoadEditorContent(_editorElement, Content); + await interop.LoadEditorContent(_editorElement, _richhtml); + } - _content = Content; // raw HTML + public void CloseFileManager() + { + _filemanagervisible = false; + _message = string.Empty; + StateHasChanged(); + } - // preserve a copy of the rich text content ( Quill sanitizes content so we need to retrieve it from the editor ) - _original = await interop.GetHtml(_editorElement); - } - } - - public void CloseFileManager() - { - _filemanagervisible = false; - _message = string.Empty; + public void RefreshRichText() + { + _richhtml = _rawhtml; StateHasChanged(); } - public async Task RefreshRichText() - { - var interop = new RichTextEditorInterop(JSRuntime); - await interop.LoadEditorContent(_editorElement, _content); - } - public async Task RefreshRawHtml() { var interop = new RichTextEditorInterop(JSRuntime); - _content = await interop.GetHtml(_editorElement); + _rawhtml = await interop.GetHtml(_editorElement); StateHasChanged(); } public async Task GetHtml() { - // get rich text content - var interop = new RichTextEditorInterop(JSRuntime); - string content = await interop.GetHtml(_editorElement); - - if (_original != content) + // return raw content if it has changed + if (_original != _rawhtml) { - // rich text content has changed - return it - return content; + return _rawhtml; } else { - // return raw html content - return _content; + // otherwise return rich text content + var interop = new RichTextEditorInterop(JSRuntime); + return await interop.GetHtml(_editorElement); } } @@ -212,23 +206,4 @@ } StateHasChanged(); } - - // other rich text editor methods which can be used by developers - public async Task GetText() - { - var interop = new RichTextEditorInterop(JSRuntime); - return await interop.GetText(_editorElement); - } - - public async Task GetContent() - { - var interop = new RichTextEditorInterop(JSRuntime); - return await interop.GetContent(_editorElement); - } - - public async Task EnableEditor(bool mode) - { - var interop = new RichTextEditorInterop(JSRuntime); - await interop.EnableEditor(_editorElement, mode); - } } diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index 528cafef..ca86ea95 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -9,95 +9,186 @@ @inject IStringLocalizer Localizer @inject IStringLocalizer SharedLocalizer -@if (_content != null) -{ - - - @SharedLocalizer["Cancel"] - @if (!string.IsNullOrEmpty(_content)) - { -
-
- - } -} + + + @if (_content != null) + { + +
+ + @SharedLocalizer["Cancel"] + @if (!string.IsNullOrEmpty(_content)) + { +
+
+ + } + } +
+ + +
+   +   +   + @SharedLocalizer["CreatedOn"] + @SharedLocalizer["CreatedBy"] +
+ + + + + @context.CreatedOn + @context.CreatedBy + +
+ @((MarkupString)_view) +
+
@code { - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; - public override string Title => "Edit Html/Text"; + public override string Title => "Edit Html/Text"; - public override List Resources => new List() -{ + public override List Resources => new List() + { new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" } }; - private RichTextEditor RichTextEditorHtml; - private bool _allowfilemanagement; - private string _content = null; - private string _createdby; - private DateTime _createdon; - private string _modifiedby; - private DateTime _modifiedon; + private RichTextEditor RichTextEditorHtml; + private bool _allowfilemanagement; + private string _content = null; + private string _createdby; + private DateTime _createdon; + private string _modifiedby; + private DateTime _modifiedon; + private List _htmltexts; + private string _view = ""; - protected override async Task OnInitializedAsync() - { - try - { - _allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true")); + protected override async Task OnInitializedAsync() + { + try + { + _allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true")); + await LoadContent(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Content {Error}", ex.Message); + AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error); + } + } - var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId); - if (htmltext != null) - { - _content = htmltext.Content; - _content = Utilities.FormatContent(_content, PageState.Alias, "render"); - _createdby = htmltext.CreatedBy; - _createdon = htmltext.CreatedOn; - _modifiedby = htmltext.ModifiedBy; - _modifiedon = htmltext.ModifiedOn; - } - else - { - _content = string.Empty; - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Content {Error}", ex.Message); - AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error); - } - } + private async Task LoadContent() + { + var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId); + if (htmltext != null) + { + _content = htmltext.Content; + _content = Utilities.FormatContent(_content, PageState.Alias, "render"); + _createdby = htmltext.CreatedBy; + _createdon = htmltext.CreatedOn; + _modifiedby = htmltext.ModifiedBy; + _modifiedon = htmltext.ModifiedOn; + } + else + { + _content = string.Empty; + } - private async Task SaveContent() - { - string content = await RichTextEditorHtml.GetHtml(); - content = Utilities.FormatContent(content, PageState.Alias, "save"); + _htmltexts = await HtmlTextService.GetHtmlTextsAsync(ModuleState.ModuleId); + _htmltexts = _htmltexts.OrderByDescending(item => item.CreatedOn).ToList(); - try - { - var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId); - if (htmltext != null) - { - htmltext.Content = content; - await HtmlTextService.UpdateHtmlTextAsync(htmltext); - } - else - { - htmltext = new HtmlText(); - htmltext.ModuleId = ModuleState.ModuleId; - htmltext.Content = content; - await HtmlTextService.AddHtmlTextAsync(htmltext); - } + _view = ""; + } - await logger.LogInformation("Content Saved {HtmlText}", htmltext); - NavigationManager.NavigateTo(NavigateUrl()); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Saving Content {Error}", ex.Message); - AddModuleMessage(Localizer["Error.Content.Save"], MessageType.Error); - } - } + private async Task SaveContent() + { + string content = await RichTextEditorHtml.GetHtml(); + content = Utilities.FormatContent(content, PageState.Alias, "save"); + + try + { + var htmltext = new HtmlText(); + htmltext.ModuleId = ModuleState.ModuleId; + htmltext.Content = content; + await HtmlTextService.AddHtmlTextAsync(htmltext); + + await logger.LogInformation("Content Saved {HtmlText}", htmltext); + NavigationManager.NavigateTo(NavigateUrl()); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Saving Content {Error}", ex.Message); + AddModuleMessage(Localizer["Error.Content.Save"], MessageType.Error); + } + } + + private async Task View(Models.HtmlText htmltext) + { + try + { + htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, htmltext.ModuleId); + if (htmltext != null) + { + _view = htmltext.Content; + _view = Utilities.FormatContent(_view, PageState.Alias, "render"); + StateHasChanged(); + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Viewing Content {Error}", ex.Message); + AddModuleMessage(Localizer["Error.Content.View"], MessageType.Error); + } + } + + private async Task Restore(Models.HtmlText htmltext) + { + try + { + htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, ModuleState.ModuleId); + if (htmltext != null) + { + var content = htmltext.Content; + htmltext = new HtmlText(); + htmltext.ModuleId = ModuleState.ModuleId; + htmltext.Content = content; + await HtmlTextService.AddHtmlTextAsync(htmltext); + await logger.LogInformation("Content Restored {HtmlText}", htmltext); + AddModuleMessage(Localizer["Message.Content.Restored"], MessageType.Success); + await LoadContent(); + StateHasChanged(); + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Restoring Content {Error}", ex.Message); + AddModuleMessage(Localizer["Error.Content.Restore"], MessageType.Error); + } + } + + private async Task Delete(Models.HtmlText htmltext) + { + try + { + htmltext = await HtmlTextService.GetHtmlTextAsync(htmltext.HtmlTextId, ModuleState.ModuleId); + if (htmltext != null) + { + await HtmlTextService.DeleteHtmlTextAsync(htmltext.HtmlTextId, htmltext.ModuleId); + await logger.LogInformation("Content Deleted {HtmlText}", htmltext); + AddModuleMessage(Localizer["Message.Content.Deleted"], MessageType.Success); + await LoadContent(); + StateHasChanged(); + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting Content {Error}", ex.Message); + AddModuleMessage(Localizer["Error.Content.Delete"], MessageType.Error); + } + } } diff --git a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs index 85583f7e..e4dd0174 100644 --- a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs +++ b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Oqtane.Documentation; @@ -13,24 +15,29 @@ namespace Oqtane.Modules.HtmlText.Services private string ApiUrl => CreateApiUrl("HtmlText"); + public async Task> GetHtmlTextsAsync(int moduleId) + { + return await GetJsonAsync>(CreateAuthorizationPolicyUrl($"{ApiUrl}?moduleid={moduleId}", EntityNames.Module, moduleId)); + } + public async Task GetHtmlTextAsync(int moduleId) { return await GetJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId)); } + public async Task GetHtmlTextAsync(int htmlTextId, int moduleId) + { + return await GetJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlTextId}/{moduleId}", EntityNames.Module, moduleId)); + } + public async Task AddHtmlTextAsync(Models.HtmlText htmlText) { await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", EntityNames.Module, htmlText.ModuleId), htmlText); } - public async Task UpdateHtmlTextAsync(Models.HtmlText htmlText) + public async Task DeleteHtmlTextAsync(int htmlTextId, int moduleId) { - await PutJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlText.HtmlTextId}", EntityNames.Module, htmlText.ModuleId), htmlText); - } - - public async Task DeleteHtmlTextAsync(int moduleId) - { - await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", EntityNames.Module, moduleId)); + await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlTextId}/{moduleId}", EntityNames.Module, moduleId)); } } } diff --git a/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs b/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs index 80d725a9..7ca069c6 100644 --- a/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs +++ b/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs @@ -1,19 +1,20 @@ using System.Collections.Generic; using System.Threading.Tasks; using Oqtane.Documentation; -using Oqtane.Modules.HtmlText.Models; namespace Oqtane.Modules.HtmlText.Services { [PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")] public interface IHtmlTextService { - Task GetHtmlTextAsync(int ModuleId); + Task> GetHtmlTextsAsync(int moduleId); + + Task GetHtmlTextAsync(int moduleId); + + Task GetHtmlTextAsync(int htmlTextId, int moduleId); Task AddHtmlTextAsync(Models.HtmlText htmltext); - Task UpdateHtmlTextAsync(Models.HtmlText htmltext); - - Task DeleteHtmlTextAsync(int ModuleId); + Task DeleteHtmlTextAsync(int htmlTextId, int moduleId); } } diff --git a/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx b/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx index b29a4f1e..71c3f04f 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx @@ -231,4 +231,10 @@ A page with path {0} already exists for the selected parent page in the Recycle Bin. Either recover the page or remove from the Recycle Bin and create it again. + + Optionally enter meta tags (in exactly the form you want them to be included in the page output). + + + Meta: + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx b/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx index ad6dfd25..5dc82a55 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx @@ -264,4 +264,10 @@ Clickable? + + Optionally enter meta tags (in exactly the form you want them to be included in the page output). + + + Meta: + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/HtmlText/Edit.resx b/Oqtane.Client/Resources/Modules/HtmlText/Edit.resx index e2eed7a2..5e1d2160 100644 --- a/Oqtane.Client/Resources/Modules/HtmlText/Edit.resx +++ b/Oqtane.Client/Resources/Modules/HtmlText/Edit.resx @@ -117,10 +117,52 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Are You Sure You Wish To Delete The {0} Version? + + + Are You Sure You Wish To Restore The {0} Version? + + + Created By + + + Created On + + + Delete Version + + + Delete + + + Error Deleting Version + An Error Occurred Loading Content + + Error Restoring Version + An Error Occurred Saving Content + + Error Viewing Version + + + Version Deleted + + + Version Restored + + + Restore Version + + + Restore + + + View + \ No newline at end of file diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index cd5bb63a..d8693bd2 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -17,18 +17,17 @@ using Oqtane.Infrastructure; using Oqtane.Modules; using Oqtane.Repository; using Oqtane.Security; -using Oqtane.Services; using Oqtane.Shared; namespace Microsoft.Extensions.DependencyInjection { public static class OqtaneServiceCollectionExtensions { - public static IServiceCollection AddOqtane(this IServiceCollection services, Runtime runtime, string[] supportedCultures) + public static IServiceCollection AddOqtane(this IServiceCollection services, string[] supportedCultures) { LoadAssemblies(); LoadSatelliteAssemblies(supportedCultures); - services.AddOqtaneServices(runtime); + services.AddOqtaneServices(); return services; } @@ -190,7 +189,7 @@ namespace Microsoft.Extensions.DependencyInjection return services; } - private static IServiceCollection AddOqtaneServices(this IServiceCollection services, Runtime runtime) + private static IServiceCollection AddOqtaneServices(this IServiceCollection services) { if (services is null) { @@ -229,14 +228,6 @@ namespace Microsoft.Extensions.DependencyInjection { startup.ConfigureServices(services); } - - if (runtime == Runtime.Server) - { - // register client startup services if running on server - assembly.GetInstances() - .ToList() - .ForEach(x => x.ConfigureServices(services)); - } } return services; } diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index cad82ac9..df58e73a 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -28,14 +28,14 @@ namespace Oqtane.Infrastructure { public class DatabaseManager : IDatabaseManager { - private readonly IConfigurationRoot _config; + private readonly IConfigManager _config; private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IWebHostEnvironment _environment; private readonly IMemoryCache _cache; private readonly IConfigManager _configManager; private readonly ILogger _filelogger; - public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment, IMemoryCache cache, IConfigManager configManager, ILogger filelogger) + public DatabaseManager(IConfigManager config, IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment, IMemoryCache cache, IConfigManager configManager, ILogger filelogger) { _config = config; _serviceScopeFactory = serviceScopeFactory; diff --git a/Oqtane.Server/Infrastructure/LogManager.cs b/Oqtane.Server/Infrastructure/LogManager.cs index afbb1de8..e8f58854 100644 --- a/Oqtane.Server/Infrastructure/LogManager.cs +++ b/Oqtane.Server/Infrastructure/LogManager.cs @@ -14,18 +14,18 @@ namespace Oqtane.Infrastructure public class LogManager : ILogManager { private readonly ILogRepository _logs; - private readonly IConfigurationRoot _config; + private readonly ITenantManager _tenantManager; + private readonly IConfigManager _config; private readonly IUserPermissions _userPermissions; private readonly IHttpContextAccessor _accessor; - private readonly Alias _alias; - public LogManager(ILogRepository logs, ITenantManager tenantManager, IConfigurationRoot config, IUserPermissions userPermissions, IHttpContextAccessor accessor) + public LogManager(ILogRepository logs, ITenantManager tenantManager, IConfigManager config, IUserPermissions userPermissions, IHttpContextAccessor accessor) { _logs = logs; + _tenantManager = tenantManager; _config = config; _userPermissions = userPermissions; _accessor = accessor; - _alias = tenantManager.GetAlias(); } public void Log(LogLevel level, object @class, LogFunction function, string message, params object[] args) @@ -48,9 +48,13 @@ namespace Oqtane.Infrastructure Log log = new Log(); log.SiteId = siteId; - if (log.SiteId == -1 && _alias != null) + if (log.SiteId == -1) { - log.SiteId = _alias.SiteId; + var alias = _tenantManager.GetAlias(); + if (alias != null) + { + log.SiteId = alias.SiteId; + } } if (log.SiteId == -1) return; // logs must be site specific diff --git a/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs b/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs index 189a44c9..9a748d96 100644 --- a/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs +++ b/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; namespace Oqtane.Infrastructure { @@ -16,8 +15,8 @@ namespace Oqtane.Infrastructure public async Task Invoke(HttpContext context) { // check if framework is installed - var config = context.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration; - if (!string.IsNullOrEmpty(config.GetConnectionString("DefaultConnection"))) + var config = context.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager; + if (config.IsInstalled()) { // get alias var tenantManager = context.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager; diff --git a/Oqtane.Server/Migrations/Tenant/03000104_AddVisitorReferrer.cs b/Oqtane.Server/Migrations/Tenant/03000105_AddVisitorReferrer.cs similarity index 100% rename from Oqtane.Server/Migrations/Tenant/03000104_AddVisitorReferrer.cs rename to Oqtane.Server/Migrations/Tenant/03000105_AddVisitorReferrer.cs diff --git a/Oqtane.Server/Migrations/Tenant/03000301_AddMetaToPage.cs b/Oqtane.Server/Migrations/Tenant/03000301_AddMetaToPage.cs new file mode 100644 index 00000000..9dea0d49 --- /dev/null +++ b/Oqtane.Server/Migrations/Tenant/03000301_AddMetaToPage.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Databases.Interfaces; +using Oqtane.Migrations.EntityBuilders; +using Oqtane.Repository; + +namespace Oqtane.Migrations.Tenant +{ + [DbContext(typeof(TenantDBContext))] + [Migration("Tenant.03.00.03.01")] + public class AddPageMeta : MultiDatabaseMigration + { + public AddPageMeta(IDatabase database) : base(database) + { + } + + protected override void Up(MigrationBuilder migrationBuilder) + { + var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase); + pageEntityBuilder.AddStringColumn("Meta", 2000, true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase); + pageEntityBuilder.DropColumn("Meta"); + } + } +} diff --git a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs index 020d6e48..f269650b 100644 --- a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs +++ b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs @@ -8,6 +8,8 @@ using Oqtane.Infrastructure; using Oqtane.Controllers; using System.Net; using Oqtane.Documentation; +using System.Collections.Generic; +using System.Linq; namespace Oqtane.Modules.HtmlText.Controllers { @@ -22,18 +24,60 @@ namespace Oqtane.Modules.HtmlText.Controllers _htmlText = htmlText; } - // GET api//5 - [HttpGet("{id}")] - [Authorize(Policy = PolicyNames.ViewModule)] - public Models.HtmlText Get(int id) + // GET: api/?moduleid=x + [HttpGet] + [Authorize(Roles = RoleNames.Registered)] + public IEnumerable Get(string moduleId) { - if (AuthEntityId(EntityNames.Module) == id) + if (int.TryParse(moduleId, out int ModuleId) && AuthEntityId(EntityNames.Module) == ModuleId) + { + return _htmlText.GetHtmlTexts(ModuleId); + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Html/Text Get Attempt {ModuleId}", moduleId); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return null; + } + } + + // GET api//5 + [HttpGet("{moduleid}")] + [Authorize(Policy = PolicyNames.ViewModule)] + public Models.HtmlText Get(int moduleId) + { + if (AuthEntityId(EntityNames.Module) == moduleId) + { + var htmltexts = _htmlText.GetHtmlTexts(moduleId); + if (htmltexts != null && htmltexts.Any()) + { + return htmltexts.OrderByDescending(item => item.CreatedOn).First(); + } + else + { + return null; + } + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Html/Text Get Attempt {ModuleId}", moduleId); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return null; + } + } + + // GET api//5/6 + [HttpGet("{id}/{moduleid}")] + [Authorize(Policy = PolicyNames.ViewModule)] + public Models.HtmlText Get(int id, int moduleId) + { + if (AuthEntityId(EntityNames.Module) == moduleId) { return _htmlText.GetHtmlText(id); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Get Attempt {ModuleId}", id); + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Html/Text Get Attempt {HtmlTextId}", id); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return null; } @@ -53,27 +97,7 @@ namespace Oqtane.Modules.HtmlText.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Post Attempt {HtmlText}", htmlText); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; - return null; - } - } - - // PUT api//5 - [ValidateAntiForgeryToken] - [HttpPut("{id}")] - [Authorize(Policy = PolicyNames.EditModule)] - public Models.HtmlText Put(int id, [FromBody] Models.HtmlText htmlText) - { - if (ModelState.IsValid && AuthEntityId(EntityNames.Module) == htmlText.ModuleId) - { - htmlText = _htmlText.UpdateHtmlText(htmlText); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Html/Text Updated {HtmlText}", htmlText); - return htmlText; - } - else - { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Put Attempt {HtmlText}", htmlText); + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Html/Text Post Attempt {HtmlText}", htmlText); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return null; } @@ -81,18 +105,18 @@ namespace Oqtane.Modules.HtmlText.Controllers // DELETE api//5 [ValidateAntiForgeryToken] - [HttpDelete("{id}")] + [HttpDelete("{id}/{moduleid}")] [Authorize(Policy = PolicyNames.EditModule)] - public void Delete(int id) + public void Delete(int id, int moduleId) { - if (AuthEntityId(EntityNames.Module) == id) + if (AuthEntityId(EntityNames.Module) == moduleId) { _htmlText.DeleteHtmlText(id); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Html/Text Deleted {HtmlTextId}", id); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Delete Attempt {ModuleId}", id); + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Html/Text Delete Attempt {HtmlTextId}", id); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } } diff --git a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs index f9a862e8..6179c40c 100644 --- a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs +++ b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs @@ -43,19 +43,10 @@ namespace Oqtane.Modules.HtmlText.Manager public void ImportModule(Module module, string content, string version) { content = WebUtility.HtmlDecode(content); - var htmlText = _htmlText.GetHtmlText(module.ModuleId); - if (htmlText != null) - { - htmlText.Content = content; - _htmlText.UpdateHtmlText(htmlText); - } - else - { - htmlText = new Models.HtmlText(); - htmlText.ModuleId = module.ModuleId; - htmlText.Content = content; - _htmlText.AddHtmlText(htmlText); - } + var htmlText = new Models.HtmlText(); + htmlText.ModuleId = module.ModuleId; + htmlText.Content = content; + _htmlText.AddHtmlText(htmlText); } public bool Install(Tenant tenant, string version) diff --git a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs index a2229f0e..6f6d8e6d 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using System.Linq; using Oqtane.Modules.HtmlText.Models; using Oqtane.Documentation; +using System.Collections.Generic; namespace Oqtane.Modules.HtmlText.Repository { @@ -15,11 +16,15 @@ namespace Oqtane.Modules.HtmlText.Repository _db = context; } - public Models.HtmlText GetHtmlText(int moduleId) + public IEnumerable GetHtmlTexts(int moduleId) { - return _db.HtmlText.FirstOrDefault(item => item.ModuleId == moduleId); + return _db.HtmlText.Where(item => item.ModuleId == moduleId); } + public Models.HtmlText GetHtmlText(int htmlTextId) + { + return _db.HtmlText.Find(htmlTextId); + } public Models.HtmlText AddHtmlText(Models.HtmlText htmlText) { @@ -28,16 +33,9 @@ namespace Oqtane.Modules.HtmlText.Repository return htmlText; } - public Models.HtmlText UpdateHtmlText(Models.HtmlText htmlText) + public void DeleteHtmlText(int htmlTextId) { - _db.Entry(htmlText).State = EntityState.Modified; - _db.SaveChanges(); - return htmlText; - } - - public void DeleteHtmlText(int moduleId) - { - Models.HtmlText htmlText = _db.HtmlText.FirstOrDefault(item => item.ModuleId == moduleId); + Models.HtmlText htmlText = _db.HtmlText.FirstOrDefault(item => item.HtmlTextId == htmlTextId); if (htmlText != null) _db.HtmlText.Remove(htmlText); _db.SaveChanges(); } diff --git a/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs b/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs index 014c5adc..44be5973 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Oqtane.Documentation; using Oqtane.Modules.HtmlText.Models; @@ -6,9 +7,9 @@ namespace Oqtane.Modules.HtmlText.Repository [PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")] public interface IHtmlTextRepository { - Models.HtmlText GetHtmlText(int moduleId); + IEnumerable GetHtmlTexts(int moduleId); + Models.HtmlText GetHtmlText(int htmlTextId); Models.HtmlText AddHtmlText(Models.HtmlText htmlText); - Models.HtmlText UpdateHtmlText(Models.HtmlText htmlText); - void DeleteHtmlText(int moduleId); + void DeleteHtmlText(int htmlTextId); } } diff --git a/Oqtane.Server/Pages/_Host.cshtml b/Oqtane.Server/Pages/_Host.cshtml index 86827eba..0fedf0b3 100644 --- a/Oqtane.Server/Pages/_Host.cshtml +++ b/Oqtane.Server/Pages/_Host.cshtml @@ -8,6 +8,7 @@ @Model.Title + @Model.Meta @if (!string.IsNullOrEmpty(Model.PWAScript)) @@ -20,34 +21,42 @@ @Html.Raw(Model.HeadResources) - @(Html.AntiForgeryToken()) - - -
- - An error has occurred. This application may no longer respond until reloaded. - - - An unhandled exception has occurred. See browser dev tools for details. - - Reload - 🗙 -
- - - - @if (Model.Runtime == "WebAssembly") - { - - } - else - { - - } - @if (!string.IsNullOrEmpty(Model.PWAScript)) + @if (string.IsNullOrEmpty(Model.Message)) { - @Html.Raw(Model.PWAScript) + @(Html.AntiForgeryToken()) + + + +
+ + An error has occurred. This application may no longer respond until reloaded. + + + An unhandled exception has occurred. See browser dev tools for details. + + Reload + 🗙 +
+ + + + @if (Model.Runtime == "WebAssembly") + { + + } + else + { + + } + @if (!string.IsNullOrEmpty(Model.PWAScript)) + { + @Html.Raw(Model.PWAScript) + } + @Html.Raw(Model.BodyResources) + } + else + { +
@Model.Message
} - @Html.Raw(Model.BodyResources) diff --git a/Oqtane.Server/Pages/_Host.cshtml.cs b/Oqtane.Server/Pages/_Host.cshtml.cs index 1c6259b8..7c76e354 100644 --- a/Oqtane.Server/Pages/_Host.cshtml.cs +++ b/Oqtane.Server/Pages/_Host.cshtml.cs @@ -25,7 +25,7 @@ namespace Oqtane.Pages { public class HostModel : PageModel { - private IConfiguration _configuration; + private IConfigManager _configuration; private readonly ITenantManager _tenantManager; private readonly ILocalizationManager _localizationManager; private readonly ILanguageRepository _languages; @@ -38,7 +38,7 @@ namespace Oqtane.Pages private readonly ISettingRepository _settings; private readonly ILogManager _logger; - public HostModel(IConfiguration configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ILogManager logger) + public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ILogManager logger) { _configuration = configuration; _tenantManager = tenantManager; @@ -63,9 +63,11 @@ namespace Oqtane.Pages public string HeadResources = ""; public string BodyResources = ""; public string Title = ""; + public string Meta = ""; public string FavIcon = "favicon.ico"; public string PWAScript = ""; public string ThemeType = ""; + public string Message = ""; public IActionResult OnGet() { @@ -83,7 +85,7 @@ namespace Oqtane.Pages } // if framework is installed - if (!string.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection"))) + if (_configuration.IsInstalled()) { var alias = _tenantManager.GetAlias(); if (alias != null) @@ -108,7 +110,7 @@ namespace Oqtane.Pages } var site = _sites.GetSite(alias.SiteId); - if (site != null) + if (site != null && !site.IsDeleted) { Route route = new Route(url, alias.Path); @@ -148,6 +150,7 @@ namespace Oqtane.Pages { Title = Title + " - " + page.Name; } + Meta = page.Meta; // include theme resources if (!string.IsNullOrEmpty(page.ThemeType)) @@ -165,42 +168,50 @@ namespace Oqtane.Pages return RedirectPermanent(url); } } - } - // include global resources - var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); - foreach (Assembly assembly in assemblies) - { - ProcessHostResources(assembly); - ProcessModuleControls(assembly); - ProcessThemeControls(assembly); - } - - // set culture if not specified - string culture = HttpContext.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName]; - if (culture == null) - { - // get default language for site - var languages = _languages.GetLanguages(alias.SiteId); - if (languages.Any()) + // include global resources + var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); + foreach (Assembly assembly in assemblies) { - // use default language if specified otherwise use first language in collection - culture = (languages.Where(l => l.IsDefault).SingleOrDefault() ?? languages.First()).Code; + ProcessHostResources(assembly); + ProcessModuleControls(assembly); + ProcessThemeControls(assembly); } - else - { - culture = _localizationManager.GetDefaultCulture(); - } - SetLocalizationCookie(culture); - } - // set language for page - if (!string.IsNullOrEmpty(culture)) - { - // localization cookie value in form of c=en|uic=en - Language = culture.Split('|')[0]; - Language = Language.Replace("c=", ""); + // set culture if not specified + string culture = HttpContext.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName]; + if (culture == null) + { + // get default language for site + var languages = _languages.GetLanguages(alias.SiteId); + if (languages.Any()) + { + // use default language if specified otherwise use first language in collection + culture = (languages.Where(l => l.IsDefault).SingleOrDefault() ?? languages.First()).Code; + } + else + { + culture = _localizationManager.GetDefaultCulture(); + } + SetLocalizationCookie(culture); + } + + // set language for page + if (!string.IsNullOrEmpty(culture)) + { + // localization cookie value in form of c=en|uic=en + Language = culture.Split('|')[0]; + Language = Language.Replace("c=", ""); + } } + else + { + Message = "Site Is Either Disabled Or Not Configured Correctly"; + } + } + else + { + Message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name"; } } return Page(); diff --git a/Oqtane.Server/Repository/Context/MasterDBContext.cs b/Oqtane.Server/Repository/Context/MasterDBContext.cs index 58b80d59..d607749d 100644 --- a/Oqtane.Server/Repository/Context/MasterDBContext.cs +++ b/Oqtane.Server/Repository/Context/MasterDBContext.cs @@ -1,19 +1,16 @@ using System; -using System.Collections.Generic; -using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Migrations; using Oqtane.Models; -using Microsoft.Extensions.Configuration; using Oqtane.Databases.Interfaces; using Oqtane.Extensions; -using Oqtane.Interfaces; using Oqtane.Migrations.Framework; using Oqtane.Repository.Databases.Interfaces; using Oqtane.Shared; using System.Threading.Tasks; using System.Threading; +using Oqtane.Infrastructure; // ReSharper disable BuiltInTypeReferenceStyleForMemberAccess // ReSharper disable UnusedAutoPropertyAccessor.Global @@ -24,14 +21,14 @@ namespace Oqtane.Repository public class MasterDBContext : DbContext, IMultiDatabase { private readonly IHttpContextAccessor _accessor; - private readonly IConfiguration _configuration; + private readonly IConfigManager _config; private string _connectionString; private string _databaseType; - public MasterDBContext(DbContextOptions options, IHttpContextAccessor accessor, IConfiguration configuration) : base(options) + public MasterDBContext(DbContextOptions options, IHttpContextAccessor accessor, IConfigManager config) : base(options) { _accessor = accessor; - _configuration = configuration; + _config = config; } public IDatabase ActiveDatabase { get; private set; } @@ -40,15 +37,15 @@ namespace Oqtane.Repository { optionsBuilder.ReplaceService(); - if(_configuration != null) + if(_config != null) { - if (!String.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection"))) + if (_config.IsInstalled()) { - _connectionString = _configuration.GetConnectionString("DefaultConnection") + _connectionString = _config.GetConnectionString("DefaultConnection") .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); } - _databaseType = _configuration.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey]; + _databaseType = _config.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey]; } if (!String.IsNullOrEmpty(_databaseType)) diff --git a/Oqtane.Server/Security/PrincipalValidator.cs b/Oqtane.Server/Security/PrincipalValidator.cs index 170e20ff..46f9f3c7 100644 --- a/Oqtane.Server/Security/PrincipalValidator.cs +++ b/Oqtane.Server/Security/PrincipalValidator.cs @@ -6,7 +6,6 @@ using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Models; using System.Collections.Generic; -using Microsoft.Extensions.Configuration; namespace Oqtane.Security { @@ -17,8 +16,8 @@ namespace Oqtane.Security if (context != null && context.Principal.Identity.IsAuthenticated) { // check if framework is installed - var config = context.HttpContext.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration; - if (!string.IsNullOrEmpty(config.GetConnectionString("DefaultConnection"))) + var config = context.HttpContext.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager; + if (config.IsInstalled()) { var tenantManager = context.HttpContext.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager; var alias = tenantManager.GetAlias(); diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index eb6a26a2..83cb5a6a 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -21,7 +21,6 @@ namespace Oqtane { public class Startup { - private readonly Runtime _runtime; private readonly bool _useSwagger; private readonly IWebHostEnvironment _env; private readonly string[] _supportedCultures; @@ -36,7 +35,6 @@ namespace Oqtane Configuration = builder.Build(); _supportedCultures = localizationManager.GetSupportedCultures(); - _runtime = (Configuration.GetSection("Runtime").Value == "WebAssembly") ? Runtime.WebAssembly : Runtime.Server; //add possibility to switch off swagger on production. _useSwagger = Configuration.GetSection("UseSwagger").Value != "false"; @@ -115,7 +113,7 @@ namespace Oqtane services.AddOqtaneTransientServices(); // load the external assemblies into the app domain, install services - services.AddOqtane(_runtime, _supportedCultures); + services.AddOqtane(_supportedCultures); services.AddOqtaneDbContext(); services.AddMvc() diff --git a/Oqtane.Shared/Models/Page.cs b/Oqtane.Shared/Models/Page.cs index e11661f1..ff2fdfc3 100644 --- a/Oqtane.Shared/Models/Page.cs +++ b/Oqtane.Shared/Models/Page.cs @@ -61,7 +61,12 @@ namespace Oqtane.Models /// Reference to a Container which will be used for modules on this page. /// public string DefaultContainerType { get; set; } - + + /// + /// Meta tags to be included in the head of the page + /// + public string Meta { get; set; } + /// /// Icon file for this page. /// TODO: unclear what this is for, and what icon library is used. Probably FontAwesome? From 95d8c368c82033d641ccfe4e33916f4fdc7221f4 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sun, 6 Feb 2022 18:54:09 -0500 Subject: [PATCH 3/4] Meta tags should not be HTML encoded --- Oqtane.Server/Pages/_Host.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Server/Pages/_Host.cshtml b/Oqtane.Server/Pages/_Host.cshtml index 0fedf0b3..4b56af10 100644 --- a/Oqtane.Server/Pages/_Host.cshtml +++ b/Oqtane.Server/Pages/_Host.cshtml @@ -8,7 +8,7 @@ @Model.Title - @Model.Meta + @Html.Raw(@Model.Meta) @if (!string.IsNullOrEmpty(Model.PWAScript)) From ede6a45f15440f933ae742a1e0919d26b31abc41 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 8 Feb 2022 07:42:47 -0500 Subject: [PATCH 4/4] more RichTextEditor refactoring --- .../Modules/Controls/RichTextEditor.razor | 69 ++++++++++++------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index 6d2012c3..402f289b 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -87,9 +87,10 @@ private ElementReference _toolBar; private bool _filemanagervisible = false; private FileManager _fileManager; - private string _rawhtml = string.Empty; private string _richhtml = string.Empty; - private string _original = string.Empty; + private string _originalrichhtml = string.Empty; + private string _rawhtml = string.Empty; + private string _originalrawhtml = string.Empty; private string _message = string.Empty; [Parameter] @@ -123,9 +124,9 @@ protected override void OnParametersSet() { - _rawhtml = Content; _richhtml = Content; - _original = _rawhtml; // preserve original content for comparison + _rawhtml = Content; + _originalrawhtml = _rawhtml; // preserve for comparison later } protected override async Task OnAfterRenderAsync(bool firstRender) @@ -143,9 +144,16 @@ Placeholder, Theme, DebugLevel); - } - await interop.LoadEditorContent(_editorElement, _richhtml); + await interop.LoadEditorContent(_editorElement, _richhtml); + + // preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor) + _originalrichhtml = await interop.GetHtml(_editorElement); + } + else + { + await interop.LoadEditorContent(_editorElement, _richhtml); + } } public void CloseFileManager() @@ -158,29 +166,38 @@ public void RefreshRichText() { _richhtml = _rawhtml; - StateHasChanged(); - } + StateHasChanged(); + } - public async Task RefreshRawHtml() - { - var interop = new RichTextEditorInterop(JSRuntime); - _rawhtml = await interop.GetHtml(_editorElement); - StateHasChanged(); - } + public async Task RefreshRawHtml() + { + var interop = new RichTextEditorInterop(JSRuntime); + _rawhtml = await interop.GetHtml(_editorElement); + StateHasChanged(); + } - public async Task GetHtml() - { - // return raw content if it has changed - if (_original != _rawhtml) - { - return _rawhtml; - } - else - { - // otherwise return rich text content + public async Task GetHtml() + { + // evaluate raw html content as first priority + if (_rawhtml != _originalrawhtml) + { + return _rawhtml; + } + else + { + // return rich text content if it has changed var interop = new RichTextEditorInterop(JSRuntime); - return await interop.GetHtml(_editorElement); - } + var richhtml = await interop.GetHtml(_editorElement); + if (richhtml != _originalrichhtml) + { + return richhtml; + } + else + { + // return original raw html content + return _originalrawhtml; + } + } } public async Task InsertImage()