diff --git a/Oqtane.Client/Modules/Admin/Files/Add.razor b/Oqtane.Client/Modules/Admin/Files/Add.razor index a01b9b1c..9f19370d 100644 --- a/Oqtane.Client/Modules/Admin/Files/Add.razor +++ b/Oqtane.Client/Modules/Admin/Files/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Files +@using System.IO @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService @@ -12,7 +13,7 @@ - + @@ -70,18 +71,32 @@ private async Task Download() { + if (url == string.Empty || _folderId == -1) + { + AddModuleMessage("You Must Enter A Url And Select A Folder", MessageType.Warning); + return; + } + + var filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); + + if (!Constants.UploadableFiles.Split(',') + .Contains(Path.GetExtension(filename).ToLower().Replace(".", ""))) + { + AddModuleMessage("File Could Not Be Downloaded From Url Due To Its File Extension", MessageType.Warning); + return ; + } + + if (!filename.IsPathOrFileValid()) + { + AddModuleMessage("You Must Enter A Url With A Valid File Name", MessageType.Warning); + return; + } + try { - if (url != string.Empty && _folderId != -1) - { - await FileService.UploadFileAsync(url, _folderId); - await logger.LogInformation("File Downloaded Successfully From Url {Url}", url); - AddModuleMessage("File Downloaded Successfully From Url", MessageType.Success); - } - else - { - AddModuleMessage("You Must Enter A Url And Select A Folder", MessageType.Warning); - } + await FileService.UploadFileAsync(url, _folderId); + await logger.LogInformation("File Downloaded Successfully From Url {Url}", url); + AddModuleMessage("File Downloaded Successfully From Url", MessageType.Success); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index 0083cce0..5d42485b 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -25,7 +25,7 @@ - + @@ -112,51 +112,63 @@ private async Task SaveFolder() { + if (_name == string.Empty || _parentId == -1) + { + AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning); + return; + } + + if (!_name.IsPathOrFileValid()) + { + AddModuleMessage("Folder Name Not Valid.", MessageType.Warning); + return; + } + try { - if (_name != string.Empty && _parentId != -1) + Folder folder; + if (_folderId != -1) { - Folder folder; - if (_folderId != -1) - { - folder = await FolderService.GetFolderAsync(_folderId); - } - else - { - folder = new Folder(); - } + folder = await FolderService.GetFolderAsync(_folderId); + } + else + { + folder = new Folder(); + } - folder.SiteId = PageState.Site.SiteId; + folder.SiteId = PageState.Site.SiteId; - if (_parentId == -1) - { - folder.ParentId = null; - } - else - { - folder.ParentId = _parentId; - } + if (_parentId == -1) + { + folder.ParentId = null; + } + else + { + folder.ParentId = _parentId; + } - folder.Name = _name; - folder.IsSystem = _isSystem; - folder.Permissions = _permissionGrid.GetPermissions(); + folder.Name = _name; + folder.IsSystem = _isSystem; + folder.Permissions = _permissionGrid.GetPermissions(); - if (_folderId != -1) - { - folder = await FolderService.UpdateFolderAsync(folder); - } - else - { - folder = await FolderService.AddFolderAsync(folder); - } + if (_folderId != -1) + { + folder = await FolderService.UpdateFolderAsync(folder); + } + else + { + folder = await FolderService.AddFolderAsync(folder); + } + if (folder != null) + { await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); await logger.LogInformation("Folder Saved {Folder}", folder); NavigationManager.NavigateTo(NavigateUrl()); } else { - AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning); + AddModuleMessage("An Error Was Encountered Saving The Folder", MessageType.Error); } } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor index 8b4cb758..9d26615a 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor @@ -3,53 +3,66 @@ @inject NavigationManager NavigationManager @inject IModuleDefinitionService ModuleDefinitionService @inject IModuleService ModuleService +@inject ISystemService SystemService - +
+ + + + + + + + + + + + + + + + + @if (!string.IsNullOrEmpty(_location)) + { - - - - - - - - - - - - -
+ + + +
+ + + +
+ + + +
+ + + +
- + - +
- - - -
- - - -
- - - -
+ } + -@code { +@code { private string _owner = string.Empty; private string _module = string.Empty; private string _description = string.Empty; - private string _template = string.Empty; + private string _template = "-"; + private string _location = string.Empty; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; @@ -62,9 +75,9 @@ { try { - if (!string.IsNullOrEmpty(_owner) && !string.IsNullOrEmpty(_module) && !string.IsNullOrEmpty(_template)) + if (!string.IsNullOrEmpty(_owner) && !string.IsNullOrEmpty(_module) && _template != "-") { - var moduleDefinition = new ModuleDefinition { Owner = _owner.Replace(" ",""), Name = _module.Replace(" ", ""), Description = _description, Template = _template }; + var moduleDefinition = new ModuleDefinition { Owner = _owner.Replace(" ", ""), Name = _module.Replace(" ", ""), Description = _description, Template = _template }; await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition, ModuleState.ModuleId); } else @@ -77,4 +90,35 @@ await logger.LogError(ex, "Error Creating Module"); } } + + private async void TemplateChanged(ChangeEventArgs e) + { + try + { + _location = string.Empty; + _template = (string)e.Value; + if (_template != "-") + { + Dictionary systeminfo = await SystemService.GetSystemInfoAsync(); + if (systeminfo != null) + { + string[] path = systeminfo["serverpath"].Split('\\'); + if (_template == "internal") + { + _location = string.Join("\\", path, 0, path.Length - 1) + "\\Oqtane.Client\\Modules\\" + _owner + "." + _module + "s"; + } + else + { + _location = string.Join("\\", path, 0, path.Length - 2) + "\\" + _owner + "." + _module + "s"; + } + } + } + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Getting System Info {Error}", ex.Message); + AddModuleMessage("Error Getting System Info", MessageType.Error); + } + } } diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/debug.cmd b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/debug.cmd deleted file mode 100644 index 37f0dd87..00000000 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/debug.cmd +++ /dev/null @@ -1,6 +0,0 @@ -XCOPY "..\Client\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Client.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y -XCOPY "..\Client\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Client.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y -XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Module.Server.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y -XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Module.Server.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y -XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Shared.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y -XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Shared.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index a63784bc..db9186bd 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -35,7 +35,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index c8a90d23..98e78bdf 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -24,7 +24,7 @@ + + + + + + + + @@ -77,6 +88,7 @@ private Dictionary _containers; private string _title; private string _containerType; + private string _allPages = "false"; private string _permissionNames = ""; private string _permissions; private string _pageId; @@ -95,6 +107,15 @@ _title = ModuleState.Title; _containers = ThemeService.GetContainerTypes(await ThemeService.GetThemesAsync()); _containerType = ModuleState.ContainerType; + if (!string.IsNullOrEmpty(PageState.Page.DefaultContainerType) && _containerType == PageState.Page.DefaultContainerType) + { + _containerType = "-"; + } + if (_containerType == PageState.Site.DefaultContainerType) + { + _containerType = "-"; + } + _allPages = ModuleState.AllPages.ToString(); _permissions = ModuleState.Permissions; _permissionNames = ModuleState.ModuleDefinition.PermissionNames; _pageId = ModuleState.PageId.ToString(); @@ -102,8 +123,8 @@ _settingsModuleType = Type.GetType(ModuleState.ModuleType); if (_settingsModuleType != null) { - var moduleobject = Activator.CreateInstance(_settingsModuleType); - _settingstitle = (string)_settingsModuleType.GetProperty("Title").GetValue(moduleobject, null); + var moduleobject = Activator.CreateInstance(_settingsModuleType) as IModuleControl; + _settingstitle = moduleobject.Title; if (string.IsNullOrEmpty(_settingstitle)) { _settingstitle = "Other Settings"; @@ -120,18 +141,26 @@ private async Task SaveModule() { - var module = ModuleState; - module.Permissions = _permissionGrid.GetPermissions(); - await ModuleService.UpdateModuleAsync(module); - var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); pagemodule.PageId = int.Parse(_pageId); pagemodule.Title = _title; - pagemodule.ContainerType = _containerType; - + pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty; + if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType) + { + pagemodule.ContainerType = string.Empty; + } + if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType) + { + pagemodule.ContainerType = string.Empty; + } await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); + var module = ModuleState; + module.AllPages = bool.Parse(_allPages); + module.Permissions = _permissionGrid.GetPermissions(); + await ModuleService.UpdateModuleAsync(module); + if (_settingsModuleType != null) { var moduleType = Type.GetType(ModuleState.ModuleType); diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index f7e3c20f..8761ff3a 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -101,7 +101,7 @@ - + @foreach (KeyValuePair panelayout in _panelayouts) { if (panelayout.Key == _layouttype) @@ -137,6 +137,20 @@ + + + + + + + + @@ -187,6 +201,7 @@ @code { private Dictionary _themes; private Dictionary _panelayouts; + private Dictionary _containers = new Dictionary(); private List _themeList; private List _pageList; private string _name; @@ -202,6 +217,7 @@ private string _mode = "view"; private string _themetype = "-"; private string _layouttype = "-"; + private string _containertype = "-"; private string _icon = string.Empty; private string _permissions = string.Empty; private PermissionGrid _permissionGrid; @@ -216,11 +232,9 @@ _pageList = PageState.Pages; _children = PageState.Pages.Where(item => item.ParentId == null).ToList(); - _themetype = PageState.Site.DefaultThemeType; - _layouttype = PageState.Site.DefaultLayoutType; - _themes = ThemeService.GetThemeTypes(_themeList); _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); + _containers = ThemeService.GetContainerTypes(_themeList); _permissions = string.Empty; } @@ -351,16 +365,20 @@ page.Url = _url; page.EditMode = (_mode == "edit" ? true : false); page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; - page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty; - if (page.ThemeType == PageState.Site.DefaultThemeType) + if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType) { page.ThemeType = string.Empty; } - - if (page.LayoutType == PageState.Site.DefaultLayoutType) + page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty; + if (!string.IsNullOrEmpty(page.LayoutType) && page.LayoutType == PageState.Site.DefaultLayoutType) { page.LayoutType = 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)); diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 8132696d..b25f6b2e 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -112,7 +112,7 @@ - + @foreach (KeyValuePair panelayout in _panelayouts) { if (panelayout.Key == _layouttype) @@ -148,6 +148,20 @@ + + + + + + + + @@ -200,6 +214,7 @@ @code { private Dictionary _themes; private Dictionary _panelayouts; + private Dictionary _containers = new Dictionary(); private List _themeList; private List _pageList; private int _pageId; @@ -217,6 +232,7 @@ private string _mode; private string _themetype = "-"; private string _layouttype = "-"; + private string _containertype = "-"; private string _icon; private string _permissions; private string _createdby; @@ -241,6 +257,7 @@ _children = PageState.Pages.Where(item => item.ParentId == null).ToList(); _themes = ThemeService.GetThemeTypes(_themeList); + _containers = ThemeService.GetContainerTypes(_themeList); _pageId = Int32.Parse(PageState.QueryString["id"]); var page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); @@ -270,16 +287,21 @@ _ispersonalizable = page.IsPersonalizable.ToString(); _mode = (page.EditMode) ? "edit" : "view"; _themetype = page.ThemeType; - _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); - _layouttype = page.LayoutType; if (_themetype == PageState.Site.DefaultThemeType) { _themetype = "-"; } + _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); + _layouttype = page.LayoutType; if (_layouttype == PageState.Site.DefaultLayoutType) { _layouttype = "-"; } + _containertype = page.DefaultContainerType; + if (string.IsNullOrEmpty(_containertype)) + { + _containertype = "-"; + } _icon = page.Icon; _permissions = page.Permissions; _createdby = page.CreatedBy; @@ -426,15 +448,20 @@ page.Url = _url; page.EditMode = (_mode == "edit"); page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; - page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty; - if (page.ThemeType == PageState.Site.DefaultThemeType) + if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType) { page.ThemeType = string.Empty; } - if (page.LayoutType == PageState.Site.DefaultLayoutType) + page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty; + if (!string.IsNullOrEmpty(page.LayoutType) && page.LayoutType == PageState.Site.DefaultLayoutType) { page.LayoutType = 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)); diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index e4bae61e..3f3425f6 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -39,7 +39,7 @@ - + @@ -47,7 +47,7 @@ - + @@ -185,7 +185,7 @@ - + @@ -193,7 +193,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor b/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor index 8e19125f..ca7c7f2c 100644 --- a/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor +++ b/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor @@ -8,7 +8,7 @@ - + @@ -16,7 +16,7 @@ - + @@ -24,7 +24,7 @@ - + @@ -32,7 +32,7 @@ - + @@ -40,7 +40,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor index 5ad4f5c6..da47d8a9 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Add.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -35,7 +35,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index 2aa2ce86..7ee95e30 100644 --- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor +++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor @@ -11,29 +11,27 @@ @if (_upgradeavailable) { - - @("Framework") @_package.Version + + @("Framework") @_package.Version } else { } - @if (_upgradeavailable) - {
- + - +
+
- } } diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index 6f3ab545..fec53f62 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -64,7 +64,7 @@ else - + diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index 3aa30e1b..95449f2d 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -63,7 +63,7 @@ else - + diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor index 08b2d77d..076f1e9d 100644 --- a/Oqtane.Client/Modules/Controls/ActionDialog.razor +++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor @@ -1,6 +1,6 @@ @namespace Oqtane.Modules.Controls @inherits ModuleBase - +@attribute [OqtaneIgnore] @if (_visible) {
@@ -62,9 +62,9 @@ [Parameter] public string Class { get; set; } // optional - [Parameter] - public bool Disabled { get; set; } // optional - + [Parameter] + public bool Disabled { get; set; } // optional + [Parameter] public string EditMode { get; set; } // optional - specifies if a user must be in edit mode to see the action - default is true @@ -109,8 +109,8 @@ Type moduleType = Type.GetType(typename); if (moduleType != null) { - var moduleobject = Activator.CreateInstance(moduleType); - security = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); + var moduleobject = Activator.CreateInstance(moduleType) as IModuleControl; + security = moduleobject.SecurityAccessLevel; } else { diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index cf588579..d8ed32a6 100644 --- a/Oqtane.Client/Modules/Controls/ActionLink.razor +++ b/Oqtane.Client/Modules/Controls/ActionLink.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@inherits ModuleBase +@attribute [OqtaneIgnore] @inject IUserService UserService @if (_authorized) @@ -100,8 +101,8 @@ var moduleType = Type.GetType(typename); if (moduleType != null) { - var moduleobject = Activator.CreateInstance(moduleType); - security = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); + var moduleobject = Activator.CreateInstance(moduleType) as IModuleControl; + security = moduleobject.SecurityAccessLevel; } else { diff --git a/Oqtane.Client/Modules/Controls/AuditInfo.razor b/Oqtane.Client/Modules/Controls/AuditInfo.razor index 75079c6c..7c081d3f 100644 --- a/Oqtane.Client/Modules/Controls/AuditInfo.razor +++ b/Oqtane.Client/Modules/Controls/AuditInfo.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@inherits ModuleBase +@attribute [OqtaneIgnore] @if (_text != string.Empty) { diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index 0519e1b5..96196b06 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -1,5 +1,7 @@ @namespace Oqtane.Modules.Controls @inherits ModuleBase + +@attribute [OqtaneIgnore] @inject IFolderService FolderService @inject IFileService FileService @inject IJSRuntime JsRuntime @@ -9,33 +11,36 @@
-
- + @if (string.IsNullOrEmpty(Folder)) { - + } - else + @foreach (Folder folder in _folders) { - + if (folder.FolderId == FolderId) + { + + } + else + { + + } } - } - -
- @if (_showfiles) + +
+ } + @if (ShowFiles) {
} - @if (_haseditpermission) + @if (ShowUpload && _haseditpermission) {
- @if (_uploadmultiple) + @if (UploadMultiple) { - + } else { - + } - - @if (_showfiles && GetFileId() != -1) - { - - } + + @if (_showfiles && GetFileId() != -1) + { + + }
- @((MarkupString)_message) } + @((MarkupString) _message)
@if (_image != string.Empty) {
- @((MarkupString)_image) + @((MarkupString) _image)
}
@@ -83,15 +88,12 @@ @code { private string _id; private List _folders; - private int _folderid = -1; private List _files = new List(); - private int _fileid = -1; private bool _showfiles = true; private string _fileinputid = string.Empty; private string _progressinfoid = string.Empty; private string _progressbarid = string.Empty; private string _filter = "*"; - private bool _uploadmultiple = false; private bool _haseditpermission = false; private string _message = string.Empty; private string _image = string.Empty; @@ -104,19 +106,25 @@ public string Folder { get; set; } // optional - for setting a specific folder by default [Parameter] - public string FolderId { get; set; } // optional - for setting a specific folderid by default + public int FolderId { get; set; } = -1; // optional - for setting a specific folderid by default [Parameter] - public string ShowFiles { get; set; } // optional - for indicating whether a list of files should be displayed - default is true + public bool ShowFiles { get; set; } = true; // optional - for indicating whether a list of files should be displayed - default is true [Parameter] - public string FileId { get; set; } // optional - for setting a specific file by default + public bool ShowUpload { get; set; } = true; // optional - for indicating whether a Upload controls should be displayed - default is true + + [Parameter] + public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true + + [Parameter] + public int FileId { get; set; } = -1; // optional - for setting a specific file by default [Parameter] public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif" [Parameter] - public string UploadMultiple { get; set; } // optional - enable multiple file uploads - default false + public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false protected override async Task OnInitializedAsync() { @@ -128,56 +136,39 @@ if (!string.IsNullOrEmpty(Folder)) { _folders = new List {new Folder {FolderId = -1, Name = Folder}}; - _folderid = -1; + FolderId = -1; } else { _folders = await FolderService.GetFoldersAsync(ModuleState.SiteId); - if (!string.IsNullOrEmpty(FolderId)) - { - _folderid = int.Parse(FolderId); - } } - if (!string.IsNullOrEmpty(FileId)) + if (FileId != -1) { - _fileid = int.Parse(FileId); - if (_fileid != -1) + File file = await FileService.GetFileAsync(FileId); + if (file != null) { - File file = await FileService.GetFileAsync(int.Parse(FileId)); - if (file != null) - { - _folderid = file.FolderId; - } - else - { - _fileid = -1; // file does not exist - } + FolderId = file.FolderId; + } + else + { + FileId = -1; // file does not exist } - await SetImage(); - } - if (!string.IsNullOrEmpty(ShowFiles)) - { - _showfiles = bool.Parse(ShowFiles); } + await SetImage(); if (!string.IsNullOrEmpty(Filter)) { - _filter = "." + Filter.Replace(",",",."); + _filter = "." + Filter.Replace(",", ",."); } await GetFiles(); - // create unique id for component + // create unique id for component _guid = Guid.NewGuid().ToString("N"); _fileinputid = _guid + "FileInput"; _progressinfoid = _guid + "ProgressInfo"; _progressbarid = _guid + "ProgressBar"; - - if (!string.IsNullOrEmpty(UploadMultiple)) - { - _uploadmultiple = bool.Parse(UploadMultiple); - } } private async Task GetFiles() @@ -190,11 +181,11 @@ } else { - Folder folder = _folders.FirstOrDefault(item => item.FolderId == _folderid); + Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId); if (folder != null) { - _haseditpermission = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, folder.Permissions); - _files = await FileService.GetFilesAsync(_folderid); + _haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions); + _files = await FileService.GetFilesAsync(FolderId); } else { @@ -221,9 +212,9 @@ _message = string.Empty; try { - _folderid = int.Parse((string)e.Value); + FolderId = int.Parse((string) e.Value); await GetFiles(); - _fileid = -1; + FileId = -1; _image = string.Empty; StateHasChanged(); } @@ -237,7 +228,7 @@ private async Task FileChanged(ChangeEventArgs e) { _message = string.Empty; - _fileid = int.Parse((string)e.Value); + FileId = int.Parse((string) e.Value); await SetImage(); StateHasChanged(); @@ -246,21 +237,21 @@ private async Task SetImage() { _image = string.Empty; - if (_fileid != -1) + if (FileId != -1) { - File file = await FileService.GetFileAsync(_fileid); + File file = await FileService.GetFileAsync(FileId); if (file != null && file.ImageHeight != 0 && file.ImageWidth != 0) { var maxwidth = 200; var maxheight = 200; - var ratioX = (double)maxwidth / (double)file.ImageWidth; - var ratioY = (double)maxheight / (double)file.ImageHeight; + var ratioX = (double) maxwidth / (double) file.ImageWidth; + var ratioY = (double) maxheight / (double) file.ImageHeight; var ratio = ratioX < ratioY ? ratioX : ratioY; - _image = "\"""; + _image = "\"""; } } } @@ -280,7 +271,7 @@ } else { - result = await FileService.UploadFilesAsync(_folderid, upload, _guid); + result = await FileService.UploadFilesAsync(FolderId, upload, _guid); } if (result == string.Empty) @@ -294,7 +285,7 @@ var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault(); if (file != null) { - _fileid = file.FileId; + FileId = file.FileId; await SetImage(); } } @@ -324,21 +315,21 @@ try { - await FileService.DeleteFileAsync(_fileid); - await logger.LogInformation("File Deleted {File}", _fileid); + await FileService.DeleteFileAsync(FileId); + await logger.LogInformation("File Deleted {File}", FileId); _message = "
File Deleted
"; await GetFiles(); - _fileid = -1; + FileId = -1; await SetImage(); StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Deleting File {File} {Error}", _fileid, ex.Message); + await logger.LogError(ex, "Error Deleting File {File} {Error}", FileId, ex.Message); _message = "
Error Deleting File
"; } } - public int GetFileId() => _fileid; + public int GetFileId() => FileId; } diff --git a/Oqtane.Client/Modules/Controls/Label.razor b/Oqtane.Client/Modules/Controls/Label.razor index 0826e0cb..e57d01ec 100644 --- a/Oqtane.Client/Modules/Controls/Label.razor +++ b/Oqtane.Client/Modules/Controls/Label.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@inherits ModuleBase +@attribute [OqtaneIgnore] @if (!string.IsNullOrEmpty(HelpText)) { @@ -41,4 +42,4 @@ else _openLabel += ">"; } -} \ No newline at end of file +} diff --git a/Oqtane.Client/Modules/Controls/ModuleMessage.razor b/Oqtane.Client/Modules/Controls/ModuleMessage.razor index 67f3f50d..5e00cb76 100644 --- a/Oqtane.Client/Modules/Controls/ModuleMessage.razor +++ b/Oqtane.Client/Modules/Controls/ModuleMessage.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@inherits ModuleBase +@attribute [OqtaneIgnore] @if (!string.IsNullOrEmpty(_message)) { diff --git a/Oqtane.Client/Modules/Controls/Pager.razor b/Oqtane.Client/Modules/Controls/Pager.razor index 184eaa8a..ff7e13ee 100644 --- a/Oqtane.Client/Modules/Controls/Pager.razor +++ b/Oqtane.Client/Modules/Controls/Pager.razor @@ -1,6 +1,8 @@ @namespace Oqtane.Modules.Controls @inherits ModuleBase -@typeparam TableItem +@attribute [OqtaneIgnore] +@typeparam TableItem +

@if(Format == "Table") @@ -209,4 +211,4 @@ UpdateList(_page); } -} \ No newline at end of file +} diff --git a/Oqtane.Client/Modules/Controls/PermissionGrid.razor b/Oqtane.Client/Modules/Controls/PermissionGrid.razor index 543403f2..90954bbe 100644 --- a/Oqtane.Client/Modules/Controls/PermissionGrid.razor +++ b/Oqtane.Client/Modules/Controls/PermissionGrid.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@inherits ModuleBase +@attribute [OqtaneIgnore] @inject IRoleService RoleService @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index 36368c5a..c588274f 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -1,28 +1,82 @@ @namespace Oqtane.Modules.Controls @inherits ModuleBase +@attribute [OqtaneIgnore] @inject IJSRuntime JsRuntime -@if (_filemanagervisible) -{ - - @((MarkupString)_message) -
-} -

- - @if (_filemanagervisible) - { - @((MarkupString)"  ") - - } -
-
-
-
- @ToolbarContent -
-
-
+
+
+ + + @if (_filemanagervisible) + { + + @((MarkupString)_message) +
+ } +
+    + + @if (_filemanagervisible) + { + @((MarkupString)"  ") + + } +
+
+
+
+ @if (ToolbarContent != null) + { + @ToolbarContent + } + else + { + + + + + + + + + + + + + + + + + + + } +
+
+
+
+
+
+ +
+ +
+ @if (ReadOnly) + { + + } + else + { + + } +
+
@@ -31,10 +85,12 @@ 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; [Parameter] - public RenderFragment ToolbarContent { get; set; } + public string Content { get; set; } [Parameter] public bool ReadOnly { get; set; } = false; @@ -42,60 +98,78 @@ [Parameter] public string Placeholder { get; set; } = "Enter Your Content..."; + // parameters only applicable to rich text editor + [Parameter] + public RenderFragment ToolbarContent { get; set; } + [Parameter] public string Theme { get; set; } = "snow"; [Parameter] public string DebugLevel { get; set; } = "info"; + protected override void OnInitialized() + { + _content = Content; // raw HTML + } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - await RichTextEditorInterop.CreateEditor( - JsRuntime, + var interop = new RichTextEditorInterop(JsRuntime); + + await interop.CreateEditor( _editorElement, _toolBar, ReadOnly, Placeholder, Theme, DebugLevel); + + await interop.LoadEditorContent(_editorElement, Content); + + // 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 async Task GetText() + public void CloseFileManager() { - return await RichTextEditorInterop.GetText( - JsRuntime, - _editorElement); + _filemanagervisible = false; + _message = string.Empty; + 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); + StateHasChanged(); } public async Task GetHtml() { - return await RichTextEditorInterop.GetHtml( - JsRuntime, - _editorElement); - } + // get rich text content + var interop = new RichTextEditorInterop(JsRuntime); + string content = await interop.GetHtml(_editorElement); - public async Task GetContent() - { - return await RichTextEditorInterop.GetContent( - JsRuntime, - _editorElement); - } - - public async Task LoadContent(string content) - { - await RichTextEditorInterop.LoadEditorContent( - JsRuntime, - _editorElement, content); - } - - public async Task EnableEditor(bool mode) - { - await RichTextEditorInterop.EnableEditor( - JsRuntime, - _editorElement, mode); + if (_original != content) + { + // rich text content has changed - return it + return content; + } + else + { + // return raw html content + return _content; + } } public async Task InsertImage() @@ -105,9 +179,8 @@ var fileid = _fileManager.GetFileId(); if (fileid != -1) { - await RichTextEditorInterop.InsertImage( - JsRuntime, - _editorElement, ContentUrl(fileid)); + var interop = new RichTextEditorInterop(JsRuntime); + await interop.InsertImage(_editorElement, ContentUrl(fileid)); _filemanagervisible = false; _message = string.Empty; } @@ -121,16 +194,25 @@ _filemanagervisible = true; _message = string.Empty; } - StateHasChanged(); } - public void CloseFileManager() + // other rich text editor methods which can be used by developers + public async Task GetText() { - _filemanagervisible = false; - _message = string.Empty; - - StateHasChanged(); + 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/Controls/RichTextEditorInterop.cs b/Oqtane.Client/Modules/Controls/RichTextEditorInterop.cs new file mode 100644 index 00000000..c9b442d4 --- /dev/null +++ b/Oqtane.Client/Modules/Controls/RichTextEditorInterop.cs @@ -0,0 +1,124 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using System.Threading.Tasks; + +namespace Oqtane.Modules.Controls +{ + public class RichTextEditorInterop + { + private readonly IJSRuntime _jsRuntime; + + public RichTextEditorInterop(IJSRuntime jsRuntime) + { + _jsRuntime = jsRuntime; + } + + public Task CreateEditor( + ElementReference quillElement, + ElementReference toolbar, + bool readOnly, + string placeholder, + string theme, + string debugLevel) + { + try + { + _jsRuntime.InvokeAsync( + "interop.createQuill", + quillElement, toolbar, readOnly, + placeholder, theme, debugLevel); + return Task.CompletedTask; + } + catch + { + return Task.CompletedTask; + } + } + + public ValueTask GetText(ElementReference quillElement) + { + try + { + return _jsRuntime.InvokeAsync( + "interop.getQuillText", + quillElement); + } + catch + { + return new ValueTask(Task.FromResult(string.Empty)); + } + } + + public ValueTask GetHtml(ElementReference quillElement) + { + try + { + return _jsRuntime.InvokeAsync( + "interop.getQuillHTML", + quillElement); + } + catch + { + return new ValueTask(Task.FromResult(string.Empty)); + } + } + + public ValueTask GetContent(ElementReference quillElement) + { + try + { + return _jsRuntime.InvokeAsync( + "interop.getQuillContent", + quillElement); + } + catch + { + return new ValueTask(Task.FromResult(string.Empty)); + } + } + + public Task LoadEditorContent(ElementReference quillElement, string content) + { + try + { + _jsRuntime.InvokeAsync( + "interop.loadQuillContent", + quillElement, content); + return Task.CompletedTask; + } + catch + { + return Task.CompletedTask; + } + } + + public Task EnableEditor(ElementReference quillElement, bool mode) + { + try + { + _jsRuntime.InvokeAsync( + "interop.enableQuillEditor", quillElement, mode); + return Task.CompletedTask; + } + catch + { + return Task.CompletedTask; + } + } + + public Task InsertImage(ElementReference quillElement, string imageUrl) + { + try + { + _jsRuntime.InvokeAsync( + "interop.insertQuillImage", + quillElement, imageUrl); + return Task.CompletedTask; + } + catch + { + return Task.CompletedTask; + } + } + } +} diff --git a/Oqtane.Client/Modules/Controls/Section.razor b/Oqtane.Client/Modules/Controls/Section.razor index 6921f933..4f33b42f 100644 --- a/Oqtane.Client/Modules/Controls/Section.razor +++ b/Oqtane.Client/Modules/Controls/Section.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@inherits ModuleBase +@attribute [OqtaneIgnore]
diff --git a/Oqtane.Client/Modules/Controls/TabPanel.razor b/Oqtane.Client/Modules/Controls/TabPanel.razor index d09ff53f..bd89ab59 100644 --- a/Oqtane.Client/Modules/Controls/TabPanel.razor +++ b/Oqtane.Client/Modules/Controls/TabPanel.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@inherits ModuleBase +@attribute [OqtaneIgnore] @if (Name == Parent.ActiveTab) { diff --git a/Oqtane.Client/Modules/Controls/TabStrip.razor b/Oqtane.Client/Modules/Controls/TabStrip.razor index 4f0b872f..8d8d3838 100644 --- a/Oqtane.Client/Modules/Controls/TabStrip.razor +++ b/Oqtane.Client/Modules/Controls/TabStrip.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@inherits ModuleBase +@attribute [OqtaneIgnore]
diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index ab0f1feb..292706e2 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -3,115 +3,62 @@ @using Oqtane.Modules.Controls @namespace Oqtane.Modules.HtmlText @inherits ModuleBase +@inject IHtmlTextService HtmlTextService @inject NavigationManager NavigationManager -@inject HttpClient http -@inject SiteState sitestate -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- @if (!RichTextEditorMode) - { - - } - else - { - - } - - Cancel -
-
- -
-
- -
-
+@if (_content != null) +{ + + + Cancel + @if (!string.IsNullOrEmpty(_content)) + { +
+
+ + } +} @code { - private string _visibleText = "d-none"; - private string _visibleRich; - private bool _richTextEditorMode; - private RichTextEditor RichTextEditorHtml; - private string content; - private string createdby; - private DateTime createdon; - private string modifiedby; - private DateTime modifiedon; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; public override string Title => "Edit Html/Text"; - public bool RichTextEditorMode + public override List Resources => new List() { - get => _richTextEditorMode; - set - { - _richTextEditorMode = value; - - if (_richTextEditorMode) - { - _visibleText = "d-none"; - _visibleRich = string.Empty; - } - else - { - _visibleText = string.Empty; - _visibleRich = "d-none"; - } - } - } + new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }, + // the following resources should be declared in the RichTextEditor component however the framework currently only supports resource management for modules and themes + new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.bubble.css" }, + new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.snow.css" }, + new Resource { ResourceType = ResourceType.Script, Url = "js/quill1.3.6.min.js" }, + new Resource { ResourceType = ResourceType.Script, Url = "js/quill-blot-formatter.min.js" }, + new Resource { ResourceType = ResourceType.Script, Url = "js/quill-interop.js" } + }; - protected override async Task OnAfterRenderAsync(bool firstRender) + private RichTextEditor RichTextEditorHtml; + private string _content = null; + private string _createdby; + private DateTime _createdon; + private string _modifiedby; + private DateTime _modifiedon; + + protected override async Task OnInitializedAsync() { try { - if (firstRender) + var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId); + if (htmltext != null) { - if (content == null) - { - RichTextEditorMode = true; - await LoadText(); - } + _content = htmltext.Content; + _content = _content.Replace(Constants.ContentUrl, "/" + PageState.Alias.AliasId.ToString() + Constants.ContentUrl); + _createdby = htmltext.CreatedBy; + _createdon = htmltext.CreatedOn; + _modifiedby = htmltext.ModifiedBy; + _modifiedon = htmltext.ModifiedOn; + } + else + { + _content = string.Empty; } } catch (Exception ex) @@ -121,66 +68,27 @@ } } - private async Task LoadText() - { - var htmltextservice = new HtmlTextService(http, sitestate); - var htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId); - if (htmltext != null) - { - content = htmltext.Content; - createdby = htmltext.CreatedBy; - createdon = htmltext.CreatedOn; - modifiedby = htmltext.ModifiedBy; - modifiedon = htmltext.ModifiedOn; - - if (RichTextEditorMode) - { - await RichTextEditorHtml.LoadContent(content); - StateHasChanged(); - } - } - } - - private async Task RichTextEditor() - { - RichTextEditorMode = true; - await RichTextEditorHtml.LoadContent(content); - StateHasChanged(); - } - - private async Task RawHtmlEditor() - { - content = await this.RichTextEditorHtml.GetHtml(); - RichTextEditorMode = false; - StateHasChanged(); - } - private async Task SaveContent() { - if (RichTextEditorMode) - { - content = await RichTextEditorHtml.GetHtml(); - } - - content = content.Replace(((PageState.Alias.Path == string.Empty) ? "/~" : PageState.Alias.Path) + Constants.ContentUrl, Constants.ContentUrl); + string content = await RichTextEditorHtml.GetHtml(); + content = content.Replace("/" + PageState.Alias.AliasId.ToString() + Constants.ContentUrl, Constants.ContentUrl); try { - var htmltextservice = new HtmlTextService(http, sitestate); - var htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId); + var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId); if (htmltext != null) { htmltext.Content = content; - await htmltextservice.UpdateHtmlTextAsync(htmltext); + await HtmlTextService.UpdateHtmlTextAsync(htmltext); } else { htmltext = new HtmlTextInfo(); htmltext.ModuleId = ModuleState.ModuleId; htmltext.Content = content; - await htmltextservice.AddHtmlTextAsync(htmltext); + await HtmlTextService.AddHtmlTextAsync(htmltext); } - + await logger.LogInformation("Html/Text Content Saved {HtmlText}", htmltext); NavigationManager.NavigateTo(NavigateUrl()); } @@ -190,5 +98,4 @@ AddModuleMessage("Error Saving Content", MessageType.Error); } } - } diff --git a/Oqtane.Client/Modules/HtmlText/Index.razor b/Oqtane.Client/Modules/HtmlText/Index.razor index e04ffbf2..8160c19a 100644 --- a/Oqtane.Client/Modules/HtmlText/Index.razor +++ b/Oqtane.Client/Modules/HtmlText/Index.razor @@ -1,36 +1,32 @@ @using Oqtane.Modules.HtmlText.Services -@using Oqtane.Modules.HtmlText.Models @namespace Oqtane.Modules.HtmlText @inherits ModuleBase -@inject NavigationManager NavigationManager -@inject HttpClient http -@inject SiteState sitestate +@inject IHtmlTextService HtmlTextService @((MarkupString)content) @if (PageState.EditMode) { -
-} - -@if (PageState.EditMode) -{ -

+


} @code { + public override List Resources => new List() + { + new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" } + }; + private string content = ""; protected override async Task OnParametersSetAsync() { try { - var htmltextservice = new HtmlTextService(http, sitestate); - var htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId); + var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId); if (htmltext != null) { content = htmltext.Content; - content = content.Replace(Constants.ContentUrl, ((PageState.Alias.Path == "") ? "/~" : PageState.Alias.Path) + Constants.ContentUrl); + content = content.Replace(Constants.ContentUrl, "/" + PageState.Alias.AliasId.ToString() + Constants.ContentUrl); } } catch (Exception ex) diff --git a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs index cb73b9a4..51ab7887 100644 --- a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs +++ b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs @@ -8,7 +8,7 @@ using Oqtane.Shared; namespace Oqtane.Modules.HtmlText.Services { - public class HtmlTextService : ServiceBase, IHtmlTextService + public class HtmlTextService : ServiceBase, IHtmlTextService, IService { private readonly SiteState _siteState; diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index c81f8d10..9da4a090 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -6,10 +6,11 @@ using Oqtane.Services; using System; using Oqtane.Enums; using Oqtane.UI; +using System.Collections.Generic; namespace Oqtane.Modules { - public class ModuleBase : ComponentBase, IModuleControl + public abstract class ModuleBase : ComponentBase, IModuleControl { private Logger _logger; @@ -37,6 +38,9 @@ namespace Oqtane.Modules public virtual bool UseAdminContainer { get { return true; } } + public virtual List Resources { get; set; } + + // path method public string ModulePath() diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index f19dac81..a551efc9 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -6,7 +6,7 @@ 7.3 3.0 Debug;Release - 0.9.0 + 1.0.0 Oqtane Shaun Walker .NET Foundation @@ -27,10 +27,11 @@ - - - - + + + + + diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index 74fa8de4..8a1fd2b8 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -4,12 +4,16 @@ using System.Threading.Tasks; using Oqtane.Services; using System.Reflection; using System; +using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Net.Http.Json; using Oqtane.Modules; using Oqtane.Shared; using Oqtane.Providers; using Microsoft.AspNetCore.Components.Authorization; +using System.IO.Compression; +using System.IO; namespace Oqtane.Client { @@ -19,10 +23,9 @@ namespace Oqtane.Client { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("app"); + HttpClient httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)}; - builder.Services.AddSingleton( - new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) } - ); + builder.Services.AddSingleton(httpClient); builder.Services.AddOptions(); // register auth services @@ -57,14 +60,16 @@ namespace Oqtane.Client builder.Services.AddScoped(); builder.Services.AddScoped(); + await LoadClientAssemblies(httpClient); + // dynamically register module contexts and repository services Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { - Type[] implementationtypes = assembly.GetTypes() - .Where(item => item.GetInterfaces().Contains(typeof(IService))) - .ToArray(); - foreach (Type implementationtype in implementationtypes) + var implementationTypes = assembly.GetTypes() + .Where(item => item.GetInterfaces().Contains(typeof(IService))); + + foreach (Type implementationtype in implementationTypes) { Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name)); if (servicetype != null) @@ -76,9 +81,62 @@ namespace Oqtane.Client builder.Services.AddScoped(implementationtype, implementationtype); // no interface defined for service } } + + assembly.GetInstances() + .ToList() + .ForEach(x => x.ConfigureServices(builder.Services)); } await builder.Build().RunAsync(); } + + private static async Task LoadClientAssemblies(HttpClient http) + { + // get list of loaded assemblies on the client + var assemblies = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToList(); + + // get assemblies from server and load into client app domain + var zip = await http.GetByteArrayAsync($"/~/api/Installation/load"); + + // asemblies and debug symbols are packaged in a zip file + using (ZipArchive archive = new ZipArchive(new MemoryStream(zip))) + { + Dictionary dlls = new Dictionary(); + Dictionary pdbs = new Dictionary(); + + foreach (ZipArchiveEntry entry in archive.Entries) + { + if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.Name))) + { + using (var memoryStream = new MemoryStream()) + { + entry.Open().CopyTo(memoryStream); + byte[] file = memoryStream.ToArray(); + switch (Path.GetExtension(entry.Name)) + { + case ".dll": + dlls.Add(entry.Name, file); + break; + case ".pdb": + pdbs.Add(entry.Name, file); + break; + } + } + } + } + + foreach (var item in dlls) + { + if (pdbs.ContainsKey(item.Key)) + { + Assembly.Load(item.Value, pdbs[item.Key]); + } + else + { + Assembly.Load(item.Value); + } + } + } + } } } diff --git a/Oqtane.Client/Services/AliasService.cs b/Oqtane.Client/Services/AliasService.cs index 1e0f5ac8..21df7dd0 100644 --- a/Oqtane.Client/Services/AliasService.cs +++ b/Oqtane.Client/Services/AliasService.cs @@ -5,16 +5,21 @@ using System.Linq; using System.Collections.Generic; using System.Net; using System; - +using Oqtane.Shared; namespace Oqtane.Services { public class AliasService : ServiceBase, IAliasService { - - public AliasService(HttpClient http) :base(http) { } - private string Apiurl => CreateApiUrl("Alias"); + private readonly SiteState _siteState; + + public AliasService(HttpClient http, SiteState siteState) : base(http) + { + _siteState = siteState; + } + + private string Apiurl => CreateApiUrl(_siteState.Alias, "Alias"); public async Task> GetAliasesAsync() { diff --git a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs index 8560c661..cca5e642 100644 --- a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs +++ b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs @@ -12,7 +12,6 @@ namespace Oqtane.Services Task UpdateModuleDefinitionAsync(ModuleDefinition moduleDefinition); Task InstallModuleDefinitionsAsync(); Task DeleteModuleDefinitionAsync(int moduleDefinitionId, int siteId); - Task LoadModuleDefinitionsAsync(int siteId, Runtime runtime); Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId); } } diff --git a/Oqtane.Client/Services/JobLogService.cs b/Oqtane.Client/Services/JobLogService.cs index c15940b5..aa518771 100644 --- a/Oqtane.Client/Services/JobLogService.cs +++ b/Oqtane.Client/Services/JobLogService.cs @@ -3,14 +3,20 @@ using System.Threading.Tasks; using System.Net.Http; using System.Linq; using System.Collections.Generic; +using Oqtane.Shared; namespace Oqtane.Services { public class JobLogService : ServiceBase, IJobLogService { - public JobLogService(HttpClient http) :base(http) { } + private readonly SiteState _siteState; - private string Apiurl => CreateApiUrl("JobLog"); + public JobLogService(HttpClient http, SiteState siteState) : base(http) + { + _siteState = siteState; + } + + private string Apiurl => CreateApiUrl(_siteState.Alias, "JobLog"); public async Task> GetJobLogsAsync() { diff --git a/Oqtane.Client/Services/JobService.cs b/Oqtane.Client/Services/JobService.cs index 14cd96ad..3161cd90 100644 --- a/Oqtane.Client/Services/JobService.cs +++ b/Oqtane.Client/Services/JobService.cs @@ -3,15 +3,21 @@ using System.Threading.Tasks; using System.Net.Http; using System.Linq; using System.Collections.Generic; +using Oqtane.Shared; namespace Oqtane.Services { public class JobService : ServiceBase, IJobService { - public JobService(HttpClient http) : base(http) { } + private readonly SiteState _siteState; - private string Apiurl => CreateApiUrl("Job"); + public JobService(HttpClient http, SiteState siteState) : base(http) + { + _siteState = siteState; + } + private string Apiurl => CreateApiUrl(_siteState.Alias, "Job"); + public async Task> GetJobsAsync() { List jobs = await GetJsonAsync>(Apiurl); diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index 12d408b4..8486e417 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -49,46 +49,9 @@ namespace Oqtane.Services await DeleteAsync($"{Apiurl}/{moduleDefinitionId}?siteid={siteId}"); } - public async Task LoadModuleDefinitionsAsync(int siteId, Runtime runtime) - { - // get list of modules from the server - List moduledefinitions = await GetModuleDefinitionsAsync(siteId); - - // download assemblies to browser when running client-side Blazor - if (runtime == Runtime.WebAssembly) - { - // get list of loaded assemblies on the client ( in the client-side hosting module the browser client has its own app domain ) - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - - foreach (ModuleDefinition moduledefinition in moduledefinitions) - { - // if a module has dependencies, check if they are loaded - if (moduledefinition.Dependencies != "") - { - foreach (string dependency in moduledefinition.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) - { - string assemblyname = dependency.Replace(".dll", ""); - if (assemblies.Where(item => item.FullName.StartsWith(assemblyname + ",")).FirstOrDefault() == null) - { - // download assembly from server and load - var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{assemblyname}.dll"); - Assembly.Load(bytes); - } - } - } - // check if the module assembly is loaded - if (assemblies.Where(item => item.FullName.StartsWith(moduledefinition.AssemblyName + ",")).FirstOrDefault() == null) - { - // download assembly from server and load - var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{moduledefinition.AssemblyName}.dll"); - Assembly.Load(bytes); - } - } - } - } public async Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId) { - await PostJsonAsync($"{Apiurl}?moduleid={moduleId.ToString()}", moduleDefinition); + await PostJsonAsync($"{Apiurl}?moduleid={moduleId}", moduleDefinition); } } } diff --git a/Oqtane.Client/Services/ServiceBase.cs b/Oqtane.Client/Services/ServiceBase.cs index 742bd3fa..091709f2 100644 --- a/Oqtane.Client/Services/ServiceBase.cs +++ b/Oqtane.Client/Services/ServiceBase.cs @@ -82,7 +82,6 @@ namespace Oqtane.Services var result = await response.Content.ReadFromJsonAsync(); return result; } - return default; } @@ -121,6 +120,8 @@ namespace Oqtane.Services if (response.StatusCode != HttpStatusCode.NoContent && response.StatusCode != HttpStatusCode.NotFound) { //TODO: Log errors here + + Console.WriteLine($"Request: {response.RequestMessage.RequestUri}"); Console.WriteLine($"Response status: {response.StatusCode} {response.ReasonPhrase}"); } @@ -134,13 +135,13 @@ namespace Oqtane.Services //TODO Missing content JSON validation } - // create an API Url which is tenant agnostic ( for use with entities in the MasterDB ) + // create an API Url which is tenant agnostic ( for use during installation ) public string CreateApiUrl(string serviceName) { return CreateApiUrl(null, serviceName); } - // create an API Url which is tenant aware ( for use with entities in the TenantDB ) + // create an API Url which is tenant aware ( for use with repositories ) public string CreateApiUrl(Alias alias, string serviceName) { string apiurl = "/"; diff --git a/Oqtane.Client/Services/SettingService.cs b/Oqtane.Client/Services/SettingService.cs index c5cf45c4..451e97b8 100644 --- a/Oqtane.Client/Services/SettingService.cs +++ b/Oqtane.Client/Services/SettingService.cs @@ -1,4 +1,5 @@ -using Oqtane.Models; +using System; +using Oqtane.Models; using System.Threading.Tasks; using System.Net.Http; using System.Linq; @@ -103,10 +104,10 @@ namespace Oqtane.Services public async Task UpdateSettingsAsync(Dictionary settings, string entityName, int entityId) { var settingsList = await GetJsonAsync>($"{Apiurl}?entityname={entityName}&entityid={entityId}"); - + foreach (KeyValuePair kvp in settings) { - Setting setting = settingsList.FirstOrDefault(item => item.SettingName == kvp.Key); + Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key,StringComparison.OrdinalIgnoreCase)); if (setting == null) { setting = new Setting(); diff --git a/Oqtane.Client/Services/SqlService.cs b/Oqtane.Client/Services/SqlService.cs index a99fbd23..719156b2 100644 --- a/Oqtane.Client/Services/SqlService.cs +++ b/Oqtane.Client/Services/SqlService.cs @@ -1,4 +1,5 @@ using Oqtane.Models; +using Oqtane.Shared; using System.Net.Http; using System.Threading.Tasks; @@ -6,9 +7,14 @@ namespace Oqtane.Services { public class SqlService : ServiceBase, ISqlService { - public SqlService(HttpClient http) : base(http) { } + private readonly SiteState _siteState; - private string Apiurl => CreateApiUrl("Sql"); + public SqlService(HttpClient http, SiteState siteState) : base(http) + { + _siteState = siteState; + } + + private string Apiurl => CreateApiUrl(_siteState.Alias, "Sql"); public async Task ExecuteQueryAsync(SqlQuery sqlquery) { diff --git a/Oqtane.Client/Services/TenantService.cs b/Oqtane.Client/Services/TenantService.cs index d8e9be02..d644348e 100644 --- a/Oqtane.Client/Services/TenantService.cs +++ b/Oqtane.Client/Services/TenantService.cs @@ -3,14 +3,20 @@ using System.Net.Http; using System.Threading.Tasks; using System.Collections.Generic; using System.Linq; +using Oqtane.Shared; namespace Oqtane.Services { public class TenantService : ServiceBase, ITenantService { - public TenantService(HttpClient http) : base(http) { } + private readonly SiteState _siteState; - private string Apiurl => CreateApiUrl("Tenant"); + public TenantService(HttpClient http, SiteState siteState) : base(http) + { + _siteState = siteState; + } + + private string Apiurl => CreateApiUrl(_siteState.Alias, "Tenant"); public async Task> GetTenantsAsync() { diff --git a/Oqtane.Client/Services/ThemeService.cs b/Oqtane.Client/Services/ThemeService.cs index e40e83dc..ddb47158 100644 --- a/Oqtane.Client/Services/ThemeService.cs +++ b/Oqtane.Client/Services/ThemeService.cs @@ -23,33 +23,6 @@ namespace Oqtane.Services public async Task> GetThemesAsync() { List themes = await GetJsonAsync>(Apiurl); - - // get list of loaded assemblies - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - - foreach (Theme theme in themes) - { - if (theme.Dependencies != "") - { - foreach (string dependency in theme.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) - { - string assemblyname = dependency.Replace(".dll", ""); - if (assemblies.Where(item => item.FullName.StartsWith(assemblyname + ",")).FirstOrDefault() == null) - { - // download assembly from server and load - var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{assemblyname}.dll"); - Assembly.Load(bytes); - } - } - } - if (assemblies.Where(item => item.FullName.StartsWith(theme.AssemblyName + ",")).FirstOrDefault() == null) - { - // download assembly from server and load - var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{theme.AssemblyName}.dll"); - Assembly.Load(bytes); - } - } - return themes.OrderBy(item => item.Name).ToList(); } diff --git a/Oqtane.Client/Themes/BlazorTheme/Container.razor b/Oqtane.Client/Themes/BlazorTheme/Container.razor index d286aef0..e62bc207 100644 --- a/Oqtane.Client/Themes/BlazorTheme/Container.razor +++ b/Oqtane.Client/Themes/BlazorTheme/Container.razor @@ -2,7 +2,9 @@ @inherits ContainerBase
-

+
+

+

diff --git a/Oqtane.Client/Themes/BlazorTheme/Default.razor b/Oqtane.Client/Themes/BlazorTheme/Default.razor index 7d9301e2..14b7df03 100644 --- a/Oqtane.Client/Themes/BlazorTheme/Default.razor +++ b/Oqtane.Client/Themes/BlazorTheme/Default.razor @@ -7,15 +7,7 @@ @@ -36,9 +28,8 @@ @code { public override string Panes => "Content"; - protected override async Task OnParametersSetAsync() + public override List Resources => new List() { - await IncludeCSS("Theme.css"); - } - + new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" } + }; } \ No newline at end of file diff --git a/Oqtane.Client/Themes/ContainerBase.cs b/Oqtane.Client/Themes/ContainerBase.cs index d55b718f..af4ff355 100644 --- a/Oqtane.Client/Themes/ContainerBase.cs +++ b/Oqtane.Client/Themes/ContainerBase.cs @@ -17,7 +17,6 @@ namespace Oqtane.Themes [CascadingParameter] protected Module ModuleState { get; set; } - public virtual string Name { get; set; } public string ThemePath() { diff --git a/Oqtane.Client/Themes/Controls/Breadcrumbs.razor b/Oqtane.Client/Themes/Controls/Breadcrumbs.razor index 7175f85d..310132f5 100644 --- a/Oqtane.Client/Themes/Controls/Breadcrumbs.razor +++ b/Oqtane.Client/Themes/Controls/Breadcrumbs.razor @@ -1,26 +1,33 @@ @namespace Oqtane.Themes.Controls -@inherits ThemeControlBase +@inherits ThemeControlBase +@attribute [OqtaneIgnore] @if (BreadCrumbPages.Any()) { - + + + } @code { protected IEnumerable BreadCrumbPages => GetBreadCrumbPages().Reverse().ToList(); - - protected string ActiveClass(Page page) - { - return (page.PageId == PageState.Page.PageId) ? " active" : string.Empty; - } private IEnumerable GetBreadCrumbPages() { diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index fe9e5486..e80ca085 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -1,6 +1,6 @@ @namespace Oqtane.Themes.Controls -@using Oqtane.Enums -@inherits ThemeControlBase +@inherits ThemeControlBase +@attribute [OqtaneIgnore] @inject NavigationManager NavigationManager @inject IUserService UserService @inject IModuleDefinitionService ModuleDefinitionService @@ -16,7 +16,7 @@
- Control Panel + Control Panel @@ -149,7 +149,7 @@