From 49e69d3c3e66ae15c8e7177b1220ee39d2732dff Mon Sep 17 00:00:00 2001 From: Emanuele Filardo Date: Sat, 8 Feb 2020 00:34:16 +0100 Subject: [PATCH 001/265] Fix .net core version in Upgrade project --- Oqtane.Upgrade/Oqtane.Upgrade.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Upgrade/Oqtane.Upgrade.csproj b/Oqtane.Upgrade/Oqtane.Upgrade.csproj index 0ddddf67..3e32c64c 100644 --- a/Oqtane.Upgrade/Oqtane.Upgrade.csproj +++ b/Oqtane.Upgrade/Oqtane.Upgrade.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 From b012a0dcaafc45771cc76e0c17d089bd3d6ebef9 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sat, 8 Feb 2020 17:35:27 +0100 Subject: [PATCH 002/265] Missing nullchecks -> unhandled exceptions --- Oqtane.Client/Shared/ModuleInstance.razor | 20 +++++++++++++------- Oqtane.Client/Shared/SiteRouter.razor | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Oqtane.Client/Shared/ModuleInstance.razor b/Oqtane.Client/Shared/ModuleInstance.razor index c6ae4a23..8d563405 100644 --- a/Oqtane.Client/Shared/ModuleInstance.razor +++ b/Oqtane.Client/Shared/ModuleInstance.razor @@ -39,17 +39,23 @@ if (typename != null) { moduleType = Type.GetType(typename); - } - if (moduleType != null) - { - builder.OpenComponent(0, moduleType); - builder.CloseComponent(); + + if (moduleType != null) + { + builder.OpenComponent(0, moduleType); + builder.CloseComponent(); + } + else + { + // module does not exist with typename specified + message = "Module Does Not Have A Component Named " + Utilities.GetTypeNameLastSegment(typename, 0) + ".razor"; + } } else { - // module does not exist with typename specified - message = "Module Does Not Have A Component Named " + Utilities.GetTypeNameLastSegment(typename, 0) + ".razor"; + message = "Something is wrong with moduletype"; } + }; } diff --git a/Oqtane.Client/Shared/SiteRouter.razor b/Oqtane.Client/Shared/SiteRouter.razor index 4b63f2b6..95d67998 100644 --- a/Oqtane.Client/Shared/SiteRouter.razor +++ b/Oqtane.Client/Shared/SiteRouter.razor @@ -408,7 +408,7 @@ if (module.PageId == pageid) { // ensure module's pane exists in current page and if not, assign it to the Admin pane - if (!panes.ToLower().Contains(module.Pane.ToLower())) + if (panes == null || !panes.ToLower().Contains(module.Pane.ToLower())) { module.Pane = Constants.AdminPane; } From c76ffa1714f5aed2a96fa3a2fe3d66b2a151df21 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sat, 8 Feb 2020 22:48:00 +0100 Subject: [PATCH 003/265] Save Site SMTP settings bug --- Oqtane.Client/Modules/Admin/Sites/Edit.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor index 42c0e108..2f2e8ae9 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor @@ -299,7 +299,7 @@ else SettingService.SetSetting(settings, "SMTPSSL", smtpssl); SettingService.SetSetting(settings, "SMTPUsername", smtpusername); SettingService.SetSetting(settings, "SMTPPassword", smtppassword); - await SettingService.UpdateModuleSettingsAsync(settings, site.SiteId); + await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId); await logger.LogInformation("Site Saved {Site}", site); From 6a92c9f7645faf950fc1a93731c1898c07039fc5 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 11 Feb 2020 14:25:38 -0500 Subject: [PATCH 004/265] Folder and file management service --- Oqtane.Client/Modules/Admin/Files/Add.razor | 128 +++++--- Oqtane.Client/Modules/Admin/Files/Edit.razor | 178 ++++++++--- Oqtane.Client/Modules/Admin/Files/Index.razor | 91 ++++-- .../Modules/Admin/ModuleDefinitions/Add.razor | 52 +--- Oqtane.Client/Modules/Admin/Pages/Add.razor | 2 +- Oqtane.Client/Modules/Admin/Sites/Add.razor | 11 +- Oqtane.Client/Modules/Admin/Sites/Edit.razor | 37 ++- Oqtane.Client/Modules/Admin/Themes/Add.razor | 52 +--- .../Modules/Admin/Upgrade/Index.razor | 54 +--- .../Modules/Admin/UserProfile/Index.razor | 35 ++- Oqtane.Client/Modules/Admin/Users/Add.razor | 15 + Oqtane.Client/Modules/Admin/Users/Edit.razor | 28 ++ .../Modules/Controls/FileManager.razor | 285 ++++++++++++++++++ .../Modules/Controls/FileUpload.razor | 54 ---- .../Modules/Controls/PermissionGrid.razor | 25 +- Oqtane.Client/Modules/ModuleBase.cs | 11 + Oqtane.Client/Services/FileService.cs | 53 +++- .../Services/Interfaces/IFileService.cs | 12 +- Oqtane.Client/Shared/Installer.razor | 6 +- Oqtane.Client/Shared/Interop.cs | 8 +- Oqtane.Client/Startup.cs | 3 +- .../Themes/Controls/ControlPanel.razor | 2 +- Oqtane.Client/Themes/Controls/Logo.razor | 4 +- Oqtane.Client/Themes/ThemeControlBase.cs | 11 + Oqtane.Client/wwwroot/js/interop.js | 12 +- .../Tenants/1/Sites/1/logo.png} | Bin Oqtane.Server/Controllers/FileController.cs | 192 ++++++++++-- Oqtane.Server/Controllers/FolderController.cs | 12 + Oqtane.Server/Controllers/SiteController.cs | 5 - Oqtane.Server/Controllers/UserController.cs | 18 +- Oqtane.Server/Repository/FileRepository.cs | 8 +- Oqtane.Server/Repository/FolderRepository.cs | 13 +- .../Interfaces/IFolderRepository.cs | 1 + Oqtane.Server/Repository/JobRepository.cs | 11 +- .../Repository/PageModuleRepository.cs | 4 +- Oqtane.Server/Repository/PageRepository.cs | 4 +- Oqtane.Server/Repository/SiteRepository.cs | 17 +- Oqtane.Server/Scripts/00.00.00.sql | 10 +- Oqtane.Server/Scripts/Master.sql | 2 +- Oqtane.Server/Startup.cs | 10 +- Oqtane.Server/wwwroot/js/interop.js | 12 +- Oqtane.Shared/Models/File.cs | 2 + Oqtane.Shared/Models/Folder.cs | 1 + Oqtane.Shared/Models/Site.cs | 2 +- Oqtane.Shared/Models/User.cs | 3 + 45 files changed, 1075 insertions(+), 421 deletions(-) create mode 100644 Oqtane.Client/Modules/Controls/FileManager.razor delete mode 100644 Oqtane.Client/Modules/Controls/FileUpload.razor rename Oqtane.Server/{wwwroot/Tenants/1/Sites/1/oqtane.png => Content/Tenants/1/Sites/1/logo.png} (100%) diff --git a/Oqtane.Client/Modules/Admin/Files/Add.razor b/Oqtane.Client/Modules/Admin/Files/Add.razor index 26feaf57..fa041fed 100644 --- a/Oqtane.Client/Modules/Admin/Files/Add.razor +++ b/Oqtane.Client/Modules/Admin/Files/Add.razor @@ -2,55 +2,109 @@ @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService +@inject IFolderService FolderService - - - - - -
- - - -
- -Cancel +@if (folders != null) +{ +
+
+ + + +
+
+ + + + + +
+ + + +
+
+
+ + + + + + + + + +
+ + + +
+ + + +
+ +
+
+
+
+
+ Cancel +} @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - FileUpload fileupload; + string url = ""; + List folders; + int folderid = -1; - private async Task UploadFile() + protected override async Task OnInitializedAsync() { - string[] files = await fileupload.GetFiles(); - if (files.Length > 0) - { - try - { - ShowProgressIndicator(); + folders = await FolderService.GetFoldersAsync(ModuleState.SiteId); - string result = await FileService.UploadFilesAsync(PageState.Site.SiteRootPath, files, ""); - if (result == "") - { - await logger.LogInformation("Files Uploaded Successfully"); - AddModuleMessage("Files Uploaded Successfully", MessageType.Success); - } - else - { - await logger.LogError("Upload Failed For {Files}", result.Replace(",",", ")); - AddModuleMessage("Upload Failed For " + result.Replace(",",", ") + ". This Could Be Due To A Network Error Or Because A File Type Is Restricted.", MessageType.Error); - } - } - catch (Exception ex) + if (PageState.QueryString.ContainsKey("id")) + { + folderid = int.Parse(PageState.QueryString["id"]); + } + } + + private async Task Download() + { + try + { + if (url != "" && folderid != -1) { - await logger.LogError(ex, "Upload Failed {Error}", ex.Message); - AddModuleMessage("Upload Failed. " + ex.Message, MessageType.Error); + 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); } } - else + catch (Exception ex) { - AddModuleMessage("You Must Select Some Files To Upload", MessageType.Warning); + await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", url, ex.Message); + AddModuleMessage("Error Downloading File From Url. Please Verify That The Url Is Valid.", MessageType.Error); } } } diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index 8ad0f1ca..52ebeff1 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -3,36 +3,70 @@ @inject IFolderService FolderService @inject NavigationManager NavigationManager - - - - - - - - - -
- - - -
- - - -
- -Cancel -
-
- +@if (folders != null) +{ + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ @if (!issystem) + { + + } + Cancel + @if (!issystem && PageState.QueryString.ContainsKey("id")) + { + + } +
+
+ @if (PageState.QueryString.ContainsKey("id")) + { + + } +} @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } + public override string Title { get { return "Folder Management"; } } - int FolderId; + List folders; + int folderid; string name; - string permissions; + int parentid = -1; + bool issystem = false; + string permissions = ""; string createdby; DateTime createdon; string modifiedby; @@ -44,22 +78,38 @@ { try { - FolderId = Int32.Parse(PageState.QueryString["id"]); - Folder folder = await FolderService.GetFolderAsync(FolderId); - if (folder != null) + folders = await FolderService.GetFoldersAsync(PageState.Site.SiteId); + + if (PageState.QueryString.ContainsKey("id")) { - name = folder.Name; - permissions = folder.Permissions; - createdby = folder.CreatedBy; - createdon = folder.CreatedOn; - modifiedby = folder.ModifiedBy; - modifiedon = folder.ModifiedOn; + folderid = Int32.Parse(PageState.QueryString["id"]); + Folder folder = await FolderService.GetFolderAsync(folderid); + if (folder != null) + { + parentid = (folder.ParentId == null) ? -1 : folder.ParentId.Value; + name = folder.Name; + issystem = folder.IsSystem; + permissions = folder.Permissions; + createdby = folder.CreatedBy; + createdon = folder.CreatedOn; + modifiedby = folder.ModifiedBy; + modifiedon = folder.ModifiedOn; + } + } + else + { + parentid = folders[0].FolderId; + List permissionstrings = new List(); + permissionstrings.Add(new PermissionString { PermissionName = "Browse", Permissions = Constants.AdminRole }); + permissionstrings.Add(new PermissionString { PermissionName = "View", Permissions = Constants.AdminRole }); + permissionstrings.Add(new PermissionString { PermissionName = "Edit", Permissions = Constants.AdminRole }); + permissions = UserSecurity.SetPermissionStrings(permissionstrings); } } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Folder {FolderId} {Error}", FolderId, ex.Message); - AddModuleMessage("Error Loading Module", MessageType.Error); + await logger.LogError(ex, "Error Loading Folder {FolderId} {Error}", folderid, ex.Message); + AddModuleMessage("Error Loading Folder", MessageType.Error); } } @@ -67,19 +117,67 @@ { try { - Folder folder = await FolderService.GetFolderAsync(FolderId); - if (folder != null) + if (name != "" && parentid != -1) { + Folder folder; + if (folderid != -1) + { + folder = await FolderService.GetFolderAsync(folderid); + } + else + { + folder = new Folder(); + } + + folder.SiteId = PageState.Site.SiteId; + if (parentid == -1) + { + folder.ParentId = null; + } + else + { + folder.ParentId = parentid; + } + folder.Name = name; + folder.IsSystem = issystem; folder.Permissions = permissiongrid.GetPermissions(); - await FolderService.UpdateFolderAsync(folder); + + if (folderid != -1) + { + folder = await FolderService.UpdateFolderAsync(folder); + } + else + { + folder = await FolderService.AddFolderAsync(folder); + } + await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); await logger.LogInformation("Folder Saved {Folder}", folder); NavigationManager.NavigateTo(NavigateUrl(Reload.Site)); } + else + { + AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning); + } } catch (Exception ex) { - await logger.LogError(ex, "Error Saving Folder {FolderId} {Error}", FolderId, ex.Message); + await logger.LogError(ex, "Error Saving Folder {FolderId} {Error}", folderid, ex.Message); AddModuleMessage("Error Saving Module", MessageType.Error); } } + + private async Task DeleteFolder() + { + try + { + await FolderService.DeleteFolderAsync(folderid); + await logger.LogInformation("Folder Deleted {Folder}", folderid); + AddModuleMessage("Folder Deleted", MessageType.Success); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting Folder {Folder} {Error}", folderid, ex.Message); + AddModuleMessage("Error Deleting Folder", MessageType.Error); + } + } } diff --git a/Oqtane.Client/Modules/Admin/Files/Index.razor b/Oqtane.Client/Modules/Admin/Files/Index.razor index 35269a70..677b117b 100644 --- a/Oqtane.Client/Modules/Admin/Files/Index.razor +++ b/Oqtane.Client/Modules/Admin/Files/Index.razor @@ -1,39 +1,71 @@ @namespace Oqtane.Modules.Admin.Files @inherits ModuleBase @inject NavigationManager NavigationManager +@inject IFolderService FolderService @inject IFileService FileService -@if (Files == null) +@if (Files != null) { -

Loading...

-} -else -{ - - + + + + + + +
+ + + + +   +   + +
  Name + Modified + Type + Size
- - @context + + @context.Name + @context.ModifiedOn + @context.Extension.ToUpper() File + @(context.Size / 1000) KB
+ @if (Files.Count == 0) + { +
No Files Exist In Selected Folder
+ } } @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - List Files; + List Folders; + int folderid; + List Files; Uri uri; protected override async Task OnParametersSetAsync() { try { - Files = await FileService.GetFilesAsync(PageState.Site.SiteRootPath); + Folders = await FolderService.GetFoldersAsync(PageState.Site.SiteId); + if (Folders.Count > 0) + { + folderid = Folders[0].FolderId; + await GetFiles(); + } uri = new Uri(NavigationManager.Uri); } catch (Exception ex) @@ -43,19 +75,40 @@ else } } - private async Task DeleteFile(string filename) + private async Task GetFiles() + { + Files = await FileService.GetFilesAsync(folderid); + } + + private async void FolderChanged(ChangeEventArgs e) { try { - await FileService.DeleteFileAsync(PageState.Site.SiteRootPath, filename); - Files = await FileService.GetFilesAsync(PageState.Site.SiteRootPath); - await logger.LogInformation("File Deleted {File}", filename); - AddModuleMessage("File " + filename + " Deleted", MessageType.Success); + folderid = int.Parse((string)e.Value); + await GetFiles(); + StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Deleting File {File} {Error}", filename, ex.Message); - AddModuleMessage("Error Deleting File " + filename, MessageType.Error); + await logger.LogError(ex, "Error Loading Files {Error}", ex.Message); + AddModuleMessage("Error Loading Files", MessageType.Error); } } -} \ No newline at end of file + + private async Task DeleteFile(File file) + { + try + { + await FileService.DeleteFileAsync(file.FileId); + await logger.LogInformation("File Deleted {File}", file.Name); + AddModuleMessage("File " + file.Name + " Deleted", MessageType.Success); + await GetFiles(); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting File {File} {Error}", file.Name, ex.Message); + AddModuleMessage("Error Deleting File " + file.Name, MessageType.Error); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index e234650f..4e534cc7 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -11,11 +11,10 @@ - + - @if (packages != null) { @@ -38,19 +37,14 @@ } -@if (uploaded) -{ - -} + Cancel @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - bool uploaded = false; List packages; - FileUpload fileupload; protected override async Task OnInitializedAsync() { @@ -73,47 +67,6 @@ } } - private async Task UploadFile() - { - string[] files = await fileupload.GetFiles(); - if (files.Length > 0) - { - if (files[0].Contains(".Module.")) - { - try - { - string result = await FileService.UploadFilesAsync("Modules", files, ""); - if (result == "") - { - await logger.LogInformation("Module Uploaded Successfully {Package}", files[0]); - AddModuleMessage("Module Uploaded Successfully. Click Install To Complete Installation.", MessageType.Success); - uploaded = true; - StateHasChanged(); - } - else - { - await logger.LogError("Module Upload Failed For {Package}", files[0]); - AddModuleMessage("Module Upload Failed For " + result.Replace(",",", ") + ". This Could Be Due To A Network Error Or Because A File Type Is Restricted.", MessageType.Error); - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Module Upload Failed For {Package} {Error}", files[0], ex.Message); - AddModuleMessage("Module Upload Failed.", MessageType.Error); - } - } - else - { - await logger.LogError("Invalid Module Package {Package}", files[0]); - AddModuleMessage("Invalid Module Package", MessageType.Error); - } - } - else - { - AddModuleMessage("You Must Select A Module To Upload", MessageType.Warning); - } - } - private async Task InstallModules() { try @@ -134,7 +87,6 @@ await PackageService.DownloadPackageAsync(moduledefinitionname, version, "Modules"); await logger.LogInformation("Module {ModuleDefinitionName} {Version} Downloaded Successfully", moduledefinitionname, version); AddModuleMessage("Module Downloaded Successfully. Click Install To Complete Installation.", MessageType.Success); - uploaded = true; StateHasChanged(); } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index fe217df0..9b642b02 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -161,7 +161,7 @@ string themetype = ""; string layouttype = ""; string icon = ""; - string permissions = ""; // need to set default permissions + string permissions = ""; PermissionGrid permissiongrid; diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 5c1532cb..94face09 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -49,7 +49,7 @@ else - + @@ -129,7 +129,7 @@ else string tenantid = "-1"; string name = ""; string urls = ""; - string logo = ""; + FileManager filemanager; string themetype = ""; string layouttype = ""; string containertype = ""; @@ -223,7 +223,12 @@ else Site site = new Site(); site.TenantId = int.Parse(tenantid); site.Name = name; - site.Logo = (logo == null ? "" : logo); + site.LogoFileId = null; + int logofileid = filemanager.GetFileId(); + if (logofileid != -1) + { + site.LogoFileId = logofileid; + } site.DefaultThemeType = themetype; site.DefaultLayoutType = (layouttype == null ? "" : layouttype); site.DefaultContainerType = containertype; diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor index 42c0e108..34fda2bf 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor @@ -6,12 +6,8 @@ @inject IThemeService ThemeService @inject ISettingService SettingService -@if (themes == null) -{ -

Loading...

-} -else -{ +@if (themes != null) +{ @@ -157,16 +153,17 @@ else @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - Dictionary themes = new Dictionary(); - Dictionary panelayouts = new Dictionary(); - Dictionary containers = new Dictionary(); + Dictionary themes; + Dictionary panelayouts; + Dictionary containers; Alias Alias; int siteid; string name = ""; List aliases; string urls = ""; - string logo = ""; + int logofileid = -1; + FileManager filemanager; string themetype; string layouttype; string containertype; @@ -189,9 +186,6 @@ else { try { - themes = ThemeService.GetThemeTypes(PageState.Themes); - containers = ThemeService.GetContainerTypes(PageState.Themes); - Alias = PageState.Aliases.Where(item => item.AliasId == Int32.Parse(PageState.QueryString["id"])).FirstOrDefault(); siteid = Alias.SiteId; Site site = await SiteService.GetSiteAsync(siteid, Alias); @@ -203,7 +197,10 @@ else { urls += alias.Name + "\n"; } - logo = site.Logo; + if (site.LogoFileId != null) + { + logofileid = site.LogoFileId.Value; + } themetype = site.DefaultThemeType; panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype); layouttype = site.DefaultLayoutType; @@ -224,6 +221,9 @@ else deletedon = site.DeletedOn; isdeleted = site.IsDeleted.ToString(); } + + themes = ThemeService.GetThemeTypes(PageState.Themes); + containers = ThemeService.GetContainerTypes(PageState.Themes); } catch (Exception ex) { @@ -264,7 +264,12 @@ else if (site != null) { site.Name = name; - site.Logo = (logo == null ? "" : logo); + site.LogoFileId = null; + int logofileid = filemanager.GetFileId(); + if (logofileid != -1) + { + site.LogoFileId = logofileid; + } site.DefaultThemeType = themetype; site.DefaultLayoutType = (layouttype == null ? "" : layouttype); site.DefaultContainerType = containertype; diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor index bd243c9e..5ea8f8c5 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Add.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -11,11 +11,10 @@
@@ -34,7 +30,7 @@ else - +
- +
- @if (packages != null) { @@ -38,18 +37,13 @@ } -@if (uploaded) -{ - -} + Cancel @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - bool uploaded = false; List packages; - FileUpload fileupload; protected override async Task OnInitializedAsync() { @@ -64,47 +58,6 @@ } } - private async Task UploadTheme() - { - string[] files = await fileupload.GetFiles(); - if (files.Length > 0) - { - if (files[0].Contains(".Theme.")) - { - try - { - string result = await FileService.UploadFilesAsync("Themes", files, ""); - if (result == "") - { - await logger.LogInformation("Theme Uploaded {Package}", files[0]); - AddModuleMessage("Theme Uploaded Successfully. Click Install To Complete Installation.", MessageType.Success); - uploaded = true; - StateHasChanged(); - } - else - { - await logger.LogInformation("Theme Upload Failed For {Package}", result.Replace(",",", ")); - AddModuleMessage("Upload Failed For " + result.Replace(",",", ") + ". This Could Be Due To A Network Error Or Because A File Type Is Restricted.", MessageType.Error); - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Theme Upload Failed {Package} {Error}", files[0], ex.Message); - AddModuleMessage("Theme Upload Failed", MessageType.Error); - } - } - else - { - await logger.LogError("Invalid Theme Package {Package}", files[0]); - AddModuleMessage("Invalid Theme Package", MessageType.Error); - } - } - else - { - AddModuleMessage("You Must Select A Theme To Upload", MessageType.Warning); - } - } - private async Task InstallThemes() { await ThemeService.InstallThemesAsync(); @@ -115,7 +68,6 @@ { await PackageService.DownloadPackageAsync(packageid, version, "Themes"); AddModuleMessage("Theme Downloaded Successfully. Click Install To Complete Installation.", MessageType.Success); - uploaded = true; StateHasChanged(); } } diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index 2ae44820..7448f435 100644 --- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor +++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor @@ -11,18 +11,11 @@ - + -@if (uploaded) -{ - -} -else -{ - -} + @if (upgradeavailable) { @@ -35,9 +28,7 @@ else @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - bool uploaded = false; bool upgradeavailable = false; - FileUpload fileupload; protected override async Task OnInitializedAsync() { @@ -53,47 +44,6 @@ else } } - private async Task UploadFile() - { - string[] files = await fileupload.GetFiles(); - if (files.Length > 0) - { - if (files[0].Contains(".Framework.")) - { - try - { - string result = await FileService.UploadFilesAsync("Framework", files, ""); - if (result == "") - { - await logger.LogInformation("Framework Uploaded {Package}", files[0]); - AddModuleMessage("Framework Uploaded Successfully. Click Upgrade To Complete Installation.", MessageType.Success); - uploaded = true; - StateHasChanged(); - } - else - { - await logger.LogInformation("Framework Upload Failed For {Package}", result.Replace(",",", ")); - AddModuleMessage("Upload Failed For " + result.Replace(",",", ") + ". This Could Be Due To A Network Error Or Because A File Type Is Restricted.", MessageType.Error); - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Framework Upload Failed {Package} {Error}", files[0], ex.Message); - AddModuleMessage("Framework Upload Failed. " + ex.Message, MessageType.Error); - } - } - else - { - await logger.LogError("Invalid Framework Package {Package}", files[0]); - AddModuleMessage("Invalid Framework Package", MessageType.Error); - } - } - else - { - AddModuleMessage("You Must Select A Framework Package To Upload", MessageType.Warning); - } - } - private async Task Upgrade() { await InstallationService.Upgrade(); diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index b5d76c3c..e0d8cedd 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -26,7 +26,14 @@
-
+ @if (photofileid != -1) + { + @displayname + } + else + { +
+ } + + + + @foreach (Profile profile in profiles) { @@ -164,6 +179,8 @@ string confirm = ""; string email = ""; string displayname = ""; + FileManager filemanager; + int photofileid = -1; List profiles; Dictionary settings; string category = ""; @@ -179,6 +196,10 @@ username = PageState.User.Username; email = PageState.User.Email; displayname = PageState.User.DisplayName; + if (PageState.User.PhotoFileId != null) + { + photofileid = PageState.User.PhotoFileId.Value; + } profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); await LoadNotificationsAsync(); @@ -210,7 +231,7 @@ { try { - if (password != "" && confirm != "" && email != "") + if (username != "" && email != "") { if (password == confirm) { @@ -219,11 +240,15 @@ user.Password = password; user.Email = email; user.DisplayName = (displayname == "" ? username : displayname); + user.PhotoFileId = null; + photofileid = filemanager.GetFileId(); + if (photofileid != -1) + { + user.PhotoFileId = photofileid; + } await UserService.UpdateUserAsync(user); await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); await logger.LogInformation("User Profile Saved"); - - NavigationManager.NavigateTo(NavigateUrl("")); } else { @@ -232,7 +257,7 @@ } else { - AddModuleMessage("You Must Provide A Username, Password, and Email Address", MessageType.Warning); + AddModuleMessage("You Must Provide A Username and Email Address", MessageType.Warning); } } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor index a687cad4..781e958f 100644 --- a/Oqtane.Client/Modules/Admin/Users/Add.razor +++ b/Oqtane.Client/Modules/Admin/Users/Add.razor @@ -48,6 +48,14 @@ + + + + @foreach (Profile profile in profiles) { @@ -83,6 +91,7 @@ string confirm = ""; string email = ""; string displayname = ""; + FileManager filemanager; List profiles; Dictionary settings; string category = ""; @@ -115,6 +124,12 @@ user.Password = password; user.Email = email; user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; + user.PhotoFileId = null; + int photofileid = filemanager.GetFileId(); + if (photofileid != -1) + { + user.PhotoFileId = photofileid; + } user = await UserService.AddUserAsync(user); diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index c3a005de..e8c40d2d 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -7,6 +7,14 @@ @if (profiles != null) { + @if (photofileid != -1) + { + @displayname + } + else + { +
+ }
@@ -68,6 +75,14 @@
+ + + +
+ + + +
+ + + + @foreach (Profile profile in profiles) { @@ -98,6 +114,8 @@ string confirm = ""; string email = ""; string displayname = ""; + FileManager filemanager; + int photofileid = -1; List profiles; Dictionary settings; string category = ""; @@ -122,6 +140,10 @@ username = user.Username; email = user.Email; displayname = user.DisplayName; + if (user.PhotoFileId != null) + { + photofileid = user.PhotoFileId.Value; + } settings = await SettingService.GetUserSettingsAsync(user.UserId); createdby = user.CreatedBy; createdon = user.CreatedOn; @@ -158,6 +180,12 @@ user.Password = password; user.Email = email; user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; + user.PhotoFileId = null; + photofileid = filemanager.GetFileId(); + if (photofileid != -1) + { + user.PhotoFileId = photofileid; + } user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted)); user = await UserService.UpdateUserAsync(user); diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor new file mode 100644 index 00000000..d61e05c2 --- /dev/null +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -0,0 +1,285 @@ +@namespace Oqtane.Modules.Controls +@inherits ModuleBase +@inject IFolderService FolderService +@inject IFileService FileService +@inject IJSRuntime jsRuntime + +@if (folders != null) +{ + + @if (showfiles) + { + + } + @if (haseditpermission) + { +
+ @if (uploadmultiple) + { + + } + else + { + + } + + @if (showfiles && GetFileId() != -1) + { + + } + +
+ @((MarkupString)@message) + } +} + +@code { + [Parameter] + 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 + + [Parameter] + public string ShowFiles { get; set; } // 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 + + [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 + + string id; + List folders; + int folderid = -1; + List files = new List(); + int fileid = -1; + bool showfiles = true; + string fileinputid = ""; + string progressinfoid = ""; + string progressbarid = ""; + string filter = "*"; + bool uploadmultiple = false; + bool haseditpermission = false; + string message = ""; + + protected override async Task OnInitializedAsync() + { + if (!string.IsNullOrEmpty(Folder)) + { + folders = new List(); + folders.Add(new Folder { FolderId = -1, Name = Folder }); + folderid = -1; + } + else + { + folders = await FolderService.GetFoldersAsync(ModuleState.SiteId); + if (!string.IsNullOrEmpty(FolderId)) + { + folderid = int.Parse(FolderId); + } + } + + if (!string.IsNullOrEmpty(FileId)) + { + fileid = int.Parse(FileId); + if (fileid != -1) + { + File file = await FileService.GetFileAsync(int.Parse(FileId)); + if (file != null) + { + folderid = file.FolderId; + } + } + } + if (!string.IsNullOrEmpty(ShowFiles)) + { + showfiles = bool.Parse(ShowFiles); + } + + if (!string.IsNullOrEmpty(Filter)) + { + filter = Filter; + } + + await GetFiles(); + + // create unique id for component + id = Guid.NewGuid().ToString("N"); + fileinputid = id + "FileInput"; + progressinfoid = id + "ProgressInfo"; + progressbarid = id + "ProgressBar"; + + if (!string.IsNullOrEmpty(UploadMultiple)) + { + uploadmultiple = bool.Parse(UploadMultiple); + } + } + + private async Task GetFiles() + { + haseditpermission = false; + if (!string.IsNullOrEmpty(Folder)) + { + haseditpermission = UserSecurity.IsAuthorized(PageState.User, Constants.HostRole); + files = await FileService.GetFilesAsync(Folder); + } + else + { + Folder folder = folders.Where(item => item.FolderId == folderid).FirstOrDefault(); + if (folder != null) + { + haseditpermission = UserSecurity.IsAuthorized(PageState.User, "Edit", folder.Permissions); + files = await FileService.GetFilesAsync(folderid); + } + else + { + haseditpermission = false; + files = new List(); + } + } + if (filter != "*") + { + List filtered = new List(); + foreach (File file in files) + { + if (filter.ToUpper().IndexOf("." + file.Extension.ToUpper()) != -1) + { + filtered.Add(file); + } + } + files = filtered; + } + } + + private async void FolderChanged(ChangeEventArgs e) + { + message = ""; + try + { + folderid = int.Parse((string)e.Value); + await GetFiles(); + fileid = -1; + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Files {Error}", ex.Message); + message = "
Error Loading Files
"; + } + } + + private void FileChanged(ChangeEventArgs e) + { + message = ""; + fileid = int.Parse((string)e.Value); + StateHasChanged(); + } + + private async Task UploadFile() + { + var interop = new Interop(jsRuntime); + string[] upload = await interop.GetFiles(fileinputid); + if (upload.Length > 0) + { + try + { + string result; + if (!string.IsNullOrEmpty(Folder)) + { + result = await FileService.UploadFilesAsync(Folder, upload, id); + } + else + { + result = await FileService.UploadFilesAsync(folderid, upload, id); + } + if (result == "") + { + await logger.LogInformation("File Upload Succeeded {Files}", upload); + message = "
File Upload Succeeded
"; + await GetFiles(); + if (upload.Length == 1) + { + File file = files.Where(item => item.Name == upload[0]).FirstOrDefault(); + if (file != null) + { + fileid = file.FileId; + } + } + StateHasChanged(); + } + else + { + await logger.LogError("File Upload Failed For {Files}", result.Replace(",", ", ")); + message = "
File Upload Failed
"; + } + } + catch (Exception ex) + { + await logger.LogError(ex, "File Upload Failed {Error}", ex.Message); + message = "
File Upload Failed
"; + } + } + else + { + message = "
You Have Not Selected A File To Upload
"; + } + } + + private async Task DeleteFile() + { + message = ""; + try + { + await FileService.DeleteFileAsync(fileid); + await logger.LogInformation("File Deleted {File}", fileid); + message = "
File Deleted
"; + await GetFiles(); + fileid = -1; + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting File {File} {Error}", fileid, ex.Message); + message = "
Error Deleting File
"; + } + } + + public int GetFileId() + { + return fileid; + } + +} diff --git a/Oqtane.Client/Modules/Controls/FileUpload.razor b/Oqtane.Client/Modules/Controls/FileUpload.razor deleted file mode 100644 index 43f0228a..00000000 --- a/Oqtane.Client/Modules/Controls/FileUpload.razor +++ /dev/null @@ -1,54 +0,0 @@ -@namespace Oqtane.Modules.Controls -@inject IJSRuntime jsRuntime - -@if (multiple) -{ - -} -else -{ - -} - - -@code { - [Parameter] - public string Name { get; set; } // optional - can be used for managing multiple file upload controls on a page - - [Parameter] - public string Filter { get; set; } // optional - for restricting types of files that can be selected - - [Parameter] - public string Multiple { get; set; } // optional - enable multiple file uploads - - string fileid = ""; - string progressinfoid = ""; - string progressbarid = ""; - string filter = "*"; - string files = ""; - bool multiple = false; - - protected override void OnInitialized() - { - fileid = Name + "FileInput"; - progressinfoid = Name + "ProgressInfo"; - progressbarid = Name + "ProgressBar"; - - if (!string.IsNullOrEmpty(Filter)) - { - filter = Filter; - } - - if (!string.IsNullOrEmpty(Multiple)) - { - multiple = bool.Parse(Multiple); - } - } - - public async Task GetFiles() - { - var interop = new Interop(jsRuntime); - string[] files = await interop.GetFiles(fileid); - return files; - } -} diff --git a/Oqtane.Client/Modules/Controls/PermissionGrid.razor b/Oqtane.Client/Modules/Controls/PermissionGrid.razor index 2345eb37..fda498e1 100644 --- a/Oqtane.Client/Modules/Controls/PermissionGrid.razor +++ b/Oqtane.Client/Modules/Controls/PermissionGrid.razor @@ -107,22 +107,25 @@ { permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = "" }); } - foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions)) + if (Permissions != "") { - if (permissions.Find(item => item.PermissionName == permissionstring.PermissionName) != null) + foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions)) { - permissions[permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions; - } - if (permissionstring.Permissions.Contains("[")) - { - foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries)) + if (permissions.Find(item => item.PermissionName == permissionstring.PermissionName) != null) { - if (user.Contains("]")) + permissions[permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions; + } + if (permissionstring.Permissions.Contains("[")) + { + foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries)) { - int userid = int.Parse(user.Substring(0, user.IndexOf("]"))); - if (users.Where(item => item.UserId == userid).FirstOrDefault() == null) + if (user.Contains("]")) { - users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId)); + int userid = int.Parse(user.Substring(0, user.IndexOf("]"))); + if (users.Where(item => item.UserId == userid).FirstOrDefault() == null) + { + users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId)); + } } } } diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 397daa2b..c2ae550e 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -109,6 +109,17 @@ namespace Oqtane.Modules return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters); } + public string ContentUrl(int fileid) + { + string apiurl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/"; + if (PageState.Alias.Path == "") + { + apiurl += "~/"; + } + apiurl += "api/File/Download/" + fileid.ToString(); + return apiurl; + } + // user feedback methods public void AddModuleMessage(string message, MessageType type) { diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index 240bb144..f706756f 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using Oqtane.Models; using Oqtane.Shared; namespace Oqtane.Services @@ -28,17 +30,52 @@ namespace Oqtane.Services get { return CreateApiUrl(sitestate.Alias, NavigationManager.Uri, "File"); } } - public async Task> GetFilesAsync(string Folder) + public async Task> GetFilesAsync(int FolderId) { - return await http.GetJsonAsync>(apiurl + "?folder=" + Folder); + return await GetFilesAsync(FolderId.ToString()); } - public async Task UploadFilesAsync(string Folder, string[] Files, string FileUploadName) + public async Task> GetFilesAsync(string Folder) + { + return await http.GetJsonAsync>(apiurl + "?folder=" + Folder); + } + + public async Task GetFileAsync(int FileId) + { + return await http.GetJsonAsync(apiurl + "/" + FileId.ToString()); + } + + public async Task AddFileAsync(File File) + { + return await http.PostJsonAsync(apiurl, File); + } + + public async Task UpdateFileAsync(File File) + { + return await http.PutJsonAsync(apiurl + "/" + File.FileId.ToString(), File); + } + + public async Task DeleteFileAsync(int FileId) + { + await http.DeleteAsync(apiurl + "/" + FileId.ToString()); + } + + public async Task UploadFileAsync(string Url, int FolderId) + { + return await http.GetJsonAsync(apiurl + "/upload?url=" + WebUtility.UrlEncode(Url) + "&folderid=" + FolderId.ToString()); + } + + public async Task UploadFilesAsync(int FolderId, string[] Files, string Id) + { + return await UploadFilesAsync(FolderId.ToString(), Files, Id); + } + + public async Task UploadFilesAsync(string Folder, string[] Files, string Id) { string result = ""; var interop = new Interop(jsRuntime); - await interop.UploadFiles(apiurl + "/upload", Folder, FileUploadName); + await interop.UploadFiles(apiurl + "/upload", Folder, Id); // uploading files is asynchronous so we need to wait for the upload to complete bool success = false; @@ -48,13 +85,13 @@ namespace Oqtane.Services Thread.Sleep(2000); // wait 2 seconds result = ""; - List files = await GetFilesAsync(Folder); + List files = await GetFilesAsync(Folder); if (files.Count > 0) { success = true; foreach (string file in Files) { - if (!files.Contains(file)) + if (!files.Exists(item => item.Name == file)) { success = false; result += file + ","; @@ -71,9 +108,9 @@ namespace Oqtane.Services return result; } - public async Task DeleteFileAsync(string Folder, string File) + public async Task DownloadFileAsync(int FileId) { - await http.DeleteAsync(apiurl + "?folder=" + Folder + "&file=" + File); + return await http.GetByteArrayAsync(apiurl + "/download/" + FileId.ToString()); } } } diff --git a/Oqtane.Client/Services/Interfaces/IFileService.cs b/Oqtane.Client/Services/Interfaces/IFileService.cs index b840397b..2a6234b6 100644 --- a/Oqtane.Client/Services/Interfaces/IFileService.cs +++ b/Oqtane.Client/Services/Interfaces/IFileService.cs @@ -6,8 +6,16 @@ namespace Oqtane.Services { public interface IFileService { - Task> GetFilesAsync(string Folder); + Task> GetFilesAsync(int FolderId); + Task> GetFilesAsync(string Folder); + Task GetFileAsync(int FileId); + Task AddFileAsync(File File); + Task UpdateFileAsync(File File); + Task DeleteFileAsync(int FileId); + Task UploadFileAsync(string Url, int FolderId); + Task UploadFilesAsync(int FolderId, string[] Files, string FileUploadName); Task UploadFilesAsync(string Folder, string[] Files, string FileUploadName); - Task DeleteFileAsync(string Folder, string File); + Task DownloadFileAsync(int FileId); + } } diff --git a/Oqtane.Client/Shared/Installer.razor b/Oqtane.Client/Shared/Installer.razor index b4962476..477384f4 100644 --- a/Oqtane.Client/Shared/Installer.razor +++ b/Oqtane.Client/Shared/Installer.razor @@ -98,7 +98,7 @@
- - - - @foreach (Profile profile in profiles) { @@ -91,7 +83,6 @@ string confirm = ""; string email = ""; string displayname = ""; - FileManager filemanager; List profiles; Dictionary settings; string category = ""; @@ -125,11 +116,6 @@ user.Email = email; user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; user.PhotoFileId = null; - int photofileid = filemanager.GetFileId(); - if (photofileid != -1) - { - user.PhotoFileId = photofileid; - } user = await UserService.AddUserAsync(user); diff --git a/screenshot6.png b/screenshot6.png new file mode 100644 index 0000000000000000000000000000000000000000..df2066522ae978ff3224ca0a272458103ffc0959 GIT binary patch literal 69541 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 literal 0 HcmV?d00001 From dd3523eedd340257b694b8240cb5a6806cb5167f Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 12 Feb 2020 09:19:05 -0500 Subject: [PATCH 006/265] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e719bd89..09989025 100644 --- a/README.md +++ b/README.md @@ -55,3 +55,6 @@ Control panel for adding, editing, and deleting pages as well as adding new modu ![Manage Page](https://github.com/oqtane/framework/blob/master/screenshot5.png?raw=true "Manage Page") +Admin dashboard for accessing the variuous administrative features of the framework: + +![Admin Dashboard](https://github.com/oqtane/framework/blob/master/screenshot6.png?raw=true "Admin Dashboard") From fea799657b5b369ec2d6fb316cd3d79b8d2cca42 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 12 Feb 2020 09:34:30 -0500 Subject: [PATCH 007/265] Update README.md --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 09989025..69579d51 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,27 @@ Oqtane uses Blazor, a new web framework for .NET Core that lets you build intera # Roadmap This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist. +V1 (MVP) +- Multi-Tenant ( Shared Database & Isolated Database ) +- Modular Architecture / Headless API +- Dynamic Page Compositing Model / Site & Page Management +- Authentication / User Management / Profile Management +- Authorization / Roles Management / Granular Permissions +- Dynamic Routing +- Extensibility via Custom Modules +- Extensibility via Custom Themes +- Event Logging +- Folder / File Management +- Recycle Bin +- Scheduled Jobs ( Background Processing ) +- Notifications / Email Delivery +- Auto-Upgrade Framework + +V.Next +- Localization +- Migrate to Code-Behind Pattern ( *.razor.cs ) +- Generic Repository Pattern + # Background Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalker/) and is inspired by the DotNetNuke web application framework. Initially created as a proof of concept, Oqtane is a native Blazor application written from the ground up using modern .NET Core technology. It is a modular application framework offering a fully dynamic page compositing model, multi-site support, designer friendly templates (skins), and extensibility via third party modules. From 77ea4cb129bde9cf21e9c504bbf4fb48c21453f2 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 12 Feb 2020 09:38:55 -0500 Subject: [PATCH 008/265] Update README.md --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 69579d51..c941481d 100644 --- a/README.md +++ b/README.md @@ -19,20 +19,20 @@ Oqtane uses Blazor, a new web framework for .NET Core that lets you build intera This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist. V1 (MVP) -- Multi-Tenant ( Shared Database & Isolated Database ) -- Modular Architecture / Headless API -- Dynamic Page Compositing Model / Site & Page Management -- Authentication / User Management / Profile Management -- Authorization / Roles Management / Granular Permissions -- Dynamic Routing -- Extensibility via Custom Modules -- Extensibility via Custom Themes -- Event Logging -- Folder / File Management -- Recycle Bin -- Scheduled Jobs ( Background Processing ) -- Notifications / Email Delivery -- Auto-Upgrade Framework +- Multi-Tenant ( Shared Database & Isolated Database ) *done* +- Modular Architecture / Headless API *done* +- Dynamic Page Compositing Model / Site & Page Management *done* +- Authentication / User Management / Profile Management *done* +- Authorization / Roles Management / Granular Permissions *done* +- Dynamic Routing *done* +- Extensibility via Custom Modules *done* +- Extensibility via Custom Themes *done* +- Event Logging *done* +- Folder / File Management *done* +- Recycle Bin *done* +- Scheduled Jobs ( Background Processing ) *done* +- Notifications / Email Delivery *done* +- Auto-Upgrade Framework *done* V.Next - Localization From a1a3b607fa137181888d664616dc0d460913132d Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 12 Feb 2020 09:40:10 -0500 Subject: [PATCH 009/265] Update README.md --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index c941481d..5373bf75 100644 --- a/README.md +++ b/README.md @@ -19,20 +19,20 @@ Oqtane uses Blazor, a new web framework for .NET Core that lets you build intera This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist. V1 (MVP) -- Multi-Tenant ( Shared Database & Isolated Database ) *done* -- Modular Architecture / Headless API *done* -- Dynamic Page Compositing Model / Site & Page Management *done* -- Authentication / User Management / Profile Management *done* -- Authorization / Roles Management / Granular Permissions *done* -- Dynamic Routing *done* -- Extensibility via Custom Modules *done* -- Extensibility via Custom Themes *done* -- Event Logging *done* -- Folder / File Management *done* -- Recycle Bin *done* -- Scheduled Jobs ( Background Processing ) *done* -- Notifications / Email Delivery *done* -- Auto-Upgrade Framework *done* +- Multi-Tenant ( Shared Database & Isolated Database ) **done** +- Modular Architecture / Headless API **done** +- Dynamic Page Compositing Model / Site & Page Management **done** +- Authentication / User Management / Profile Management **done** +- Authorization / Roles Management / Granular Permissions **done** +- Dynamic Routing **done** +- Extensibility via Custom Modules **done** +- Extensibility via Custom Themes **done** +- Event Logging **done** +- Folder / File Management **done** +- Recycle Bin **done** +- Scheduled Jobs ( Background Processing ) **done** +- Notifications / Email Delivery **done** +- Auto-Upgrade Framework **done** V.Next - Localization From f148b1b148bbbcd3bc61ebd9aa3b276d49577776 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 12 Feb 2020 17:14:31 -0500 Subject: [PATCH 010/265] fixed EditMode after refresh --- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 2 +- Oqtane.Client/Shared/PageState.cs | 1 - Oqtane.Client/Shared/Pane.razor | 2 +- Oqtane.Client/Shared/SiteRouter.razor | 9 ++++---- .../Themes/Controls/ControlPanel.razor | 22 ++++++++++++------- .../Themes/Controls/ModuleActions.razor | 4 ++-- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index bc960d0b..520aecfe 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -44,7 +44,7 @@

diff --git a/Oqtane.Server/Controllers/PageModuleController.cs b/Oqtane.Server/Controllers/PageModuleController.cs index 953d93b8..99ba628d 100644 --- a/Oqtane.Server/Controllers/PageModuleController.cs +++ b/Oqtane.Server/Controllers/PageModuleController.cs @@ -24,13 +24,6 @@ namespace Oqtane.Controllers this.logger = logger; } - // GET: api/ - [HttpGet] - public IEnumerable Get() - { - return PageModules.GetPageModules(); - } - // GET api//5 [HttpGet("{id}")] public PageModule Get(int id) diff --git a/Oqtane.Server/Controllers/ProfileController.cs b/Oqtane.Server/Controllers/ProfileController.cs index 6b256a0b..2e4e823c 100644 --- a/Oqtane.Server/Controllers/ProfileController.cs +++ b/Oqtane.Server/Controllers/ProfileController.cs @@ -24,14 +24,7 @@ namespace Oqtane.Controllers [HttpGet] public IEnumerable Get(string siteid) { - if (siteid == "") - { - return Profiles.GetProfiles(); - } - else - { - return Profiles.GetProfiles(int.Parse(siteid)); - } + return Profiles.GetProfiles(int.Parse(siteid)); } // GET api//5 diff --git a/Oqtane.Server/Controllers/RoleController.cs b/Oqtane.Server/Controllers/RoleController.cs index c3bb6e45..c562a0f5 100644 --- a/Oqtane.Server/Controllers/RoleController.cs +++ b/Oqtane.Server/Controllers/RoleController.cs @@ -24,14 +24,7 @@ namespace Oqtane.Controllers [HttpGet] public IEnumerable Get(string siteid) { - if (siteid == "") - { - return Roles.GetRoles(); - } - else - { - return Roles.GetRoles(int.Parse(siteid)); - } + return Roles.GetRoles(int.Parse(siteid)); } // GET api//5 diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index fb720264..0660b5ff 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -42,14 +42,6 @@ namespace Oqtane.Controllers this.logger = logger; } - // GET: api/?siteid=x - [HttpGet] - [Authorize(Roles = Constants.AdminRole)] - public IEnumerable Get() - { - return Users.GetUsers(); - } - // GET api//5?siteid=x [HttpGet("{id}")] public User Get(int id, string siteid) diff --git a/Oqtane.Server/Controllers/UserRoleController.cs b/Oqtane.Server/Controllers/UserRoleController.cs index 6d20321a..7601818e 100644 --- a/Oqtane.Server/Controllers/UserRoleController.cs +++ b/Oqtane.Server/Controllers/UserRoleController.cs @@ -24,14 +24,7 @@ namespace Oqtane.Controllers [HttpGet] public IEnumerable Get(string siteid) { - if (siteid == "") - { - return UserRoles.GetUserRoles(); - } - else - { - return UserRoles.GetUserRoles(int.Parse(siteid)); - } + return UserRoles.GetUserRoles(int.Parse(siteid)); } // GET api//5 From 12a48bb67c80f84f5b8eb58b863d91c4aaa2e092 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sun, 16 Feb 2020 12:56:35 +0100 Subject: [PATCH 013/265] Missing nullcheck -> crash when 404 --- Oqtane.Client/Shared/SiteRouter.razor | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Oqtane.Client/Shared/SiteRouter.razor b/Oqtane.Client/Shared/SiteRouter.razor index a04c759f..90719d40 100644 --- a/Oqtane.Client/Shared/SiteRouter.razor +++ b/Oqtane.Client/Shared/SiteRouter.razor @@ -196,7 +196,10 @@ { page = pages.Where(item => item.Path == path).FirstOrDefault(); reload = Reload.Page; - editmode = page.EditMode; + if (page!=null) + { + editmode = page.EditMode; + } } user = null; From b9528e3f12762d882d9f932a025e912368207252 Mon Sep 17 00:00:00 2001 From: Emanuele Filardo Date: Sun, 16 Feb 2020 19:24:12 +0100 Subject: [PATCH 014/265] Check if moduledefinitionname has valid value --- Oqtane.Client/Themes/Controls/ControlPanel.razor | 2 +- Oqtane.Server/appsettings.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index e8b526d4..90715df5 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -298,7 +298,7 @@ private async Task AddModule() { - if (UserSecurity.IsAuthorized(PageState.User, "Edit", PageState.Page.Permissions)) + if (UserSecurity.IsAuthorized(PageState.User, "Edit", PageState.Page.Permissions) && !string.IsNullOrWhiteSpace(moduledefinitionname) && moduledefinitionname != "-") { if (moduletype == "new") { diff --git a/Oqtane.Server/appsettings.json b/Oqtane.Server/appsettings.json index dbfefb7f..c45f957d 100644 --- a/Oqtane.Server/appsettings.json +++ b/Oqtane.Server/appsettings.json @@ -1,5 +1,5 @@ { "ConnectionStrings": { - "DefaultConnection": "" + "DefaultConnection": "Data Source=(LocalDb)\\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\\Oqtane-202002161845.mdf;Initial Catalog=Oqtane-202002161845;Integrated Security=SSPI;" } -} \ No newline at end of file +} From 43f928667d17ee18901b3254d2a8d7303d7f639f Mon Sep 17 00:00:00 2001 From: Emanuele Filardo Date: Sun, 16 Feb 2020 19:25:00 +0100 Subject: [PATCH 015/265] Delete appsettings.json --- Oqtane.Server/appsettings.json | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 Oqtane.Server/appsettings.json diff --git a/Oqtane.Server/appsettings.json b/Oqtane.Server/appsettings.json deleted file mode 100644 index c45f957d..00000000 --- a/Oqtane.Server/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ConnectionStrings": { - "DefaultConnection": "Data Source=(LocalDb)\\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\\Oqtane-202002161845.mdf;Initial Catalog=Oqtane-202002161845;Integrated Security=SSPI;" - } -} From 066c616eca4456299c3eecceeab277358951ebed Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 17 Feb 2020 19:48:26 -0500 Subject: [PATCH 016/265] authorization changes --- Oqtane.Client/Modules/Admin/Logs/Index.razor | 101 +++++----- .../Admin/ModuleDefinitions/Edit.razor | 4 +- .../Modules/Admin/Modules/Settings.razor | 2 +- Oqtane.Client/Modules/Admin/Pages/Add.razor | 8 +- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 8 +- Oqtane.Client/Modules/Admin/Sites/Add.razor | 8 +- Oqtane.Client/Modules/Admin/Sites/Edit.razor | 16 +- Oqtane.Client/Modules/Admin/Sites/Index.razor | 5 +- Oqtane.Client/Modules/ModuleBase.cs | 7 - Oqtane.Client/Services/AliasService.cs | 17 ++ Oqtane.Client/Services/FolderService.cs | 9 + .../Services/Interfaces/IAliasService.cs | 2 + .../Interfaces/IModuleDefinitionService.cs | 2 + .../Services/Interfaces/IPageService.cs | 1 + .../Services/Interfaces/ISettingService.cs | 4 + .../Services/ModuleDefinitionService.cs | 45 +++-- Oqtane.Client/Services/PageService.cs | 14 ++ Oqtane.Client/Services/SettingService.cs | 10 + Oqtane.Client/Shared/PageState.cs | 3 - Oqtane.Client/Shared/SiteRouter.razor | 129 +++++-------- Oqtane.Client/Themes/ContainerBase.cs | 8 - .../Themes/Controls/ControlPanel.razor | 83 ++------ .../Themes/Controls/ModuleActions.razor | 2 +- Oqtane.Server/Controllers/AliasController.cs | 24 +++ Oqtane.Server/Controllers/FileController.cs | 38 +++- Oqtane.Server/Controllers/FolderController.cs | 73 +++++-- .../Controllers/InstallationController.cs | 2 +- Oqtane.Server/Controllers/JobController.cs | 2 + Oqtane.Server/Controllers/ModuleController.cs | 179 +++++++----------- .../Controllers/ModuleDefinitionController.cs | 34 +++- Oqtane.Server/Controllers/PageController.cs | 137 ++++++++++++-- .../Controllers/PageModuleController.cs | 52 ++++- .../Controllers/PermissionController.cs | 72 ------- Oqtane.Server/Controllers/RoleController.cs | 2 + .../Controllers/SettingController.cs | 91 +++++++-- Oqtane.Server/Controllers/SiteController.cs | 1 + Oqtane.Server/Controllers/ThemeController.cs | 1 + Oqtane.Server/Controllers/UserController.cs | 26 ++- .../Controllers/UserRoleController.cs | 2 + .../Interfaces/IModuleDefinitionRepository.cs | 2 +- .../Interfaces/IModuleRepository.cs | 2 + .../Repository/ModuleDefinitionRepository.cs | 64 ++++--- Oqtane.Server/Repository/ModuleRepository.cs | 113 ++++++++++- Oqtane.Shared/Models/Module.cs | 4 + 44 files changed, 880 insertions(+), 529 deletions(-) delete mode 100644 Oqtane.Server/Controllers/PermissionController.cs diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor index 6bb72e99..4b89cafb 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor @@ -8,56 +8,65 @@ } else { -
- - - - - - -
+
@@ -48,6 +56,14 @@
+ + + +
- + @@ -142,7 +142,7 @@ private void SetIntegratedSecurity(ChangeEventArgs e) { - if (Convert.ToBoolean(e.Value)) + if (Convert.ToBoolean((string)e.Value)) { IntegratedSecurityDisplay = "display: none;"; } @@ -183,7 +183,7 @@ Site site = new Site(); site.TenantId = -1; // will be populated on server site.Name = "Default Site"; - site.Logo = "oqtane.png"; + site.LogoFileId = null; site.DefaultThemeType = Constants.DefaultTheme; site.DefaultLayoutType = Constants.DefaultLayout; site.DefaultContainerType = Constants.DefaultContainer; diff --git a/Oqtane.Client/Shared/Interop.cs b/Oqtane.Client/Shared/Interop.cs index f5bc53cc..0a21512b 100644 --- a/Oqtane.Client/Shared/Interop.cs +++ b/Oqtane.Client/Shared/Interop.cs @@ -87,13 +87,13 @@ namespace Oqtane.Shared } } - public ValueTask GetFiles(string name) + public ValueTask GetFiles(string id) { try { return jsRuntime.InvokeAsync( "interop.getFiles", - name); + id); } catch { @@ -101,13 +101,13 @@ namespace Oqtane.Shared } } - public Task UploadFiles(string posturl, string folder, string name) + public Task UploadFiles(string posturl, string folder, string id) { try { jsRuntime.InvokeAsync( "interop.uploadFiles", - posturl, folder, name); + posturl, folder, id); return Task.CompletedTask; } catch diff --git a/Oqtane.Client/Startup.cs b/Oqtane.Client/Startup.cs index b5c57b17..817e36fa 100644 --- a/Oqtane.Client/Startup.cs +++ b/Oqtane.Client/Startup.cs @@ -51,12 +51,13 @@ namespace Oqtane.Client services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); // dynamically register module contexts and repository services Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index 3747108b..9a5e2198 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -263,7 +263,7 @@ } else { - moduledefinitions = PageState.ModuleDefinitions.Where(item => item.Categories.Contains(e.Value.ToString())).ToList(); + moduledefinitions = PageState.ModuleDefinitions.Where(item => item.Categories.Contains(category)).ToList(); } moduledefinitionname = "-"; StateHasChanged(); diff --git a/Oqtane.Client/Themes/Controls/Logo.razor b/Oqtane.Client/Themes/Controls/Logo.razor index 00ab7f89..afe78f5e 100644 --- a/Oqtane.Client/Themes/Controls/Logo.razor +++ b/Oqtane.Client/Themes/Controls/Logo.razor @@ -9,10 +9,10 @@ protected override void OnParametersSet() { - if (PageState.Site.Logo != "") + if (PageState.Site.LogoFileId != null) { Uri uri = new Uri(NavigationManager.Uri); - logo = "\"""; + logo = "\"""; } } } \ No newline at end of file diff --git a/Oqtane.Client/Themes/ThemeControlBase.cs b/Oqtane.Client/Themes/ThemeControlBase.cs index 014cd5b6..6e3777dc 100644 --- a/Oqtane.Client/Themes/ThemeControlBase.cs +++ b/Oqtane.Client/Themes/ThemeControlBase.cs @@ -52,5 +52,16 @@ namespace Oqtane.Themes { return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters); } + + public string ContentUrl(int fileid) + { + string apiurl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/"; + if (PageState.Alias.Path == "") + { + apiurl += "~/"; + } + apiurl += "api/File/Download/" + fileid.ToString(); + return apiurl; + } } } diff --git a/Oqtane.Client/wwwroot/js/interop.js b/Oqtane.Client/wwwroot/js/interop.js index 96ddf060..f2a2421f 100644 --- a/Oqtane.Client/wwwroot/js/interop.js +++ b/Oqtane.Client/wwwroot/js/interop.js @@ -62,9 +62,9 @@ window.interop = { document.body.appendChild(form); form.submit(); }, - getFiles: function (name) { + getFiles: function (id) { var files = []; - var fileinput = document.getElementById(name); + var fileinput = document.getElementById(id); if (fileinput !== null) { for (var i = 0; i < fileinput.files.length; i++) { files.push(fileinput.files[i].name); @@ -72,10 +72,10 @@ window.interop = { } return files; }, - uploadFiles: function (posturl, folder, name) { - var files = document.getElementById(name + 'FileInput').files; - var progressinfo = document.getElementById(name + 'ProgressInfo'); - var progressbar = document.getElementById(name + 'ProgressBar'); + uploadFiles: function (posturl, folder, id) { + var files = document.getElementById(id + 'FileInput').files; + var progressinfo = document.getElementById(id + 'ProgressInfo'); + var progressbar = document.getElementById(id + 'ProgressBar'); var filename = ''; for (var i = 0; i < files.length; i++) { diff --git a/Oqtane.Server/wwwroot/Tenants/1/Sites/1/oqtane.png b/Oqtane.Server/Content/Tenants/1/Sites/1/logo.png similarity index 100% rename from Oqtane.Server/wwwroot/Tenants/1/Sites/1/oqtane.png rename to Oqtane.Server/Content/Tenants/1/Sites/1/logo.png diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 72bf7bcb..eeea4998 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -3,12 +3,16 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Oqtane.Infrastructure; +using Oqtane.Repository; +using Oqtane.Models; using Oqtane.Shared; using System; using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; +using Oqtane.Security; +using System.Linq; namespace Oqtane.Controllers { @@ -16,53 +20,163 @@ namespace Oqtane.Controllers public class FileController : Controller { private readonly IWebHostEnvironment environment; + private readonly IFileRepository Files; + private readonly IFolderRepository Folders; + private readonly IUserPermissions UserPermissions; + private readonly ITenantResolver Tenants; private readonly ILogManager logger; private readonly string WhiteList = "jpg,jpeg,jpe,gif,bmp,png,mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg"; - public FileController(IWebHostEnvironment environment, ILogManager logger) + public FileController(IWebHostEnvironment environment, IFileRepository Files, IFolderRepository Folders, IUserPermissions UserPermissions, ITenantResolver Tenants, ILogManager logger) { this.environment = environment; + this.Files = Files; + this.Folders = Folders; + this.UserPermissions = UserPermissions; + this.Tenants = Tenants; this.logger = logger; } // GET: api/?folder=x [HttpGet] - public IEnumerable Get(string folder) + public IEnumerable Get(string folder) { - List files = new List(); - folder = GetFolder(folder); - if (Directory.Exists(folder)) + List files = new List(); + int folderid; + if (int.TryParse(folder, out folderid)) { - foreach (string file in Directory.GetFiles(folder)) + Folder Folder = Folders.GetFolder(folderid); + if (Folder != null && UserPermissions.IsAuthorized(User, "Browse", Folder.Permissions)) { - files.Add(Path.GetFileName(file)); + files = Files.GetFiles(folderid).ToList(); + } + } + else + { + if (User.IsInRole(Constants.HostRole)) + { + folder = GetFolderPath(folder); + if (Directory.Exists(folder)) + { + foreach (string file in Directory.GetFiles(folder)) + { + files.Add(new Models.File { Name = Path.GetFileName(file), Extension = Path.GetExtension(file).Replace(".","") }); + } + } } } return files; } + // GET api//5 + [HttpGet("{id}")] + public Models.File Get(int id) + { + return Files.GetFile(id); + } + + // PUT api//5 + [HttpPut("{id}")] + [Authorize(Roles = Constants.RegisteredRole)] + public Models.File Put(int id, [FromBody] Models.File File) + { + if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Folder", File.Folder.FolderId, "Edit")) + { + File = Files.UpdateFile(File); + logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", File); + } + return File; + } + + // DELETE api//5 + [HttpDelete("{id}")] + [Authorize(Roles = Constants.RegisteredRole)] + public void Delete(int id) + { + Models.File File = Files.GetFile(id); + if (UserPermissions.IsAuthorized(User, "Folder", File.Folder.FolderId, "Edit")) + { + Files.DeleteFile(id); + + string filepath = Path.Combine(GetFolderPath(File.Folder) + File.Name); + if (System.IO.File.Exists(filepath)) + { + System.IO.File.Delete(filepath); + } + logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", File); + } + } + + // GET api//upload?url=x&folderid=y + [HttpGet("upload")] + public Models.File UploadFile(string url, string folderid) + { + Models.File file = null; + Folder folder = Folders.GetFolder(int.Parse(folderid)); + if (folder != null && UserPermissions.IsAuthorized(User, "Edit", folder.Permissions)) + { + string folderpath = GetFolderPath(folder); + CreateDirectory(folderpath); + string filename = url.Substring(url.LastIndexOf("/") + 1); + try + { + var client = new System.Net.WebClient(); + client.DownloadFile(url, folderpath + filename); + FileInfo fileinfo = new FileInfo(folderpath + filename); + file = Files.AddFile(new Models.File { Name = filename, FolderId = folder.FolderId, Extension = fileinfo.Extension.Replace(".",""), Size = (int)fileinfo.Length }); + } + catch + { + logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url); + } + } + return file; + } + // POST api//upload [HttpPost("upload")] - [Authorize(Roles = Constants.AdminRole)] public async Task UploadFile(string folder, IFormFile file) { if (file.Length > 0) { - folder = GetFolder(folder); - if (!Directory.Exists(folder)) + string folderpath = ""; + int folderid = -1; + if (int.TryParse(folder, out folderid)) { - Directory.CreateDirectory(folder); + Folder Folder = Folders.GetFolder(folderid); + if (Folder != null && UserPermissions.IsAuthorized(User, "Edit", Folder.Permissions)) + { + folderpath = GetFolderPath(Folder); + } } - using (var stream = new FileStream(Path.Combine(folder, file.FileName), FileMode.Create)) + else { - await file.CopyToAsync(stream); + if (User.IsInRole(Constants.HostRole)) + { + folderpath = GetFolderPath(folder); + } + } + if (folderpath != "") + { + CreateDirectory(folderpath); + using (var stream = new FileStream(Path.Combine(folderpath, file.FileName), FileMode.Create)) + { + await file.CopyToAsync(stream); + } + string upload = await MergeFile(folderpath, file.FileName); + if (upload != "" && folderid != -1) + { + FileInfo fileinfo = new FileInfo(folderpath + upload); + Files.AddFile(new Models.File { Name = upload, FolderId = folderid, Extension = fileinfo.Extension.Replace(".", ""), Size = (int)fileinfo.Length }); + } } - await MergeFile(folder, file.FileName); } } - private async Task MergeFile(string folder, string filename) + private async Task MergeFile(string folder, string filename) { + string merged = ""; + // parse the filename which is in the format of filename.ext.part_x_y string token = ".part_"; string parts = Path.GetExtension(filename).Replace(token, ""); // returns "x_y" @@ -112,6 +226,7 @@ namespace Oqtane.Controllers System.IO.File.Move(Path.Combine(folder, filename + ".tmp"), Path.Combine(folder, filename)); logger.Log(LogLevel.Information, this, LogFunction.Create, "File Uploaded {File}", Path.Combine(folder, filename)); } + merged = filename; } } @@ -125,6 +240,8 @@ namespace Oqtane.Controllers System.IO.File.Delete(filepart); } } + + return merged; } private bool CanAccessFiles(string[] files) @@ -164,24 +281,47 @@ namespace Oqtane.Controllers return canaccess; } - // DELETE api//?folder=x&file=y - [HttpDelete] - [Authorize(Roles = Constants.AdminRole)] - public void Delete(string folder, string file) + // GET api//download/5 + [HttpGet("download/{id}")] + public IActionResult Download(int id) { - file = Path.Combine(GetFolder(folder) + file); - if (System.IO.File.Exists(file)) + Models.File file = Files.GetFile(id); + if (file != null && UserPermissions.IsAuthorized(User, "View", file.Folder.Permissions)) { - System.IO.File.Delete(file); - logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file); + byte[] filebytes = System.IO.File.ReadAllBytes(GetFolderPath(file.Folder) + file.Name); + return File(filebytes, "application/octet-stream", file.Name); + } + else + { + return NotFound(); } } - private string GetFolder(string folder) + private string GetFolderPath(Folder folder) + { + return environment.ContentRootPath + "\\Content\\Tenants\\" + Tenants.GetTenant().TenantId.ToString() + "\\Sites\\" + folder.SiteId.ToString() + "\\" + folder.Path; + } + + private string GetFolderPath(string folder) { - folder = folder.Replace("/", "\\"); - if (folder.StartsWith("\\")) folder = folder.Substring(1); return Path.Combine(environment.WebRootPath, folder); } + + private void CreateDirectory(string folderpath) + { + if (!Directory.Exists(folderpath)) + { + string path = ""; + string[] folders = folderpath.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string folder in folders) + { + path += folder + "\\"; + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + } + } + } } } \ No newline at end of file diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 46bae326..2bc4bc90 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -52,6 +52,12 @@ namespace Oqtane.Controllers { if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Edit", Folder.Permissions)) { + Folder.Path = ""; + if (string.IsNullOrEmpty(Folder.Path) && Folder.ParentId != null) + { + Folder parent = Folders.GetFolder(Folder.ParentId.Value); + Folder.Path = parent.Path + Folder.Name + "\\"; + } Folder = Folders.AddFolder(Folder); logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", Folder); } @@ -65,6 +71,12 @@ namespace Oqtane.Controllers { if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Folder", Folder.FolderId, "Edit")) { + Folder.Path = ""; + if (string.IsNullOrEmpty(Folder.Path) && Folder.ParentId != null) + { + Folder parent = Folders.GetFolder(Folder.ParentId.Value); + Folder.Path = parent.Path + Folder.Name + "\\"; + } Folder = Folders.UpdateFolder(Folder); logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", Folder); } diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index 6d220188..316a7ba3 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -62,11 +62,6 @@ namespace Oqtane.Controllers if (authorized) { Site = Sites.AddSite(Site); - string folder = environment.WebRootPath + "\\Tenants\\" + Tenants.GetTenant().TenantId.ToString() + "\\Sites\\" + Site.SiteId.ToString(); - if (!Directory.Exists(folder)) - { - Directory.CreateDirectory(folder); - } logger.Log(LogLevel.Information, this, LogFunction.Create, "Site Added {Site}", Site); } } diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 37038c9a..fb720264 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -26,9 +26,10 @@ namespace Oqtane.Controllers private readonly SignInManager IdentitySignInManager; private readonly ITenantResolver Tenants; private readonly INotificationRepository Notifications; + private readonly IFolderRepository Folders; private readonly ILogManager logger; - public UserController(IUserRepository Users, IRoleRepository Roles, IUserRoleRepository UserRoles, UserManager IdentityUserManager, SignInManager IdentitySignInManager, ITenantResolver Tenants, INotificationRepository Notifications, ILogManager logger) + public UserController(IUserRepository Users, IRoleRepository Roles, IUserRoleRepository UserRoles, UserManager IdentityUserManager, SignInManager IdentitySignInManager, ITenantResolver Tenants, INotificationRepository Notifications, IFolderRepository Folders, ILogManager logger) { this.Users = Users; this.Roles = Roles; @@ -36,12 +37,14 @@ namespace Oqtane.Controllers this.IdentityUserManager = IdentityUserManager; this.IdentitySignInManager = IdentitySignInManager; this.Tenants = Tenants; + this.Folders = Folders; this.Notifications = Notifications; this.logger = logger; } // GET: api/?siteid=x [HttpGet] + [Authorize(Roles = Constants.AdminRole)] public IEnumerable Get() { return Users.GetUsers(); @@ -98,6 +101,8 @@ namespace Oqtane.Controllers var result = await IdentityUserManager.CreateAsync(identityuser, User.Password); if (result.Succeeded) { + User.LastLoginOn = null; + User.LastIPAddress = ""; user = Users.AddUser(User); if (!verified) { @@ -128,6 +133,14 @@ namespace Oqtane.Controllers userrole.ExpiryDate = null; UserRoles.AddUserRole(userrole); } + + // add folder for user + Folder folder = Folders.GetFolder(User.SiteId, "Users\\"); + if (folder != null) + { + Folders.AddFolder(new Folder { SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", Path = folder.Path + user.UserId.ToString() + "\\", Order = 1, IsSystem = true, + Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + user.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + user.UserId.ToString() + "]\"}]" }); + } } } else @@ -222,6 +235,9 @@ namespace Oqtane.Controllers if (identityuser.EmailConfirmed) { user.IsAuthenticated = true; + user.LastLoginOn = DateTime.Now; + user.LastIPAddress = HttpContext.Connection.RemoteIpAddress.ToString(); + Users.UpdateUser(user); logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", User.Username); if (SetCookie) { diff --git a/Oqtane.Server/Repository/FileRepository.cs b/Oqtane.Server/Repository/FileRepository.cs index 1b85840f..3d51b17d 100644 --- a/Oqtane.Server/Repository/FileRepository.cs +++ b/Oqtane.Server/Repository/FileRepository.cs @@ -18,8 +18,8 @@ namespace Oqtane.Repository public IEnumerable GetFiles(int FolderId) { - IEnumerable permissions = Permissions.GetPermissions("Folder", FolderId); - IEnumerable files = db.File.Where(item => item.FolderId == FolderId); + IEnumerable permissions = Permissions.GetPermissions("Folder", FolderId).ToList(); + IEnumerable files = db.File.Where(item => item.FolderId == FolderId).Include(item => item.Folder); foreach (File file in files) { file.Folder.Permissions = Permissions.EncodePermissions(FolderId, permissions); @@ -43,10 +43,10 @@ namespace Oqtane.Repository public File GetFile(int FileId) { - File file = db.File.Find(FileId); + File file = db.File.Where(item => item.FileId == FileId).Include(item => item.Folder).FirstOrDefault(); if (file != null) { - IEnumerable permissions = Permissions.GetPermissions("Folder", file.FolderId); + IEnumerable permissions = Permissions.GetPermissions("Folder", file.FolderId).ToList(); file.Folder.Permissions = Permissions.EncodePermissions(file.FolderId, permissions); } return file; diff --git a/Oqtane.Server/Repository/FolderRepository.cs b/Oqtane.Server/Repository/FolderRepository.cs index 577639b6..3eb81471 100644 --- a/Oqtane.Server/Repository/FolderRepository.cs +++ b/Oqtane.Server/Repository/FolderRepository.cs @@ -53,7 +53,18 @@ namespace Oqtane.Repository Folder folder = db.Folder.Find(FolderId); if (folder != null) { - IEnumerable permissions = Permissions.GetPermissions("Folder", folder.FolderId); + IEnumerable permissions = Permissions.GetPermissions("Folder", folder.FolderId).ToList(); + folder.Permissions = Permissions.EncodePermissions(folder.FolderId, permissions); + } + return folder; + } + + public Folder GetFolder(int SiteId, string Path) + { + Folder folder = db.Folder.Where(item => item.SiteId == SiteId && item.Path == Path).FirstOrDefault(); + if (folder != null) + { + IEnumerable permissions = Permissions.GetPermissions("Folder", folder.FolderId).ToList(); folder.Permissions = Permissions.EncodePermissions(folder.FolderId, permissions); } return folder; diff --git a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs index d5619331..0d809ef1 100644 --- a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs @@ -10,6 +10,7 @@ namespace Oqtane.Repository Folder AddFolder(Folder Folder); Folder UpdateFolder(Folder Folder); Folder GetFolder(int FolderId); + Folder GetFolder(int SiteId, string Path); void DeleteFolder(int FolderId); } } diff --git a/Oqtane.Server/Repository/JobRepository.cs b/Oqtane.Server/Repository/JobRepository.cs index 46ffaba4..6c390355 100644 --- a/Oqtane.Server/Repository/JobRepository.cs +++ b/Oqtane.Server/Repository/JobRepository.cs @@ -3,21 +3,28 @@ using System.Linq; using Oqtane.Models; using Microsoft.EntityFrameworkCore; using System; +using Microsoft.Extensions.Caching.Memory; namespace Oqtane.Repository { public class JobRepository : IJobRepository { private MasterDBContext db; + private readonly IMemoryCache _cache; - public JobRepository(MasterDBContext context) + public JobRepository(MasterDBContext context, IMemoryCache cache) { db = context; + _cache = cache; } public IEnumerable GetJobs() { - return db.Job.ToList(); + return _cache.GetOrCreate("jobs", entry => + { + entry.SlidingExpiration = TimeSpan.FromMinutes(30); + return db.Job.ToList(); + }); } public Job AddJob(Job Job) diff --git a/Oqtane.Server/Repository/PageModuleRepository.cs b/Oqtane.Server/Repository/PageModuleRepository.cs index 92ebb732..730d3c4c 100644 --- a/Oqtane.Server/Repository/PageModuleRepository.cs +++ b/Oqtane.Server/Repository/PageModuleRepository.cs @@ -56,7 +56,7 @@ namespace Oqtane.Repository .SingleOrDefault(item => item.PageModuleId == PageModuleId); if (pagemodule != null) { - IEnumerable permissions = Permissions.GetPermissions("Module", pagemodule.ModuleId); + IEnumerable permissions = Permissions.GetPermissions("Module", pagemodule.ModuleId).ToList(); pagemodule.Module.Permissions = Permissions.EncodePermissions(pagemodule.ModuleId, permissions); } return pagemodule; @@ -68,7 +68,7 @@ namespace Oqtane.Repository .SingleOrDefault(item => item.PageId == PageId && item.ModuleId == ModuleId); if (pagemodule != null) { - IEnumerable permissions = Permissions.GetPermissions("Module", pagemodule.ModuleId); + IEnumerable permissions = Permissions.GetPermissions("Module", pagemodule.ModuleId).ToList(); pagemodule.Module.Permissions = Permissions.EncodePermissions(pagemodule.ModuleId, permissions); } return pagemodule; diff --git a/Oqtane.Server/Repository/PageRepository.cs b/Oqtane.Server/Repository/PageRepository.cs index 8ec9d1fd..6e4fc2ff 100644 --- a/Oqtane.Server/Repository/PageRepository.cs +++ b/Oqtane.Server/Repository/PageRepository.cs @@ -55,7 +55,7 @@ namespace Oqtane.Repository Page page = db.Page.Find(PageId); if (page != null) { - IEnumerable permissions = Permissions.GetPermissions("Page", page.PageId); + IEnumerable permissions = Permissions.GetPermissions("Page", page.PageId).ToList(); page.Permissions = Permissions.EncodePermissions(page.PageId, permissions); } return page; @@ -73,7 +73,7 @@ namespace Oqtane.Repository } if (page != null) { - IEnumerable permissions = Permissions.GetPermissions("Page", page.PageId); + IEnumerable permissions = Permissions.GetPermissions("Page", page.PageId).ToList(); page.Permissions = Permissions.EncodePermissions(page.PageId, permissions); } } diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index c1233b5d..d206c763 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -15,6 +15,8 @@ namespace Oqtane.Repository private readonly TenantDBContext db; private readonly IRoleRepository RoleRepository; private readonly IProfileRepository ProfileRepository; + private readonly IFolderRepository FolderRepository; + private readonly IFileRepository FileRepository; private readonly IPageRepository PageRepository; private readonly IModuleRepository ModuleRepository; private readonly IPageModuleRepository PageModuleRepository; @@ -22,11 +24,13 @@ namespace Oqtane.Repository private readonly IServiceProvider ServiceProvider; private readonly List SiteTemplate; - public SiteRepository(TenantDBContext context, IRoleRepository RoleRepository, IProfileRepository ProfileRepository, IPageRepository PageRepository, IModuleRepository ModuleRepository, IPageModuleRepository PageModuleRepository, IModuleDefinitionRepository ModuleDefinitionRepository, IServiceProvider ServiceProvider) + public SiteRepository(TenantDBContext context, IRoleRepository RoleRepository, IProfileRepository ProfileRepository, IFolderRepository FolderRepository, IFileRepository FileRepository, IPageRepository PageRepository, IModuleRepository ModuleRepository, IPageModuleRepository PageModuleRepository, IModuleDefinitionRepository ModuleDefinitionRepository, IServiceProvider ServiceProvider) { db = context; this.RoleRepository = RoleRepository; this.ProfileRepository = ProfileRepository; + this.FolderRepository = FolderRepository; + this.FileRepository = FileRepository; this.PageRepository = PageRepository; this.ModuleRepository = ModuleRepository; this.PageModuleRepository = PageModuleRepository; @@ -40,7 +44,7 @@ namespace Oqtane.Repository Content = "

Oqtane is an open source modular application framework built from the ground up using modern .NET Core technology. It leverages the revolutionary new Blazor component model to create a fully dynamic web development experience which can be executed on a client or server. Whether you are looking for a platform to accelerate your web development efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.

" + "



Join Our Community  Clone Our Repo

" + "

Blazor is a single-page app framework that lets you build interactive web applications using C# instead of JavaScript. Client-side Blazor relies on WebAssembly, an open web standard that does not require plugins or code transpilation in order to run natively in a web browser. Server-side Blazor uses SignalR to host your application on a web server and provide a responsive and robust debugging experience. Blazor applications works in all modern web browsers, including mobile browsers.

" + - "

Blazor is a feature of ASP.NET Core 3.0, the popular cross platform web development framework from Microsoft that extends the .NET developer platform with tools and libraries for building web apps.

" + "

Blazor is a feature of ASP.NET Core 3, the popular cross platform web development framework from Microsoft that extends the .NET developer platform with tools and libraries for building web apps.

" }, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "

Copyright (c) 2019 .NET Foundation

" + @@ -168,6 +172,15 @@ namespace Oqtane.Repository ProfileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "PostalCode", Title = "Postal Code", Description = "Postal Code Or Zip Code", Category = "Address", ViewOrder = 7, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false }); ProfileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "Phone", Title = "Phone Number", Description = "Phone Number", Category = "Contact", ViewOrder = 8, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false }); + Folder folder = FolderRepository.AddFolder(new Folder { SiteId = site.SiteId, ParentId = null, Name = "Root", Path = "", Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); + FolderRepository.AddFolder(new Folder { SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = "Users\\", Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); + if (site.Name == "Default Site") + { + File file = FileRepository.AddFile(new File { FolderId = folder.FolderId, Name = "logo.png", Extension = "png", Size = 8192 }); + site.LogoFileId = file.FileId; + UpdateSite(site); + } + List moduledefinitions = ModuleDefinitionRepository.GetModuleDefinitions(site.SiteId).ToList(); foreach (PageTemplate pagetemplate in SiteTemplate) { diff --git a/Oqtane.Server/Scripts/00.00.00.sql b/Oqtane.Server/Scripts/00.00.00.sql index 93dc05f7..29637d45 100644 --- a/Oqtane.Server/Scripts/00.00.00.sql +++ b/Oqtane.Server/Scripts/00.00.00.sql @@ -8,7 +8,7 @@ CREATE TABLE [dbo].[Site]( [SiteId] [int] IDENTITY(1,1) NOT NULL, [TenantId] [int] NOT NULL, [Name] [nvarchar](200) NOT NULL, - [Logo] [nvarchar](50) NOT NULL, + [LogoFileId] [int] NULL, [DefaultThemeType] [nvarchar](200) NOT NULL, [DefaultLayoutType] [nvarchar](200) NOT NULL, [DefaultContainerType] [nvarchar](200) NOT NULL, @@ -96,6 +96,9 @@ CREATE TABLE [dbo].[User]( [Username] [nvarchar](256) NOT NULL, [DisplayName] [nvarchar](50) NOT NULL, [Email] [nvarchar](256) NOT NULL, + [PhotoFileId] [int] NULL, + [LastLoginOn] [datetime] NULL, + [LastIPAddress] [nvarchar](50) NOT NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, @@ -261,6 +264,7 @@ CREATE TABLE [dbo].[Folder]( [Name] [nvarchar](50) NOT NULL, [ParentId] [int] NULL, [Order] [int] NOT NULL, + [IsSystem] [bit] NOT NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, @@ -278,7 +282,9 @@ GO CREATE TABLE [dbo].[File]( [FileId] [int] IDENTITY(1,1) NOT NULL, [FolderId] [int] NOT NULL, - [Name] [nvarchar](50) NOT NULL, + [Name] [nvarchar](250) NOT NULL, + [Extension] [nvarchar](50) NOT NULL, + [Size] [int] NOT NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, diff --git a/Oqtane.Server/Scripts/Master.sql b/Oqtane.Server/Scripts/Master.sql index 0db09f12..45e474c7 100644 --- a/Oqtane.Server/Scripts/Master.sql +++ b/Oqtane.Server/Scripts/Master.sql @@ -139,7 +139,7 @@ GO SET IDENTITY_INSERT [dbo].[Job] ON GO INSERT [dbo].[Job] ([JobId], [Name], [JobType], [Frequency], [Interval], [StartDate], [EndDate], [IsEnabled], [IsStarted], [IsExecuting], [NextExecution], [RetentionHistory], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (1, N'Notification Job', N'Oqtane.Infrastructure.NotificationJob, Oqtane.Server', N'm', 1, null, null, 1, 0, 0, null, 10, '', getdate(), '', getdate()) +VALUES (1, N'Notification Job', N'Oqtane.Infrastructure.NotificationJob, Oqtane.Server', N'm', 1, null, null, 0, 0, 0, null, 10, '', getdate(), '', getdate()) GO SET IDENTITY_INSERT [dbo].[Job] OFF GO diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 3e65ec1c..2ea27dd4 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -82,6 +82,9 @@ namespace Oqtane.Server options.AddPolicy("EditPage", policy => policy.Requirements.Add(new PermissionRequirement("Page", "Edit"))); options.AddPolicy("ViewModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", "View"))); options.AddPolicy("EditModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", "Edit"))); + options.AddPolicy("ViewFolder", policy => policy.Requirements.Add(new PermissionRequirement("Folder", "View"))); + options.AddPolicy("EditFolder", policy => policy.Requirements.Add(new PermissionRequirement("Folder", "Edit"))); + options.AddPolicy("ListFolder", policy => policy.Requirements.Add(new PermissionRequirement("Folder", "List"))); }); // register scoped core services @@ -101,12 +104,13 @@ namespace Oqtane.Server services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddSingleton(); @@ -181,6 +185,8 @@ namespace Oqtane.Server services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddOqtaneModules(); services.AddOqtaneThemes(); @@ -329,6 +335,8 @@ namespace Oqtane.Server services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddOqtaneModules(); services.AddOqtaneThemes(); diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index 96ddf060..f2a2421f 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -62,9 +62,9 @@ window.interop = { document.body.appendChild(form); form.submit(); }, - getFiles: function (name) { + getFiles: function (id) { var files = []; - var fileinput = document.getElementById(name); + var fileinput = document.getElementById(id); if (fileinput !== null) { for (var i = 0; i < fileinput.files.length; i++) { files.push(fileinput.files[i].name); @@ -72,10 +72,10 @@ window.interop = { } return files; }, - uploadFiles: function (posturl, folder, name) { - var files = document.getElementById(name + 'FileInput').files; - var progressinfo = document.getElementById(name + 'ProgressInfo'); - var progressbar = document.getElementById(name + 'ProgressBar'); + uploadFiles: function (posturl, folder, id) { + var files = document.getElementById(id + 'FileInput').files; + var progressinfo = document.getElementById(id + 'ProgressInfo'); + var progressbar = document.getElementById(id + 'ProgressBar'); var filename = ''; for (var i = 0; i < files.length; i++) { diff --git a/Oqtane.Shared/Models/File.cs b/Oqtane.Shared/Models/File.cs index 55b8a637..44acbcbd 100644 --- a/Oqtane.Shared/Models/File.cs +++ b/Oqtane.Shared/Models/File.cs @@ -7,6 +7,8 @@ namespace Oqtane.Models public int FileId { get; set; } public int FolderId { get; set; } public string Name { get; set; } + public string Extension { get; set; } + public int Size { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } diff --git a/Oqtane.Shared/Models/Folder.cs b/Oqtane.Shared/Models/Folder.cs index c632eac4..5f04f4bb 100644 --- a/Oqtane.Shared/Models/Folder.cs +++ b/Oqtane.Shared/Models/Folder.cs @@ -11,6 +11,7 @@ namespace Oqtane.Models public string Name { get; set; } public string Path { get; set; } public int Order { get; set; } + public bool IsSystem { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs index dc7fbf35..439f5131 100644 --- a/Oqtane.Shared/Models/Site.cs +++ b/Oqtane.Shared/Models/Site.cs @@ -8,7 +8,7 @@ namespace Oqtane.Models public int SiteId { get; set; } public int TenantId { get; set; } public string Name { get; set; } - public string Logo { get; set; } + public int? LogoFileId { get; set; } public string DefaultThemeType { get; set; } public string DefaultLayoutType { get; set; } public string DefaultContainerType { get; set; } diff --git a/Oqtane.Shared/Models/User.cs b/Oqtane.Shared/Models/User.cs index bec8f3e2..e74deef2 100644 --- a/Oqtane.Shared/Models/User.cs +++ b/Oqtane.Shared/Models/User.cs @@ -9,6 +9,9 @@ namespace Oqtane.Models public string Username { get; set; } public string DisplayName { get; set; } public string Email { get; set; } + public int? PhotoFileId { get; set; } + public DateTime? LastLoginOn { get; set; } + public string LastIPAddress { get; set; } [NotMapped] public int SiteId { get; set; } From f6622a55997cb57c5c09dc84766837da2c52a555 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 12 Feb 2020 09:14:20 -0500 Subject: [PATCH 005/265] bug fixes --- Oqtane.Client/Modules/Admin/Files/Index.razor | 4 ++-- Oqtane.Client/Modules/Admin/Users/Add.razor | 14 -------------- screenshot6.png | Bin 0 -> 69541 bytes 3 files changed, 2 insertions(+), 16 deletions(-) create mode 100644 screenshot6.png diff --git a/Oqtane.Client/Modules/Admin/Files/Index.razor b/Oqtane.Client/Modules/Admin/Files/Index.razor index 677b117b..489587a8 100644 --- a/Oqtane.Client/Modules/Admin/Files/Index.razor +++ b/Oqtane.Client/Modules/Admin/Files/Index.razor @@ -52,7 +52,7 @@ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } List Folders; - int folderid; + int folderid = -1; List Files; Uri uri; @@ -61,7 +61,7 @@ try { Folders = await FolderService.GetFoldersAsync(PageState.Site.SiteId); - if (Folders.Count > 0) + if (folderid == -1 && Folders.Count > 0) { folderid = Folders[0].FolderId; await GetFiles(); diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor index 781e958f..11b8b1b9 100644 --- a/Oqtane.Client/Modules/Admin/Users/Add.razor +++ b/Oqtane.Client/Modules/Admin/Users/Add.razor @@ -48,14 +48,6 @@
- - - -
- + - +
+ + + + + +
+ + + + + + + + +
+ @if (Logs.Any()) { - -
-   - Date - Level - Feature - Function -
- - - @context.LogDate - @context.Level - @context.Feature - @context.Function - -
+ +
+   + Date + Level + Feature + Function +
+ + + @context.LogDate + @context.Level + @context.Feature + @context.Function + +
} else { -

No Logs Match The Criteria Specified

+

No Logs Match The Criteria Specified

} } @@ -159,4 +168,4 @@ else } return classname; } -} \ No newline at end of file +} diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index 21d90d91..68d535cd 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -45,7 +45,7 @@ try { ModuleDefinitionId = Int32.Parse(PageState.QueryString["id"]); - ModuleDefinition moduledefinition = PageState.ModuleDefinitions.Where(item => item.ModuleDefinitionId == ModuleDefinitionId).FirstOrDefault(); + ModuleDefinition moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(ModuleDefinitionId, ModuleState.SiteId); if (moduledefinition != null) { name = moduledefinition.Name; @@ -67,7 +67,7 @@ { try { - ModuleDefinition moduledefinition = PageState.ModuleDefinitions.Where(item => item.ModuleDefinitionId == ModuleDefinitionId).FirstOrDefault(); + ModuleDefinition moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(ModuleDefinitionId, ModuleState.SiteId); moduledefinition.Permissions = permissiongrid.GetPermissions(); await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition); await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition); diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index 3c81ef2a..14d8f9eb 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -81,7 +81,7 @@ containers = ThemeService.GetContainerTypes(await ThemeService.GetThemesAsync()); containertype = ModuleState.ContainerType; permissions = ModuleState.Permissions; - permissionnames = PageState.ModuleDefinitions.Find(item => item.ModuleDefinitionName == ModuleState.ModuleDefinitionName).PermissionNames; + permissionnames = ModuleState.ModuleDefinition.PermissionNames; pageid = ModuleState.PageId.ToString(); DynamicComponent = builder => diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 9b642b02..538f94af 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -148,6 +148,7 @@ Dictionary themes = new Dictionary(); Dictionary panelayouts = new Dictionary(); + List Themes; List pages; string name; string path = ""; @@ -169,13 +170,14 @@ { try { + Themes = await ThemeService.GetThemesAsync(); pages = PageState.Pages; children = PageState.Pages.Where(item => item.ParentId == null).ToList(); - themes = ThemeService.GetThemeTypes(PageState.Themes); + themes = ThemeService.GetThemeTypes(Themes); themetype = PageState.Site.DefaultThemeType; - panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype); + panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); layouttype = PageState.Site.DefaultLayoutType; List permissionstrings = new List(); @@ -219,7 +221,7 @@ themetype = (string)e.Value; if (themetype != "") { - panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype); + panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); } else { diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 520aecfe..10cff51b 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -169,6 +169,7 @@ Dictionary themes = new Dictionary(); Dictionary panelayouts = new Dictionary(); + List Themes; List pages; int PageId; string name; @@ -198,10 +199,11 @@ { try { + Themes = await ThemeService.GetThemesAsync(); pages = PageState.Pages; children = PageState.Pages.Where(item => item.ParentId == null).ToList(); - themes = ThemeService.GetThemeTypes(PageState.Themes); + themes = ThemeService.GetThemeTypes(Themes); PageId = Int32.Parse(PageState.QueryString["id"]); Page page = PageState.Pages.Where(item => item.PageId == PageId).FirstOrDefault(); @@ -226,7 +228,7 @@ ispersonalizable = page.IsPersonalizable.ToString(); mode = (page.EditMode) ? "edit" : "view"; themetype = page.ThemeType; - panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype); + panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); layouttype = page.LayoutType; icon = page.Icon; permissions = page.Permissions; @@ -282,7 +284,7 @@ themetype = (string)e.Value; if (themetype != "") { - panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype); + panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); } else { diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 94face09..b1e6820f 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -125,6 +125,7 @@ else Dictionary panelayouts = new Dictionary(); Dictionary containers = new Dictionary(); + List Themes; List tenants; string tenantid = "-1"; string name = ""; @@ -139,10 +140,11 @@ else protected override async Task OnInitializedAsync() { + Themes = await ThemeService.GetThemesAsync(); tenants = await TenantService.GetTenantsAsync(); urls = PageState.Alias.Name; - themes = ThemeService.GetThemeTypes(PageState.Themes); - containers = ThemeService.GetContainerTypes(PageState.Themes); + themes = ThemeService.GetThemeTypes(Themes); + containers = ThemeService.GetContainerTypes(Themes); username = Constants.HostUser; } @@ -175,7 +177,7 @@ else themetype = (string)e.Value; if (themetype != "") { - panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype); + panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); } else { diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor index da99c044..5a07fce7 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor @@ -157,6 +157,7 @@ Dictionary panelayouts; Dictionary containers; + List Themes; Alias Alias; int siteid; string name = ""; @@ -186,14 +187,15 @@ { try { - Alias = PageState.Aliases.Where(item => item.AliasId == Int32.Parse(PageState.QueryString["id"])).FirstOrDefault(); + Themes = await ThemeService.GetThemesAsync(); + aliases = await AliasService.GetAliasesAsync(); + Alias = aliases.Where(item => item.AliasId == int.Parse(PageState.QueryString["id"])).FirstOrDefault(); siteid = Alias.SiteId; Site site = await SiteService.GetSiteAsync(siteid, Alias); if (site != null) { name = site.Name; - aliases = PageState.Aliases.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList(); - foreach (Alias alias in aliases) + foreach (Alias alias in aliases.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList()) { urls += alias.Name + "\n"; } @@ -202,7 +204,7 @@ logofileid = site.LogoFileId.Value; } themetype = site.DefaultThemeType; - panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype); + panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); layouttype = site.DefaultLayoutType; containertype = site.DefaultContainerType; @@ -222,8 +224,8 @@ isdeleted = site.IsDeleted.ToString(); } - themes = ThemeService.GetThemeTypes(PageState.Themes); - containers = ThemeService.GetContainerTypes(PageState.Themes); + themes = ThemeService.GetThemeTypes(Themes); + containers = ThemeService.GetContainerTypes(Themes); } catch (Exception ex) { @@ -239,7 +241,7 @@ themetype = (string)e.Value; if (themetype != "") { - panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype); + panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); } else { diff --git a/Oqtane.Client/Modules/Admin/Sites/Index.razor b/Oqtane.Client/Modules/Admin/Sites/Index.razor index 46152dee..7133f8b5 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Index.razor @@ -32,13 +32,14 @@ else List sites; string scheme; - protected override void OnParametersSet() + protected override async Task OnParametersSetAsync() { Uri uri = new Uri(NavigationManager.Uri); scheme = uri.Scheme + "://"; + List aliases = await AliasService.GetAliasesAsync(); sites = new List(); - foreach (Alias alias in PageState.Aliases.OrderBy(item => item.Name)) + foreach (Alias alias in aliases) { if (!sites.Exists(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId)) { diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index c2ae550e..778c9633 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -29,13 +29,6 @@ namespace Oqtane.Modules [CascadingParameter] protected ModuleInstance ModuleInstance { get; set; } - protected ModuleDefinition ModuleDefinition - { - get - { - return PageState.ModuleDefinitions.Where(item => item.ModuleDefinitionName == ModuleState.ModuleDefinitionName).FirstOrDefault(); - } - } // optional interface properties public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security diff --git a/Oqtane.Client/Services/AliasService.cs b/Oqtane.Client/Services/AliasService.cs index 6fa109b5..c60fed12 100644 --- a/Oqtane.Client/Services/AliasService.cs +++ b/Oqtane.Client/Services/AliasService.cs @@ -5,6 +5,8 @@ using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; using Oqtane.Shared; +using System.Net; +using System; namespace Oqtane.Services { @@ -37,6 +39,21 @@ namespace Oqtane.Services return await http.GetJsonAsync(apiurl + "/" + AliasId.ToString()); } + public async Task GetAliasAsync(string Url) + { + Uri uri = new Uri(Url); + string name = uri.Authority; + if (uri.Segments.Count() > 1) + { + name += "/" + uri.Segments[1]; + } + if (name.EndsWith("/")) + { + name = name.Substring(0, name.Length - 1); + } + return await http.GetJsonAsync(apiurl + "/name/" + WebUtility.UrlEncode(name)); + } + public async Task AddAliasAsync(Alias alias) { return await http.PostJsonAsync(apiurl, alias); diff --git a/Oqtane.Client/Services/FolderService.cs b/Oqtane.Client/Services/FolderService.cs index 4ece5538..73ab4d74 100644 --- a/Oqtane.Client/Services/FolderService.cs +++ b/Oqtane.Client/Services/FolderService.cs @@ -87,6 +87,15 @@ namespace Oqtane.Services }; Folders = Folders.OrderBy(item => item.Order).ToList(); GetPath(Folders, null); + + // add any non-hierarchical items to the end of the list + foreach(Folder folder in Folders) + { + if (hierarchy.Find(item => item.FolderId == folder.FolderId) == null) + { + hierarchy.Add(folder); + } + } return hierarchy; } } diff --git a/Oqtane.Client/Services/Interfaces/IAliasService.cs b/Oqtane.Client/Services/Interfaces/IAliasService.cs index b9b515ac..9ecfa221 100644 --- a/Oqtane.Client/Services/Interfaces/IAliasService.cs +++ b/Oqtane.Client/Services/Interfaces/IAliasService.cs @@ -10,6 +10,8 @@ namespace Oqtane.Services Task GetAliasAsync(int AliasId); + Task GetAliasAsync(string Url); + Task AddAliasAsync(Alias Alias); Task UpdateAliasAsync(Alias Alias); diff --git a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs index b7341249..aad9b193 100644 --- a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs +++ b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs @@ -7,8 +7,10 @@ namespace Oqtane.Services public interface IModuleDefinitionService { Task> GetModuleDefinitionsAsync(int SiteId); + Task GetModuleDefinitionAsync(int ModuleDefinitionId, int SiteId); Task UpdateModuleDefinitionAsync(ModuleDefinition ModuleDefinition); Task InstallModuleDefinitionsAsync(); Task DeleteModuleDefinitionAsync(int ModuleDefinitionId, int SiteId); + Task LoadModuleDefinitionsAsync(int SiteId); } } diff --git a/Oqtane.Client/Services/Interfaces/IPageService.cs b/Oqtane.Client/Services/Interfaces/IPageService.cs index d3ab967b..35ae6a2f 100644 --- a/Oqtane.Client/Services/Interfaces/IPageService.cs +++ b/Oqtane.Client/Services/Interfaces/IPageService.cs @@ -10,6 +10,7 @@ namespace Oqtane.Services Task GetPageAsync(int PageId); Task GetPageAsync(int PageId, int UserId); Task AddPageAsync(Page Page); + Task AddPageAsync(int PageId, int UserId); Task UpdatePageAsync(Page Page); Task UpdatePageOrderAsync(int SiteId, int PageId, int? ParentId); Task DeletePageAsync(int PageId); diff --git a/Oqtane.Client/Services/Interfaces/ISettingService.cs b/Oqtane.Client/Services/Interfaces/ISettingService.cs index 66647129..6f99912e 100644 --- a/Oqtane.Client/Services/Interfaces/ISettingService.cs +++ b/Oqtane.Client/Services/Interfaces/ISettingService.cs @@ -30,6 +30,10 @@ namespace Oqtane.Services Task UpdateUserSettingsAsync(Dictionary UserSettings, int UserId); + Task> GetFolderSettingsAsync(int FolderId); + + Task UpdateFolderSettingsAsync(Dictionary FolderSettings, int FolderId); + Task> GetSettingsAsync(string EntityName, int EntityId); Task UpdateSettingsAsync(Dictionary Settings, string EntityName, int EntityId); diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index 19791a9d..aadb2dcb 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -30,8 +30,34 @@ namespace Oqtane.Services public async Task> GetModuleDefinitionsAsync(int SiteId) { - // get list of modules from the server List moduledefinitions = await http.GetJsonAsync>(apiurl + "?siteid=" + SiteId.ToString()); + return moduledefinitions.OrderBy(item => item.Name).ToList(); + } + + public async Task GetModuleDefinitionAsync(int ModuleDefinitionId, int SiteId) + { + return await http.GetJsonAsync(apiurl + "/" + ModuleDefinitionId.ToString() + "?siteid=" + SiteId.ToString()); + } + + public async Task UpdateModuleDefinitionAsync(ModuleDefinition ModuleDefinition) + { + await http.PutJsonAsync(apiurl + "/" + ModuleDefinition.ModuleDefinitionId.ToString(), ModuleDefinition); + } + + public async Task InstallModuleDefinitionsAsync() + { + await http.GetJsonAsync>(apiurl + "/install"); + } + + public async Task DeleteModuleDefinitionAsync(int ModuleDefinitionId, int SiteId) + { + await http.DeleteAsync(apiurl + "/" + ModuleDefinitionId.ToString() + "?siteid=" + SiteId.ToString()); + } + + public async Task LoadModuleDefinitionsAsync(int SiteId) + { + // get list of modules from the server + List moduledefinitions = await GetModuleDefinitionsAsync(SiteId); // 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(); @@ -60,23 +86,6 @@ namespace Oqtane.Services Assembly.Load(bytes); } } - - return moduledefinitions.OrderBy(item => item.Name).ToList(); - } - - public async Task UpdateModuleDefinitionAsync(ModuleDefinition ModuleDefinition) - { - await http.PutJsonAsync(apiurl + "/" + ModuleDefinition.ModuleDefinitionId.ToString(), ModuleDefinition); - } - - public async Task InstallModuleDefinitionsAsync() - { - await http.GetJsonAsync>(apiurl + "/install"); - } - - public async Task DeleteModuleDefinitionAsync(int ModuleDefinitionId, int SiteId) - { - await http.DeleteAsync(apiurl + "/" + ModuleDefinitionId.ToString() + "?siteid=" + SiteId.ToString()); } } } diff --git a/Oqtane.Client/Services/PageService.cs b/Oqtane.Client/Services/PageService.cs index ddcd3324..92607162 100644 --- a/Oqtane.Client/Services/PageService.cs +++ b/Oqtane.Client/Services/PageService.cs @@ -49,6 +49,11 @@ namespace Oqtane.Services return await http.PostJsonAsync(apiurl, Page); } + public async Task AddPageAsync(int PageId, int UserId) + { + return await http.PostJsonAsync(apiurl + "/" + PageId.ToString() + "?userid=" + UserId.ToString(), null); + } + public async Task UpdatePageAsync(Page Page) { return await http.PutJsonAsync(apiurl + "/" + Page.PageId.ToString(), Page); @@ -92,6 +97,15 @@ namespace Oqtane.Services }; Pages = Pages.OrderBy(item => item.Order).ToList(); GetPath(Pages, null); + + // add any non-hierarchical items to the end of the list + foreach (Page page in Pages) + { + if (hierarchy.Find(item => item.PageId == page.PageId) == null) + { + hierarchy.Add(page); + } + } return hierarchy; } } diff --git a/Oqtane.Client/Services/SettingService.cs b/Oqtane.Client/Services/SettingService.cs index e92de5da..06c6534c 100644 --- a/Oqtane.Client/Services/SettingService.cs +++ b/Oqtane.Client/Services/SettingService.cs @@ -86,6 +86,16 @@ namespace Oqtane.Services await UpdateSettingsAsync(UserSettings, "User", UserId); } + public async Task> GetFolderSettingsAsync(int FolderId) + { + return await GetSettingsAsync("Folder", FolderId); + } + + public async Task UpdateFolderSettingsAsync(Dictionary FolderSettings, int FolderId) + { + await UpdateSettingsAsync(FolderSettings, "Folder", FolderId); + } + public async Task> GetSettingsAsync(string EntityName, int EntityId) { Dictionary dictionary = new Dictionary(); diff --git a/Oqtane.Client/Shared/PageState.cs b/Oqtane.Client/Shared/PageState.cs index 37beb6f2..c8003159 100644 --- a/Oqtane.Client/Shared/PageState.cs +++ b/Oqtane.Client/Shared/PageState.cs @@ -6,9 +6,6 @@ namespace Oqtane.Shared { public class PageState { - public List ModuleDefinitions { get; set; } - public List Themes { get; set; } - public List Aliases { get; set; } public Alias Alias { get; set; } public Site Site { get; set; } public List Pages { get; set; } diff --git a/Oqtane.Client/Shared/SiteRouter.razor b/Oqtane.Client/Shared/SiteRouter.razor index a04c759f..2438ed57 100644 --- a/Oqtane.Client/Shared/SiteRouter.razor +++ b/Oqtane.Client/Shared/SiteRouter.razor @@ -10,7 +10,6 @@ @inject IUserService UserService @inject IModuleService ModuleService @inject IModuleDefinitionService ModuleDefinitionService -@inject IThemeService ThemeService @implements IHandleAfterRender @DynamicComponent @@ -67,9 +66,6 @@ private async Task Refresh() { - List moduledefinitions; - List themes; - List aliases; Alias alias; Site site; List pages; @@ -107,21 +103,18 @@ if (PageState == null || reload == Reload.Application) { - themes = await ThemeService.GetThemesAsync(); - aliases = await AliasService.GetAliasesAsync(); alias = null; } else { - themes = PageState.Themes; - aliases = PageState.Aliases; alias = PageState.Alias; } // check if site has changed - if (alias == null || GetAlias(_absoluteUri, aliases).Name != alias.Name) + Alias current = await AliasService.GetAliasAsync(_absoluteUri); + if (alias == null || current.Name != alias.Name) { - alias = GetAlias(_absoluteUri, aliases); + alias = current; SiteState.Alias = alias; // set state for services reload = Reload.Site; } @@ -137,12 +130,13 @@ { if (PageState == null || reload >= Reload.Site) { - moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(site.SiteId); +#if WASM + ModuleDefinitionService.LoadModuleDefinitionsAsync(site.SiteId); // download assemblies to browser when running client-side +#endif pages = await PageService.GetPagesAsync(site.SiteId); } else { - moduledefinitions = PageState.ModuleDefinitions; pages = PageState.Pages; } @@ -192,7 +186,7 @@ } // check if page has changed - if (page.Path != path) + if (page != null && page.Path != path) { page = pages.Where(item => item.Path == path).FirstOrDefault(); reload = Reload.Page; @@ -226,9 +220,6 @@ page = await ProcessPage(page, site, user); pagestate = new PageState(); - pagestate.ModuleDefinitions = moduledefinitions; - pagestate.Themes = themes; - pagestate.Aliases = aliases; pagestate.Alias = alias; pagestate.Site = site; pagestate.Pages = pages; @@ -247,7 +238,7 @@ if (PageState == null || reload >= Reload.Page) { modules = await ModuleService.GetModulesAsync(site.SiteId); - modules = ProcessModules(modules, moduledefinitions, page.PageId, pagestate.ModuleId, pagestate.Action, page.Panes, site.DefaultContainerType); + modules = ProcessModules(modules, page.PageId, pagestate.ModuleId, pagestate.Action, page.Panes, site.DefaultContainerType); } else { @@ -270,7 +261,10 @@ else { // page does not exist - NavigationManager.NavigateTo(""); + if (path != "") + { + NavigationManager.NavigateTo(""); + } } } else @@ -353,58 +347,52 @@ return page; } - private List ProcessModules(List modules, List moduledefinitions, int pageid, int moduleid, string control, string panes, string defaultcontainertype) + private List ProcessModules(List modules, int pageid, int moduleid, string control, string panes, string defaultcontainertype) { - ModuleDefinition moduledefinition; Dictionary paneindex = new Dictionary(); foreach (Module module in modules) { - // set the type based on the template and action - moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault(); - if (moduledefinition != null) + string typename = module.ModuleDefinition.ControlTypeTemplate; + if (module.ModuleId == moduleid && control != "") { - string typename = moduledefinition.ControlTypeTemplate; - if (module.ModuleId == moduleid && control != "") + // check if the module defines custom routes + if (module.ModuleDefinition.ControlTypeRoutes != "") { - // check if the module defines custom routes - if (moduledefinition.ControlTypeRoutes != "") + foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { - foreach (string route in moduledefinition.ControlTypeRoutes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + if (route.StartsWith(control + "=")) { - if (route.StartsWith(control + "=")) - { - typename = route.Replace(control + "=", ""); - } - } - } - module.ModuleType = typename.Replace(Constants.ActionToken, control); - - // admin controls need to load additional metadata from the IModuleControl interface - if (moduleid == module.ModuleId) - { - typename = module.ModuleType; - // check for core module actions component - if (Constants.DefaultModuleActions.Contains(control)) - { - typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, control); - } - Type moduletype = Type.GetType(typename); - if (moduletype != null) - { - var moduleobject = Activator.CreateInstance(moduletype); - module.SecurityAccessLevel = (SecurityAccessLevel)moduletype.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); - module.ControlTitle = (string)moduletype.GetProperty("Title").GetValue(moduleobject); - module.Actions = (string)moduletype.GetProperty("Actions").GetValue(moduleobject); - module.UseAdminContainer = (bool)moduletype.GetProperty("UseAdminContainer").GetValue(moduleobject); + typename = route.Replace(control + "=", ""); } } } - else - { - module.ModuleType = typename.Replace(Constants.ActionToken, Constants.DefaultAction); - } + module.ModuleType = typename.Replace(Constants.ActionToken, control); + // admin controls need to load additional metadata from the IModuleControl interface + if (moduleid == module.ModuleId) + { + typename = module.ModuleType; + // check for core module actions component + if (Constants.DefaultModuleActions.Contains(control)) + { + typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, control); + } + Type moduletype = Type.GetType(typename); + if (moduletype != null) + { + var moduleobject = Activator.CreateInstance(moduletype); + module.SecurityAccessLevel = (SecurityAccessLevel)moduletype.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); + module.ControlTitle = (string)moduletype.GetProperty("Title").GetValue(moduleobject); + module.Actions = (string)moduletype.GetProperty("Actions").GetValue(moduleobject); + module.UseAdminContainer = (bool)moduletype.GetProperty("UseAdminContainer").GetValue(moduleobject); + } + } } + else + { + module.ModuleType = typename.Replace(Constants.ActionToken, Constants.DefaultAction); + } + if (module.PageId == pageid) { @@ -442,31 +430,4 @@ return modules; } - private Alias GetAlias(string absoluteUri, List aliases) - { - - string aliasname; - Alias alias = null; - Uri uri = new Uri(absoluteUri); - - if (uri.Segments.Count() > 1) - { - // check if first path segment is an alias ( ie. a subfolder - www.domain.com/subfolder ) - aliasname = uri.Authority + "/" + uri.Segments[1]; - if (aliasname.EndsWith("/")) { aliasname = aliasname.Substring(0, aliasname.Length - 1); } - alias = aliases.Where(item => item.Name == aliasname).FirstOrDefault(); - } - if (alias == null) - { - aliasname = uri.Authority; - alias = aliases.Where(item => item.Name == aliasname).FirstOrDefault(); - } - if (alias == null && aliases.Count > 0) - { - // use first alias if Uri does not exist - alias = aliases.FirstOrDefault(); - } - return alias; - } - } \ No newline at end of file diff --git a/Oqtane.Client/Themes/ContainerBase.cs b/Oqtane.Client/Themes/ContainerBase.cs index cae6f771..867722f7 100644 --- a/Oqtane.Client/Themes/ContainerBase.cs +++ b/Oqtane.Client/Themes/ContainerBase.cs @@ -18,14 +18,6 @@ namespace Oqtane.Themes [CascadingParameter] protected Module ModuleState { get; set; } - protected ModuleDefinition ModuleDefinition - { - get - { - return PageState.ModuleDefinitions.Where(item => item.ModuleDefinitionName == ModuleState.ModuleDefinitionName).FirstOrDefault(); - } - } - public virtual string Name { get; set; } public string ThemePath() diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index e8b526d4..68a80cfe 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -80,7 +80,7 @@ @foreach (var moduledefinition in moduledefinitions) { - if (moduledefinition.Permissions == "[]" || UserSecurity.IsAuthorized(PageState.User, "Utilize", moduledefinition.Permissions)) + if (UserSecurity.IsAuthorized(PageState.User, "Utilize", moduledefinition.Permissions)) { } @@ -199,6 +199,7 @@ bool deleteconfirmation = false; string moduletype = "new"; List categories = new List(); + List ModuleDefinitions; List moduledefinitions; List pages = new List(); string pageid = ""; @@ -212,7 +213,7 @@ string display = "display: none;"; string message = ""; - protected override void OnParametersSet() + protected override async Task OnParametersSetAsync() { if (string.IsNullOrEmpty(ButtonClass)) { @@ -235,7 +236,8 @@ { pages?.Clear(); - foreach (ModuleDefinition moduledefinition in PageState.ModuleDefinitions) + ModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); + foreach (ModuleDefinition moduledefinition in ModuleDefinitions) { if (moduledefinition.Categories != "") { @@ -248,7 +250,7 @@ } } } - moduledefinitions = PageState.ModuleDefinitions.Where(item => item.Categories == "").ToList(); + moduledefinitions = ModuleDefinitions.Where(item => item.Categories == "").ToList(); foreach (Page p in PageState.Pages) { if (UserSecurity.IsAuthorized(PageState.User, "View", p.Permissions)) @@ -258,7 +260,8 @@ } var panes = PageState.Page.Panes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); pane = panes.Count() == 1 ? panes.SingleOrDefault() : ""; - containers = ThemeService.GetContainerTypes(PageState.Themes); + List themes = await ThemeService.GetThemesAsync(); + containers = ThemeService.GetContainerTypes(themes); containertype = PageState.Site.DefaultContainerType; } } @@ -268,11 +271,11 @@ string category = (string)e.Value; if (category == "-") { - moduledefinitions = PageState.ModuleDefinitions.Where(item => item.Categories == "").ToList(); + moduledefinitions = ModuleDefinitions.Where(item => item.Categories == "").ToList(); } else { - moduledefinitions = PageState.ModuleDefinitions.Where(item => item.Categories.Contains(category)).ToList(); + moduledefinitions = ModuleDefinitions.Where(item => item.Categories.Contains(category)).ToList(); } moduledefinitionname = "-"; StateHasChanged(); @@ -304,6 +307,7 @@ { Module module = new Module(); module.SiteId = PageState.Site.SiteId; + module.PageId = PageState.Page.PageId; module.ModuleDefinitionName = moduledefinitionname; module.Permissions = PageState.Page.Permissions; module = await ModuleService.AddModuleAsync(module); @@ -367,7 +371,7 @@ { if (PageState.Page.IsPersonalizable && PageState.User != null) { - await CreatePersonalizedPage(); + await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId); PageState.EditMode = true; NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0"), Reload.Page)); } @@ -458,67 +462,4 @@ await logger.Log(page.PageId, null, PageState.User.UserId, this.GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, ex, "Page Deleted {Page} {Error}", page, ex.Message); } } - - private async Task CreatePersonalizedPage() - { - Page page = new Page(); - page.SiteId = PageState.Page.SiteId; - page.Name = PageState.Page.Name; - page.Path = PageState.Page.Path; - page.ParentId = PageState.Page.ParentId; - page.Order = 0; - page.IsNavigation = false; - page.EditMode = false; - page.ThemeType = PageState.Page.ThemeType; - if (page.ThemeType == PageState.Site.DefaultThemeType) - { - page.ThemeType = ""; - } - page.LayoutType = PageState.Page.LayoutType; - if (page.LayoutType == PageState.Site.DefaultLayoutType) - { - page.LayoutType = ""; - } - page.Icon = PageState.Page.Icon; - List permissions = new List(); - permissions.Add(new PermissionString { PermissionName = "View", Permissions = "[" + PageState.User.UserId.ToString() + "]" }); - permissions.Add(new PermissionString { PermissionName = "Edit", Permissions = "[" + PageState.User.UserId.ToString() + "]" }); - page.Permissions = UserSecurity.SetPermissionStrings(permissions); - page.IsPersonalizable = false; - page.UserId = PageState.User.UserId; - page = await PageService.AddPageAsync(page); - - // copy modules - foreach (Module m in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId && !item.IsDeleted)) - { - Module module = new Module(); - module.SiteId = m.SiteId; - module.ModuleDefinitionName = m.ModuleDefinitionName; - permissions = new List(); - permissions.Add(new PermissionString { PermissionName = "View", Permissions = "[" + PageState.User.UserId.ToString() + "]" }); - permissions.Add(new PermissionString { PermissionName = "Edit", Permissions = "[" + PageState.User.UserId.ToString() + "]" }); - module.Permissions = UserSecurity.SetPermissionStrings(permissions); - module = await ModuleService.AddModuleAsync(module); - - string content = await ModuleService.ExportModuleAsync(m.ModuleId); - if (content != "") - { - await ModuleService.ImportModuleAsync(module.ModuleId, content); - } - - PageModule pagemodule = new PageModule(); - pagemodule.PageId = page.PageId; - pagemodule.ModuleId = module.ModuleId; - pagemodule.Title = m.Title; - pagemodule.Pane = m.Pane; - pagemodule.Order = m.Order; - pagemodule.ContainerType = m.ContainerType; - if (pagemodule.ContainerType == PageState.Site.DefaultContainerType) - { - pagemodule.ContainerType = ""; - } - - await PageModuleService.AddPageModuleAsync(pagemodule); - } - } } diff --git a/Oqtane.Client/Themes/Controls/ModuleActions.razor b/Oqtane.Client/Themes/Controls/ModuleActions.razor index 2c539166..d410808f 100644 --- a/Oqtane.Client/Themes/Controls/ModuleActions.razor +++ b/Oqtane.Client/Themes/Controls/ModuleActions.razor @@ -31,7 +31,7 @@ { actions = new List(); actions.Add(new ActionViewModel { Action = "settings", Name = "Manage Settings" }); - if (ModuleDefinition.ServerAssemblyName != "") + if (ModuleState.ModuleDefinition.ServerAssemblyName != "") { actions.Add(new ActionViewModel { Action = "import", Name = "Import Content" }); actions.Add(new ActionViewModel { Action = "export", Name = "Export Content" }); diff --git a/Oqtane.Server/Controllers/AliasController.cs b/Oqtane.Server/Controllers/AliasController.cs index bfd2f4c5..e07db160 100644 --- a/Oqtane.Server/Controllers/AliasController.cs +++ b/Oqtane.Server/Controllers/AliasController.cs @@ -5,6 +5,8 @@ using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure; +using System.Linq; +using System; namespace Oqtane.Controllers { @@ -22,6 +24,7 @@ namespace Oqtane.Controllers // GET: api/ [HttpGet] + [Authorize(Roles = Constants.AdminRole)] public IEnumerable Get() { return Aliases.GetAliases(); @@ -29,11 +32,32 @@ namespace Oqtane.Controllers // GET api//5 [HttpGet("{id}")] + [Authorize(Roles = Constants.AdminRole)] public Alias Get(int id) { return Aliases.GetAlias(id); } + // GET api//name/localhost:12345 + [HttpGet("name/{name}")] + public Alias Get(string name) + { + List aliases = Aliases.GetAliases().ToList(); + Alias alias = null; + alias = aliases.Where(item => item.Name == name).FirstOrDefault(); + if (alias == null && name.Contains("/")) + { + // lookup alias without folder name + alias = aliases.Where(item => item.Name == name.Substring(name.IndexOf("/") + 1)).FirstOrDefault(); + } + if (alias == null && aliases.Count > 0) + { + // use first alias if name does not exist + alias = aliases.FirstOrDefault(); + } + return alias; + } + // POST api/ [HttpPost] [Authorize(Roles = Constants.AdminRole)] diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index eeea4998..cf99306d 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -72,7 +72,17 @@ namespace Oqtane.Controllers [HttpGet("{id}")] public Models.File Get(int id) { - return Files.GetFile(id); + Models.File file = Files.GetFile(id); + if (UserPermissions.IsAuthorized(User, "View", file.Folder.Permissions)) + { + return file; + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access File {File}", file); + HttpContext.Response.StatusCode = 401; + return null; + } } // PUT api//5 @@ -85,6 +95,12 @@ namespace Oqtane.Controllers File = Files.UpdateFile(File); logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", File); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update File {File}", File); + HttpContext.Response.StatusCode = 401; + File = null; + } return File; } @@ -105,6 +121,11 @@ namespace Oqtane.Controllers } logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", File); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete File {FileId}", id); + HttpContext.Response.StatusCode = 401; + } } // GET api//upload?url=x&folderid=y @@ -130,6 +151,12 @@ namespace Oqtane.Controllers logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url); } } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Download File {Url} {FolderId}", url, folderid); + HttpContext.Response.StatusCode = 401; + file = null; + } return file; } @@ -170,6 +197,11 @@ namespace Oqtane.Controllers Files.AddFile(new Models.File { Name = upload, FolderId = folderid, Extension = fileinfo.Extension.Replace(".", ""), Size = (int)fileinfo.Length }); } } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Upload File {Folder} {File}", folder, file); + HttpContext.Response.StatusCode = 401; + } } } @@ -293,7 +325,9 @@ namespace Oqtane.Controllers } else { - return NotFound(); + logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access File {FileId}", id); + HttpContext.Response.StatusCode = 401; + return null; } } diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 2bc4bc90..5f1da3ba 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -28,21 +28,32 @@ namespace Oqtane.Controllers [HttpGet] public IEnumerable Get(string siteid) { - if (siteid == "") + List folders = new List(); + foreach(Folder folder in Folders.GetFolders(int.Parse(siteid))) { - return Folders.GetFolders(); - } - else - { - return Folders.GetFolders(int.Parse(siteid)); + if (UserPermissions.IsAuthorized(User, "Browse", folder.Permissions)) + { + folders.Add(folder); + } } + return folders; } // GET api//5 [HttpGet("{id}")] public Folder Get(int id) { - return Folders.GetFolder(id); + Folder folder = Folders.GetFolder(id); + if (UserPermissions.IsAuthorized(User, "Browse", folder.Permissions)) + { + return folder; + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Folder {Folder}", folder); + HttpContext.Response.StatusCode = 401; + return null; + } } // POST api/ @@ -50,16 +61,33 @@ namespace Oqtane.Controllers [Authorize(Roles = Constants.RegisteredRole)] public Folder Post([FromBody] Folder Folder) { - if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Edit", Folder.Permissions)) + if (ModelState.IsValid) { - Folder.Path = ""; - if (string.IsNullOrEmpty(Folder.Path) && Folder.ParentId != null) + string permissions; + if (Folder.ParentId != null) { - Folder parent = Folders.GetFolder(Folder.ParentId.Value); - Folder.Path = parent.Path + Folder.Name + "\\"; + permissions = Folders.GetFolder(Folder.ParentId.Value).Permissions; + } + else + { + permissions = UserSecurity.SetPermissionStrings(new List { new PermissionString { PermissionName = "Edit", Permissions = Constants.AdminRole } }); + } + if (UserPermissions.IsAuthorized(User, "Edit", permissions)) + { + if (string.IsNullOrEmpty(Folder.Path) && Folder.ParentId != null) + { + Folder parent = Folders.GetFolder(Folder.ParentId.Value); + Folder.Path = parent.Path + Folder.Name + "\\"; + } + Folder = Folders.AddFolder(Folder); + logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", Folder); + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Folder {Folder}", Folder); + HttpContext.Response.StatusCode = 401; + Folder = null; } - Folder = Folders.AddFolder(Folder); - logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", Folder); } return Folder; } @@ -71,7 +99,6 @@ namespace Oqtane.Controllers { if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Folder", Folder.FolderId, "Edit")) { - Folder.Path = ""; if (string.IsNullOrEmpty(Folder.Path) && Folder.ParentId != null) { Folder parent = Folders.GetFolder(Folder.ParentId.Value); @@ -80,6 +107,12 @@ namespace Oqtane.Controllers Folder = Folders.UpdateFolder(Folder); logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", Folder); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Folder {Folder}", Folder); + HttpContext.Response.StatusCode = 401; + Folder = null; + } return Folder; } @@ -103,6 +136,11 @@ namespace Oqtane.Controllers } logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Order Updated {SiteId} {FolderId} {ParentId}", siteid, folderid, parentid); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Folder Order {SiteId} {FolderId} {ParentId}", siteid, folderid, parentid); + HttpContext.Response.StatusCode = 401; + } } // DELETE api//5 @@ -115,6 +153,11 @@ namespace Oqtane.Controllers Folders.DeleteFolder(id); logger.Log(LogLevel.Information, this, LogFunction.Delete, "Folder Deleted {FolderId}", id); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Folder {FolderId}", id); + HttpContext.Response.StatusCode = 401; + } } } } diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index 46966d70..fbfcaf51 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -269,7 +269,7 @@ namespace Oqtane.Controllers var result = dbUpgrade.PerformUpgrade(); if (!result.Successful) { - // TODO: log result.Error.Message; + // TODO: log result.Error.Message - problem is logger is not available here } } } diff --git a/Oqtane.Server/Controllers/JobController.cs b/Oqtane.Server/Controllers/JobController.cs index 4dade1d9..e5647351 100644 --- a/Oqtane.Server/Controllers/JobController.cs +++ b/Oqtane.Server/Controllers/JobController.cs @@ -27,6 +27,7 @@ namespace Oqtane.Controllers // GET: api/ [HttpGet] + [Authorize(Roles = Constants.HostRole)] public IEnumerable Get() { return Jobs.GetJobs(); @@ -34,6 +35,7 @@ namespace Oqtane.Controllers // GET api//5 [HttpGet("{id}")] + [Authorize(Roles = Constants.HostRole)] public Job Get(int id) { return Jobs.GetJob(id); diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index 46a6f2fd..e61f1350 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -6,10 +6,6 @@ using Oqtane.Models; using Oqtane.Shared; using System.Linq; using System.Reflection; -using System; -using Oqtane.Modules; -using Microsoft.Extensions.DependencyInjection; -using System.Text.Json; using Oqtane.Infrastructure; using Oqtane.Security; @@ -21,16 +17,14 @@ namespace Oqtane.Controllers private readonly IModuleRepository Modules; private readonly IPageModuleRepository PageModules; private readonly IModuleDefinitionRepository ModuleDefinitions; - private readonly IServiceProvider ServiceProvider; private readonly IUserPermissions UserPermissions; private readonly ILogManager logger; - public ModuleController(IModuleRepository Modules, IPageModuleRepository PageModules, IModuleDefinitionRepository ModuleDefinitions, IServiceProvider ServiceProvider, IUserPermissions UserPermissions, ILogManager logger) + public ModuleController(IModuleRepository Modules, IPageModuleRepository PageModules, IModuleDefinitionRepository ModuleDefinitions, IUserPermissions UserPermissions, ILogManager logger) { this.Modules = Modules; this.PageModules = PageModules; this.ModuleDefinitions = ModuleDefinitions; - this.ServiceProvider = ServiceProvider; this.UserPermissions = UserPermissions; this.logger = logger; } @@ -39,36 +33,55 @@ namespace Oqtane.Controllers [HttpGet] public IEnumerable Get(string siteid) { - List modulelist = new List(); + List moduledefinitions = ModuleDefinitions.GetModuleDefinitions(int.Parse(siteid)).ToList(); + List modules = new List(); foreach (PageModule pagemodule in PageModules.GetPageModules(int.Parse(siteid))) { - Models.Module module = new Models.Module(); - module.SiteId = pagemodule.Module.SiteId; - module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName; - module.Permissions = pagemodule.Module.Permissions; - module.CreatedBy = pagemodule.Module.CreatedBy; - module.CreatedOn = pagemodule.Module.CreatedOn; - module.ModifiedBy = pagemodule.Module.ModifiedBy; - module.ModifiedOn = pagemodule.Module.ModifiedOn; - module.IsDeleted = pagemodule.IsDeleted; + if (UserPermissions.IsAuthorized(User, "View", pagemodule.Module.Permissions)) + { + Models.Module module = new Models.Module(); + module.SiteId = pagemodule.Module.SiteId; + module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName; + module.Permissions = pagemodule.Module.Permissions; + module.CreatedBy = pagemodule.Module.CreatedBy; + module.CreatedOn = pagemodule.Module.CreatedOn; + module.ModifiedBy = pagemodule.Module.ModifiedBy; + module.ModifiedOn = pagemodule.Module.ModifiedOn; + module.IsDeleted = pagemodule.IsDeleted; - module.PageModuleId = pagemodule.PageModuleId; - module.ModuleId = pagemodule.ModuleId; - module.PageId = pagemodule.PageId; - module.Title = pagemodule.Title; - module.Pane = pagemodule.Pane; - module.Order = pagemodule.Order; - module.ContainerType = pagemodule.ContainerType; - modulelist.Add(module); + module.PageModuleId = pagemodule.PageModuleId; + module.ModuleId = pagemodule.ModuleId; + module.PageId = pagemodule.PageId; + module.Title = pagemodule.Title; + module.Pane = pagemodule.Pane; + module.Order = pagemodule.Order; + module.ContainerType = pagemodule.ContainerType; + + module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName); + + modules.Add(module); + } } - return modulelist; + return modules; } // GET api//5 [HttpGet("{id}")] public Models.Module Get(int id) { - return Modules.GetModule(id); + Models.Module module = Modules.GetModule(id); + if (UserPermissions.IsAuthorized(User, "View", module.Permissions)) + { + List moduledefinitions = ModuleDefinitions.GetModuleDefinitions(module.SiteId).ToList(); + module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName); + return module; + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Module {Module}", module); + HttpContext.Response.StatusCode = 401; + return null; + } } // POST api/ @@ -76,11 +89,17 @@ namespace Oqtane.Controllers [Authorize(Roles = Constants.RegisteredRole)] public Models.Module Post([FromBody] Models.Module Module) { - if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Edit", Module.Permissions)) + if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Page", Module.PageId, "Edit")) { Module = Modules.AddModule(Module); logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Added {Module}", Module); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Module {Module}", Module); + HttpContext.Response.StatusCode = 401; + Module = null; + } return Module; } @@ -94,6 +113,12 @@ namespace Oqtane.Controllers Module = Modules.UpdateModule(Module); logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", Module); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Module {Module}", Module); + HttpContext.Response.StatusCode = 401; + Module = null; + } return Module; } @@ -107,6 +132,11 @@ namespace Oqtane.Controllers Modules.DeleteModule(id); logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Deleted {ModuleId}", id); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Module {ModuleId}", id); + HttpContext.Response.StatusCode = 401; + } } // GET api//export?moduleid=x @@ -115,48 +145,14 @@ namespace Oqtane.Controllers public string Export(int moduleid) { string content = ""; - if (UserPermissions.IsAuthorized(User, "Module", moduleid, "View")) + if (UserPermissions.IsAuthorized(User, "Module", moduleid, "Edit")) { - try - { - Models.Module module = Modules.GetModule(moduleid); - if (module != null) - { - List moduledefinitions = ModuleDefinitions.GetModuleDefinitions(module.SiteId).ToList(); - ModuleDefinition moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault(); - if (moduledefinition != null) - { - ModuleContent modulecontent = new ModuleContent(); - modulecontent.ModuleDefinitionName = moduledefinition.ModuleDefinitionName; - modulecontent.Version = moduledefinition.Version; - modulecontent.Content = ""; - - if (moduledefinition.ServerAssemblyName != "") - { - Assembly assembly = AppDomain.CurrentDomain.GetAssemblies() - .Where(item => item.FullName.StartsWith(moduledefinition.ServerAssemblyName)).FirstOrDefault(); - if (assembly != null) - { - Type moduletype = assembly.GetTypes() - .Where(item => item.Namespace != null) - .Where(item => item.Namespace.StartsWith(moduledefinition.ModuleDefinitionName.Substring(0, moduledefinition.ModuleDefinitionName.IndexOf(",")))) - .Where(item => item.GetInterfaces().Contains(typeof(IPortable))).FirstOrDefault(); - if (moduletype != null) - { - var moduleobject = ActivatorUtilities.CreateInstance(ServiceProvider, moduletype); - modulecontent.Content = ((IPortable)moduleobject).ExportModule(module); - } - } - } - content = JsonSerializer.Serialize(modulecontent); - logger.Log(LogLevel.Information, this, LogFunction.Read, "Module Content Exported {ModuleId}", moduleid); - } - } - } - catch - { - // error occurred during export - } + content = Modules.ExportModule(moduleid); + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Other, "User Not Authorized To Export Module {ModuleId}", moduleid); + HttpContext.Response.StatusCode = 401; } return content; } @@ -169,45 +165,12 @@ namespace Oqtane.Controllers bool success = false; if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Module", moduleid, "Edit")) { - try - { - Models.Module module = Modules.GetModule(moduleid); - if (module != null) - { - List moduledefinitions = ModuleDefinitions.GetModuleDefinitions(module.SiteId).ToList(); - ModuleDefinition moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault(); - if (moduledefinition != null) - { - ModuleContent modulecontent = JsonSerializer.Deserialize(Content); - if (modulecontent.ModuleDefinitionName == moduledefinition.ModuleDefinitionName) - { - if (moduledefinition.ServerAssemblyName != "") - { - Assembly assembly = AppDomain.CurrentDomain.GetAssemblies() - .Where(item => item.FullName.StartsWith(moduledefinition.ServerAssemblyName)).FirstOrDefault(); - if (assembly != null) - { - Type moduletype = assembly.GetTypes() - .Where(item => item.Namespace != null) - .Where(item => item.Namespace.StartsWith(moduledefinition.ModuleDefinitionName.Substring(0, moduledefinition.ModuleDefinitionName.IndexOf(",")))) - .Where(item => item.GetInterfaces().Contains(typeof(IPortable))).FirstOrDefault(); - if (moduletype != null) - { - var moduleobject = ActivatorUtilities.CreateInstance(ServiceProvider, moduletype); - ((IPortable)moduleobject).ImportModule(module, modulecontent.Content, modulecontent.Version); - success = true; - logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Content Imported {ModuleId}", moduleid); - } - } - } - } - } - } - } - catch - { - // error occurred during import - } + success = Modules.ImportModule(moduleid, Content); + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Other, "User Not Authorized To Import Module {ModuleId}", moduleid); + HttpContext.Response.StatusCode = 401; } return success; } diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 7845890f..1a41edb1 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -9,6 +9,7 @@ using System.IO; using System.Reflection; using System.Linq; using Microsoft.AspNetCore.Hosting; +using Oqtane.Security; namespace Oqtane.Controllers { @@ -16,13 +17,15 @@ namespace Oqtane.Controllers public class ModuleDefinitionController : Controller { private readonly IModuleDefinitionRepository ModuleDefinitions; + private readonly IUserPermissions UserPermissions; private readonly IInstallationManager InstallationManager; private readonly IWebHostEnvironment environment; private readonly ILogManager logger; - public ModuleDefinitionController(IModuleDefinitionRepository ModuleDefinitions, IInstallationManager InstallationManager, IWebHostEnvironment environment, ILogManager logger) + public ModuleDefinitionController(IModuleDefinitionRepository ModuleDefinitions, IUserPermissions UserPermissions, IInstallationManager InstallationManager, IWebHostEnvironment environment, ILogManager logger) { this.ModuleDefinitions = ModuleDefinitions; + this.UserPermissions = UserPermissions; this.InstallationManager = InstallationManager; this.environment = environment; this.logger = logger; @@ -32,9 +35,35 @@ namespace Oqtane.Controllers [HttpGet] public IEnumerable Get(int siteid) { - return ModuleDefinitions.GetModuleDefinitions(siteid); + List moduledefinitions = new List(); + foreach(ModuleDefinition moduledefinition in ModuleDefinitions.GetModuleDefinitions(siteid)) + { + if (UserPermissions.IsAuthorized(User, "Utilize", moduledefinition.Permissions)) + { + moduledefinitions.Add(moduledefinition); + } + } + return moduledefinitions; } + // GET api//5?siteid=x + [HttpGet("{id}")] + public ModuleDefinition Get(int id, string siteid) + { + ModuleDefinition moduledefinition = ModuleDefinitions.GetModuleDefinition(id, int.Parse(siteid)); + if (UserPermissions.IsAuthorized(User, "Utilize", moduledefinition.Permissions)) + { + return moduledefinition; + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access ModuleDefinition {ModuleDefinition}", moduledefinition); + HttpContext.Response.StatusCode = 401; + return null; + } + } + + // GET api//filename [HttpGet("{filename}")] public IActionResult Get(string assemblyname) @@ -93,5 +122,6 @@ namespace Oqtane.Controllers InstallationManager.RestartApplication(); } } + } } diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 9109b257..ec5a7dbf 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -14,12 +14,16 @@ namespace Oqtane.Controllers public class PageController : Controller { private readonly IPageRepository Pages; + private readonly IModuleRepository Modules; + private readonly IPageModuleRepository PageModules; private readonly IUserPermissions UserPermissions; private readonly ILogManager logger; - public PageController(IPageRepository Pages, IUserPermissions UserPermissions, ILogManager logger) + public PageController(IPageRepository Pages, IModuleRepository Modules, IPageModuleRepository PageModules, IUserPermissions UserPermissions, ILogManager logger) { this.Pages = Pages; + this.Modules = Modules; + this.PageModules = PageModules; this.UserPermissions = UserPermissions; this.logger = logger; } @@ -28,27 +32,39 @@ namespace Oqtane.Controllers [HttpGet] public IEnumerable Get(string siteid) { - if (siteid == "") + List pages = new List(); + foreach (Page page in Pages.GetPages(int.Parse(siteid))) { - return Pages.GetPages(); - } - else - { - return Pages.GetPages(int.Parse(siteid)); + if (UserPermissions.IsAuthorized(User, "View", page.Permissions)) + { + pages.Add(page); + } } + return pages; } // GET api//5?userid=x [HttpGet("{id}")] public Page Get(int id, string userid) { + Page page; if (string.IsNullOrEmpty(userid)) { - return Pages.GetPage(id); + page = Pages.GetPage(id); } else { - return Pages.GetPage(id, int.Parse(userid)); + page = Pages.GetPage(id, int.Parse(userid)); + } + if (UserPermissions.IsAuthorized(User, "View", page.Permissions)) + { + return page; + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Page {Page}", page); + HttpContext.Response.StatusCode = 401; + return null; } } @@ -57,14 +73,95 @@ namespace Oqtane.Controllers [Authorize(Roles = Constants.RegisteredRole)] public Page Post([FromBody] Page Page) { - if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Edit", Page.Permissions)) + if (ModelState.IsValid) { - Page = Pages.AddPage(Page); - logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", Page); + string permissions; + if (Page.ParentId != null) + { + permissions = Pages.GetPage(Page.ParentId.Value).Permissions; + } + else + { + permissions = UserSecurity.SetPermissionStrings(new List { new PermissionString { PermissionName = "Edit", Permissions = Constants.AdminRole } }); + } + + if (UserPermissions.IsAuthorized(User, "Edit", permissions)) + { + Page = Pages.AddPage(Page); + logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", Page); + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Page {Page}", Page); + HttpContext.Response.StatusCode = 401; + Page = null; + } } return Page; } + // POST api//5?userid=x + [HttpPost("{id}")] + [Authorize(Roles = Constants.RegisteredRole)] + public Page Post(int id, string userid) + { + Page page = null; + Page parent = Pages.GetPage(id); + if (parent != null && parent.IsPersonalizable && !string.IsNullOrEmpty(userid)) + { + page = new Page(); + page.SiteId = parent.SiteId; + page.Name = parent.Name; + page.Path = parent.Path; + page.ParentId = parent.PageId; + page.Order = 0; + page.IsNavigation = false; + page.EditMode = false; + page.ThemeType = parent.ThemeType; + page.LayoutType = parent.LayoutType; + page.Icon = parent.Icon; + List permissions = new List(); + permissions.Add(new PermissionString { PermissionName = "View", Permissions = "[" + userid + "]" }); + permissions.Add(new PermissionString { PermissionName = "Edit", Permissions = "[" + userid + "]" }); + page.Permissions = UserSecurity.SetPermissionStrings(permissions); + page.IsPersonalizable = false; + page.UserId = int.Parse(userid); + page = Pages.AddPage(page); + + // copy modules + List pagemodules = PageModules.GetPageModules(page.SiteId).ToList(); + foreach (PageModule pm in pagemodules.Where(item => item.PageId == parent.PageId && !item.IsDeleted)) + { + Module module = new Module(); + module.SiteId = page.SiteId; + module.PageId = page.PageId; + module.ModuleDefinitionName = pm.Module.ModuleDefinitionName; + permissions = new List(); + permissions.Add(new PermissionString { PermissionName = "View", Permissions = "[" + userid + "]" }); + permissions.Add(new PermissionString { PermissionName = "Edit", Permissions = "[" + userid + "]" }); + module.Permissions = UserSecurity.SetPermissionStrings(permissions); + module = Modules.AddModule(module); + + string content = Modules.ExportModule(pm.ModuleId); + if (content != "") + { + Modules.ImportModule(module.ModuleId, content); + } + + PageModule pagemodule = new PageModule(); + pagemodule.PageId = page.PageId; + pagemodule.ModuleId = module.ModuleId; + pagemodule.Title = pm.Title; + pagemodule.Pane = pm.Pane; + pagemodule.Order = pm.Order; + pagemodule.ContainerType = pm.ContainerType; + + PageModules.AddPageModule(pagemodule); + } + } + return page; + } + // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.RegisteredRole)] @@ -75,6 +172,12 @@ namespace Oqtane.Controllers Page = Pages.UpdatePage(Page); logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", Page); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Page {Page}", Page); + HttpContext.Response.StatusCode = 401; + Page = null; + } return Page; } @@ -98,6 +201,11 @@ namespace Oqtane.Controllers } logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Order Updated {SiteId} {PageId} {ParentId}", siteid, pageid, parentid); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Page Order {SiteId} {PageId} {ParentId}", siteid, pageid, parentid); + HttpContext.Response.StatusCode = 401; + } } // DELETE api//5 @@ -110,6 +218,11 @@ namespace Oqtane.Controllers Pages.DeletePage(id); logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Deleted {PageId}", id); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Page {PageId}", id); + HttpContext.Response.StatusCode = 401; + } } } } diff --git a/Oqtane.Server/Controllers/PageModuleController.cs b/Oqtane.Server/Controllers/PageModuleController.cs index 99ba628d..1f9ff8c8 100644 --- a/Oqtane.Server/Controllers/PageModuleController.cs +++ b/Oqtane.Server/Controllers/PageModuleController.cs @@ -14,12 +14,14 @@ namespace Oqtane.Controllers public class PageModuleController : Controller { private readonly IPageModuleRepository PageModules; + private readonly IModuleRepository Modules; private readonly IUserPermissions UserPermissions; private readonly ILogManager logger; - public PageModuleController(IPageModuleRepository PageModules, IUserPermissions UserPermissions, ILogManager logger) + public PageModuleController(IPageModuleRepository PageModules, IModuleRepository Modules, IUserPermissions UserPermissions, ILogManager logger) { this.PageModules = PageModules; + this.Modules = Modules; this.UserPermissions = UserPermissions; this.logger = logger; } @@ -28,14 +30,34 @@ namespace Oqtane.Controllers [HttpGet("{id}")] public PageModule Get(int id) { - return PageModules.GetPageModule(id); + PageModule pagemodule = PageModules.GetPageModule(id); + if (UserPermissions.IsAuthorized(User, "View", pagemodule.Module.Permissions)) + { + return pagemodule; + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access PageModule {PageModule}", pagemodule); + HttpContext.Response.StatusCode = 401; + return null; + } } // GET: api//pageid/moduleid [HttpGet("{pageid}/{moduleid}")] public PageModule Get(int pageid, int moduleid) { - return PageModules.GetPageModule(pageid, moduleid); + PageModule pagemodule = PageModules.GetPageModule(pageid, moduleid); + if (UserPermissions.IsAuthorized(User, "View", pagemodule.Module.Permissions)) + { + return pagemodule; + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access PageModule {PageModule}", pagemodule); + HttpContext.Response.StatusCode = 401; + return null; + } } // POST api/ @@ -48,6 +70,12 @@ namespace Oqtane.Controllers PageModule = PageModules.AddPageModule(PageModule); logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Module Added {PageModule}", PageModule); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add PageModule {PageModule}", PageModule); + HttpContext.Response.StatusCode = 401; + PageModule = null; + } return PageModule; } @@ -56,11 +84,17 @@ namespace Oqtane.Controllers [Authorize(Roles = Constants.RegisteredRole)] public PageModule Put(int id, [FromBody] PageModule PageModule) { - if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Page", PageModule.PageId, "Edit")) + if (ModelState.IsValid && UserPermissions.IsAuthorized(User, "Module", PageModule.ModuleId, "Edit")) { PageModule = PageModules.UpdatePageModule(PageModule); logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Updated {PageModule}", PageModule); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update PageModule {PageModule}", PageModule); + HttpContext.Response.StatusCode = 401; + PageModule = null; + } return PageModule; } @@ -84,6 +118,11 @@ namespace Oqtane.Controllers } logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Order Updated {PageId} {Pane}", pageid, pane); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Page Module Order {PageId} {Pane}", pageid, pane); + HttpContext.Response.StatusCode = 401; + } } // DELETE api//5 @@ -97,6 +136,11 @@ namespace Oqtane.Controllers PageModules.DeletePageModule(id); logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Module Deleted {PageModuleId}", id); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete PageModule {PageModuleId}", id); + HttpContext.Response.StatusCode = 401; + } } } } diff --git a/Oqtane.Server/Controllers/PermissionController.cs b/Oqtane.Server/Controllers/PermissionController.cs deleted file mode 100644 index 8cd68430..00000000 --- a/Oqtane.Server/Controllers/PermissionController.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; -using Oqtane.Models; -using Oqtane.Shared; -using Oqtane.Infrastructure; - -namespace Oqtane.Controllers -{ - [Route("{site}/api/[controller]")] - public class PermissionController : Controller - { - private readonly IPermissionRepository Permissions; - private readonly ILogManager logger; - - public PermissionController(IPermissionRepository Permissions, ILogManager logger) - { - this.Permissions = Permissions; - this.logger = logger; - } - - // GET: api/ - [HttpGet] - public IEnumerable Get(string entityname, int entityid, string permissionname) - { - return Permissions.GetPermissions(entityname, entityid, permissionname); - } - - // GET api//5 - [HttpGet("{id}")] - public Permission Get(int id) - { - return Permissions.GetPermission(id); - } - - // POST api/ - [HttpPost] - [Authorize(Roles = Constants.AdminRole)] - public Permission Post([FromBody] Permission Permission) - { - if (ModelState.IsValid) - { - Permission = Permissions.AddPermission(Permission); - logger.Log(LogLevel.Information, this, LogFunction.Create, "Permission Added {Permission}", Permission); - } - return Permission; - } - - // PUT api//5 - [HttpPut("{id}")] - [Authorize(Roles = Constants.AdminRole)] - public Permission Put(int id, [FromBody] Permission Permission) - { - if (ModelState.IsValid) - { - Permission = Permissions.UpdatePermission(Permission); - logger.Log(LogLevel.Information, this, LogFunction.Update, "Permission Updated {Permission}", Permission); - } - return Permission; - } - - // DELETE api//5 - [HttpDelete("{id}")] - [Authorize(Roles = Constants.AdminRole)] - public void Delete(int id) - { - Permissions.DeletePermission(id); - logger.Log(LogLevel.Information, this, LogFunction.Delete, "Permission Deleted {PermissionId}", id); - } - } -} diff --git a/Oqtane.Server/Controllers/RoleController.cs b/Oqtane.Server/Controllers/RoleController.cs index c562a0f5..a2b978ad 100644 --- a/Oqtane.Server/Controllers/RoleController.cs +++ b/Oqtane.Server/Controllers/RoleController.cs @@ -22,6 +22,7 @@ namespace Oqtane.Controllers // GET: api/?siteid=x [HttpGet] + [Authorize(Roles = Constants.RegisteredRole)] public IEnumerable Get(string siteid) { return Roles.GetRoles(int.Parse(siteid)); @@ -29,6 +30,7 @@ namespace Oqtane.Controllers // GET api//5 [HttpGet("{id}")] + [Authorize(Roles = Constants.RegisteredRole)] public Role Get(int id) { return Roles.GetRole(id); diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index e55075d7..bad1ea83 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Authorization; using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using Oqtane.Security; using Oqtane.Infrastructure; +using System.Linq; +using System.Security.Claims; +using Microsoft.AspNetCore.Http; namespace Oqtane.Controllers { @@ -13,13 +15,17 @@ namespace Oqtane.Controllers public class SettingController : Controller { private readonly ISettingRepository Settings; + private readonly IPageModuleRepository PageModules; private readonly IUserPermissions UserPermissions; + private readonly IHttpContextAccessor Accessor; private readonly ILogManager logger; - public SettingController(ISettingRepository Settings, IUserPermissions UserPermissions, ILogManager logger) + public SettingController(ISettingRepository Settings, IPageModuleRepository PageModules, IUserPermissions UserPermissions, IHttpContextAccessor Accessor, ILogManager logger) { this.Settings = Settings; + this.PageModules = PageModules; this.UserPermissions = UserPermissions; + this.Accessor = Accessor; this.logger = logger; } @@ -27,62 +33,117 @@ namespace Oqtane.Controllers [HttpGet] public IEnumerable Get(string entityname, int entityid) { - return Settings.GetSettings(entityname, entityid); + List settings = new List(); + if (IsAuthorized(entityname, entityid, "View")) + { + settings = Settings.GetSettings(entityname, entityid).ToList(); + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Settings {EntityName} {EntityId}", entityname, entityid); + HttpContext.Response.StatusCode = 401; + } + return settings; } // GET api//5 [HttpGet("{id}")] public Setting Get(int id) { - return Settings.GetSetting(id); + Setting setting = Settings.GetSetting(id); + if (IsAuthorized(setting.EntityName, setting.EntityId, "View")) + { + return setting; + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Setting {Setting}", setting); + HttpContext.Response.StatusCode = 401; + return null; + } } // POST api/ [HttpPost] - [Authorize] public Setting Post([FromBody] Setting Setting) { - if (ModelState.IsValid && IsAuthorized(Setting.EntityName, Setting.EntityId)) + if (ModelState.IsValid && IsAuthorized(Setting.EntityName, Setting.EntityId, "Edit")) { Setting = Settings.AddSetting(Setting); logger.Log(LogLevel.Information, this, LogFunction.Create, "Setting Added {Setting}", Setting); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Setting {Setting}", Setting); + HttpContext.Response.StatusCode = 401; + Setting = null; + } return Setting; } // PUT api//5 [HttpPut("{id}")] - [Authorize] public Setting Put(int id, [FromBody] Setting Setting) { - if (ModelState.IsValid && IsAuthorized(Setting.EntityName, Setting.EntityId)) + if (ModelState.IsValid && IsAuthorized(Setting.EntityName, Setting.EntityId, "Edit")) { Setting = Settings.UpdateSetting(Setting); logger.Log(LogLevel.Information, this, LogFunction.Update, "Setting Updated {Setting}", Setting); } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Setting {Setting}", Setting); + HttpContext.Response.StatusCode = 401; + Setting = null; + } return Setting; } // DELETE api//5 [HttpDelete("{id}")] - [Authorize(Roles = Constants.AdminRole)] public void Delete(int id) { - Settings.DeleteSetting(id); - logger.Log(LogLevel.Information, this, LogFunction.Delete, "Setting Deleted {SettingId}", id); + Setting setting = Settings.GetSetting(id); + if (IsAuthorized(setting.EntityName, setting.EntityId, "Edit")) + { + Settings.DeleteSetting(id); + logger.Log(LogLevel.Information, this, LogFunction.Delete, "Setting Deleted {Setting}", setting); + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Setting {Setting}", setting); + HttpContext.Response.StatusCode = 401; + } } - private bool IsAuthorized(string EntityName, int EntityId) + private bool IsAuthorized(string EntityName, int EntityId, string PermissionName) { bool authorized = false; + if (EntityName == "PageModule") + { + EntityName = "Module"; + EntityId = PageModules.GetPageModule(EntityId).ModuleId; + } switch (EntityName) { - case "Module": - authorized = UserPermissions.IsAuthorized(User, EntityName, EntityId, "Edit"); + case "Host": + authorized = User.IsInRole(Constants.HostRole); break; - default: + case "Site": authorized = User.IsInRole(Constants.AdminRole); break; + case "Page": + case "Module": + case "Folder": + authorized = UserPermissions.IsAuthorized(User, EntityName, EntityId, PermissionName); + break; + case "User": + authorized = true; + if (PermissionName == "Edit") + { + authorized = User.IsInRole(Constants.AdminRole) || (int.Parse(Accessor.HttpContext.User.FindFirst(ClaimTypes.PrimarySid).Value) == EntityId); + } + break; } return authorized; } diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index 316a7ba3..23b5c4d7 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -29,6 +29,7 @@ namespace Oqtane.Controllers // GET: api/ [HttpGet] + [Authorize(Roles = Constants.HostRole)] public IEnumerable Get() { return Sites.GetSites(); diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index ecb9ad36..0e4915f0 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -30,6 +30,7 @@ namespace Oqtane.Controllers // GET: api/ [HttpGet] + [Authorize(Roles = Constants.RegisteredRole)] public IEnumerable Get() { return Themes.GetThemes(); diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 0660b5ff..b01404af 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -44,6 +44,7 @@ namespace Oqtane.Controllers // GET api//5?siteid=x [HttpGet("{id}")] + [Authorize] public User Get(int id, string siteid) { User user = Users.GetUser(id); @@ -172,18 +173,27 @@ namespace Oqtane.Controllers { if (ModelState.IsValid) { - if (User.Password != "") + if (base.User.IsInRole(Constants.AdminRole) || base.User.Identity.Name == User.Username) { - IdentityUser identityuser = await IdentityUserManager.FindByNameAsync(User.Username); - if (identityuser != null) + if (User.Password != "") { - identityuser.PasswordHash = IdentityUserManager.PasswordHasher.HashPassword(identityuser, User.Password); - await IdentityUserManager.UpdateAsync(identityuser); + IdentityUser identityuser = await IdentityUserManager.FindByNameAsync(User.Username); + if (identityuser != null) + { + identityuser.PasswordHash = IdentityUserManager.PasswordHasher.HashPassword(identityuser, User.Password); + await IdentityUserManager.UpdateAsync(identityuser); + } } + User = Users.UpdateUser(User); + User.Password = ""; // remove sensitive information + logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", User); + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update User {User}", User); + HttpContext.Response.StatusCode = 401; + User = null; } - User = Users.UpdateUser(User); - User.Password = ""; // remove sensitive information - logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", User); } return User; } diff --git a/Oqtane.Server/Controllers/UserRoleController.cs b/Oqtane.Server/Controllers/UserRoleController.cs index 7601818e..136c019f 100644 --- a/Oqtane.Server/Controllers/UserRoleController.cs +++ b/Oqtane.Server/Controllers/UserRoleController.cs @@ -22,6 +22,7 @@ namespace Oqtane.Controllers // GET: api/?userid=x [HttpGet] + [Authorize] public IEnumerable Get(string siteid) { return UserRoles.GetUserRoles(int.Parse(siteid)); @@ -29,6 +30,7 @@ namespace Oqtane.Controllers // GET api//5 [HttpGet("{id}")] + [Authorize] public UserRole Get(int id) { return UserRoles.GetUserRole(id); diff --git a/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs index 383da311..c629d76f 100644 --- a/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs @@ -6,8 +6,8 @@ namespace Oqtane.Repository public interface IModuleDefinitionRepository { IEnumerable GetModuleDefinitions(int SideId); + ModuleDefinition GetModuleDefinition(int ModuleDefinitionId, int SideId); void UpdateModuleDefinition(ModuleDefinition ModuleDefinition); void DeleteModuleDefinition(int ModuleDefinitionId, int SiteId); - } } diff --git a/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs index d7ed6251..262bcc90 100644 --- a/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs @@ -10,5 +10,7 @@ namespace Oqtane.Repository Module UpdateModule(Module Module); Module GetModule(int ModuleId); void DeleteModule(int ModuleId); + string ExportModule(int ModuleId); + bool ImportModule(int ModuleId, string Content); } } diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index ea05c552..c1445046 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -23,7 +23,33 @@ namespace Oqtane.Repository this.Permissions = Permissions; } - private List LoadModuleDefinitions(int SiteId) + public IEnumerable GetModuleDefinitions(int SiteId) + { + return LoadModuleDefinitions(SiteId); + } + + public ModuleDefinition GetModuleDefinition(int ModuleDefinitionId, int SiteId) + { + List moduledefinitions = LoadModuleDefinitions(SiteId); + return moduledefinitions.Find(item => item.ModuleDefinitionId == ModuleDefinitionId); + } + + public void UpdateModuleDefinition(ModuleDefinition ModuleDefinition) + { + Permissions.UpdatePermissions(ModuleDefinition.SiteId, "ModuleDefinition", ModuleDefinition.ModuleDefinitionId, ModuleDefinition.Permissions); + _cache.Remove("moduledefinitions"); + } + + public void DeleteModuleDefinition(int ModuleDefinitionId, int SiteId) + { + ModuleDefinition ModuleDefinition = db.ModuleDefinition.Find(ModuleDefinitionId); + Permissions.DeletePermissions(SiteId, "ModuleDefinition", ModuleDefinitionId); + db.ModuleDefinition.Remove(ModuleDefinition); + db.SaveChanges(); + _cache.Remove("moduledefinitions"); + } + + public List LoadModuleDefinitions(int SiteId) { List ModuleDefinitions; @@ -35,9 +61,9 @@ namespace Oqtane.Repository // sync module definitions with database List moduledefs = db.ModuleDefinition.ToList(); + List permissions = Permissions.GetPermissions(SiteId, "ModuleDefinition").ToList(); foreach (ModuleDefinition moduledefinition in ModuleDefinitions) { - IEnumerable permissions = Permissions.GetPermissions(SiteId, "ModuleDefinition").ToList(); ModuleDefinition moduledef = moduledefs.Where(item => item.ModuleDefinitionName == moduledefinition.ModuleDefinitionName).FirstOrDefault(); if (moduledef == null) { @@ -47,6 +73,10 @@ namespace Oqtane.Repository if (moduledefinition.Permissions != "") { Permissions.UpdatePermissions(SiteId, "ModuleDefinition", moduledef.ModuleDefinitionId, moduledefinition.Permissions); + foreach(Permission permission in Permissions.GetPermissions("ModuleDefinition", moduledef.ModuleDefinitionId)) + { + permissions.Add(permission); + } } } else @@ -65,6 +95,7 @@ namespace Oqtane.Repository // any remaining module definitions are orphans foreach (ModuleDefinition moduledefinition in moduledefs) { + Permissions.DeletePermissions(SiteId, "ModuleDefinition", moduledefinition.ModuleDefinitionId); db.ModuleDefinition.Remove(moduledefinition); // delete } @@ -128,7 +159,7 @@ namespace Oqtane.Repository ControlTypeTemplate = ModuleType + "." + Constants.ActionToken + ", " + typename[1], ControlTypeRoutes = "", AssemblyName = assembly.FullName.Split(",")[0], - Permissions = "" + Permissions = "[{\"PermissionName\":\"Utilize\",\"Permissions\":\"Administrators\"}]" }; } else @@ -150,8 +181,13 @@ namespace Oqtane.Repository ControlTypeTemplate = ModuleType + "." + Constants.ActionToken + ", " + typename[1], ControlTypeRoutes = "", AssemblyName = assembly.FullName.Split(",")[0], - Permissions = ((QualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "[{\"PermissionName\":\"Utilize\",\"Permissions\":\"Administrators\"}]" : "") + Permissions = "[{\"PermissionName\":\"Utilize\",\"Permissions\":\"" + Constants.AdminRole + "\"}]" }; + // make non-admin modules available by default to registered users on personalized pages + if (moduledefinition.Categories != "Admin") + { + moduledefinition.Permissions = "[{\"PermissionName\":\"Utilize\",\"Permissions\":\"" + Constants.AdminRole + ";" + Constants.RegisteredRole + "\"}]"; + } } moduledefinitions.Add(moduledefinition); index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == QualifiedModuleType); @@ -162,7 +198,7 @@ namespace Oqtane.Repository string actions = (string)modulecontroltype.GetProperty("Actions").GetValue(modulecontrolobject); if (actions != "") { - foreach(string action in actions.Split(',')) + foreach (string action in actions.Split(',')) { moduledefinition.ControlTypeRoutes += (action + "=" + modulecontroltype.FullName + ", " + typename[1] + ";"); } @@ -183,23 +219,5 @@ namespace Oqtane.Repository } return Value; } - - public IEnumerable GetModuleDefinitions(int SiteId) - { - return LoadModuleDefinitions(SiteId); - } - - public void UpdateModuleDefinition(ModuleDefinition ModuleDefinition) - { - Permissions.UpdatePermissions(ModuleDefinition.SiteId, "ModuleDefinition", ModuleDefinition.ModuleDefinitionId, ModuleDefinition.Permissions); - } - - public void DeleteModuleDefinition(int ModuleDefinitionId, int SiteId) - { - ModuleDefinition ModuleDefinition = db.ModuleDefinition.Find(ModuleDefinitionId); - Permissions.DeletePermissions(SiteId, "ModuleDefinition", ModuleDefinitionId); - db.ModuleDefinition.Remove(ModuleDefinition); - db.SaveChanges(); - } } } diff --git a/Oqtane.Server/Repository/ModuleRepository.cs b/Oqtane.Server/Repository/ModuleRepository.cs index c800e790..6d978ba4 100644 --- a/Oqtane.Server/Repository/ModuleRepository.cs +++ b/Oqtane.Server/Repository/ModuleRepository.cs @@ -2,6 +2,11 @@ using System.Collections.Generic; using System.Linq; using Oqtane.Models; +using System.Reflection; +using System; +using Oqtane.Modules; +using Microsoft.Extensions.DependencyInjection; +using System.Text.Json; namespace Oqtane.Repository { @@ -9,19 +14,23 @@ namespace Oqtane.Repository { private TenantDBContext db; private readonly IPermissionRepository Permissions; + private readonly IModuleDefinitionRepository ModuleDefinitions; + private readonly IServiceProvider ServiceProvider; - public ModuleRepository(TenantDBContext context, IPermissionRepository Permissions) + public ModuleRepository(TenantDBContext context, IPermissionRepository Permissions, IModuleDefinitionRepository ModuleDefinitions, IServiceProvider ServiceProvider) { db = context; this.Permissions = Permissions; + this.ModuleDefinitions = ModuleDefinitions; + this.ServiceProvider = ServiceProvider; } - public IEnumerable GetModules() + public IEnumerable GetModules() { return db.Module; } - public Module AddModule(Module Module) + public Models.Module AddModule(Models.Module Module) { db.Module.Add(Module); db.SaveChanges(); @@ -29,7 +38,7 @@ namespace Oqtane.Repository return Module; } - public Module UpdateModule(Module Module) + public Models.Module UpdateModule(Models.Module Module) { db.Entry(Module).State = EntityState.Modified; db.SaveChanges(); @@ -37,9 +46,9 @@ namespace Oqtane.Repository return Module; } - public Module GetModule(int ModuleId) + public Models.Module GetModule(int ModuleId) { - Module module = db.Module.Find(ModuleId); + Models.Module module = db.Module.Find(ModuleId); if (module != null) { List permissions = Permissions.GetPermissions("Module", module.ModuleId).ToList(); @@ -50,10 +59,100 @@ namespace Oqtane.Repository public void DeleteModule(int ModuleId) { - Module Module = db.Module.Find(ModuleId); + Models.Module Module = db.Module.Find(ModuleId); Permissions.DeletePermissions(Module.SiteId, "Module", ModuleId); db.Module.Remove(Module); db.SaveChanges(); } + + public string ExportModule(int ModuleId) + { + string content = ""; + try + { + Models.Module module = GetModule(ModuleId); + if (module != null) + { + List moduledefinitions = ModuleDefinitions.GetModuleDefinitions(module.SiteId).ToList(); + ModuleDefinition moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault(); + if (moduledefinition != null) + { + ModuleContent modulecontent = new ModuleContent(); + modulecontent.ModuleDefinitionName = moduledefinition.ModuleDefinitionName; + modulecontent.Version = moduledefinition.Version; + modulecontent.Content = ""; + + if (moduledefinition.ServerAssemblyName != "") + { + Assembly assembly = AppDomain.CurrentDomain.GetAssemblies() + .Where(item => item.FullName.StartsWith(moduledefinition.ServerAssemblyName)).FirstOrDefault(); + if (assembly != null) + { + Type moduletype = assembly.GetTypes() + .Where(item => item.Namespace != null) + .Where(item => item.Namespace.StartsWith(moduledefinition.ModuleDefinitionName.Substring(0, moduledefinition.ModuleDefinitionName.IndexOf(",")))) + .Where(item => item.GetInterfaces().Contains(typeof(IPortable))).FirstOrDefault(); + if (moduletype != null) + { + var moduleobject = ActivatorUtilities.CreateInstance(ServiceProvider, moduletype); + modulecontent.Content = ((IPortable)moduleobject).ExportModule(module); + } + } + } + content = JsonSerializer.Serialize(modulecontent); + } + } + } + catch + { + // error occurred during export + } + return content; + } + + public bool ImportModule(int ModuleId, string Content) + { + bool success = false; + try + { + Models.Module module = GetModule(ModuleId); + if (module != null) + { + List moduledefinitions = ModuleDefinitions.GetModuleDefinitions(module.SiteId).ToList(); + ModuleDefinition moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault(); + if (moduledefinition != null) + { + ModuleContent modulecontent = JsonSerializer.Deserialize(Content); + if (modulecontent.ModuleDefinitionName == moduledefinition.ModuleDefinitionName) + { + if (moduledefinition.ServerAssemblyName != "") + { + Assembly assembly = AppDomain.CurrentDomain.GetAssemblies() + .Where(item => item.FullName.StartsWith(moduledefinition.ServerAssemblyName)).FirstOrDefault(); + if (assembly != null) + { + Type moduletype = assembly.GetTypes() + .Where(item => item.Namespace != null) + .Where(item => item.Namespace.StartsWith(moduledefinition.ModuleDefinitionName.Substring(0, moduledefinition.ModuleDefinitionName.IndexOf(",")))) + .Where(item => item.GetInterfaces().Contains(typeof(IPortable))).FirstOrDefault(); + if (moduletype != null) + { + var moduleobject = ActivatorUtilities.CreateInstance(ServiceProvider, moduletype); + ((IPortable)moduleobject).ImportModule(module, modulecontent.Content, modulecontent.Version); + success = true; + } + } + } + } + } + } + } + catch + { + // error occurred during import + } + return success; + } + } } diff --git a/Oqtane.Shared/Models/Module.cs b/Oqtane.Shared/Models/Module.cs index b7e19e84..0466c326 100644 --- a/Oqtane.Shared/Models/Module.cs +++ b/Oqtane.Shared/Models/Module.cs @@ -49,6 +49,10 @@ namespace Oqtane.Models [NotMapped] public int PaneModuleCount { get; set; } + // ModuleDefinition + [NotMapped] + public ModuleDefinition ModuleDefinition { get; set; } + // IModuleControl properties [NotMapped] public SecurityAccessLevel SecurityAccessLevel { get; set; } From 44d855e6d4652c61e9a44a1cc05bf39bf1ad4769 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 18 Feb 2020 17:27:53 -0500 Subject: [PATCH 017/265] Re-create appsettings.json Mistakenly removed by PR #213 --- Oqtane.Server/appsettings.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Oqtane.Server/appsettings.json diff --git a/Oqtane.Server/appsettings.json b/Oqtane.Server/appsettings.json new file mode 100644 index 00000000..7f07c90a --- /dev/null +++ b/Oqtane.Server/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "" + } +} From 05eaf12003ea1eac27d1c8437d7fa6937a32cd48 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 18 Feb 2020 17:49:36 -0500 Subject: [PATCH 018/265] fix page management --- Oqtane.Client/Modules/Admin/Pages/Add.razor | 7 +- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 301 +++++++++--------- .../Controllers/NotificationController.cs | 10 +- Oqtane.Server/Controllers/PageController.cs | 2 +- .../Controllers/SettingController.cs | 7 +- Oqtane.Server/Infrastructure/LogManager.cs | 12 +- Oqtane.Server/Security/IUserPermissions.cs | 5 +- Oqtane.Server/Security/UserPermissions.cs | 23 +- 8 files changed, 197 insertions(+), 170 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 538f94af..7a619b57 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -4,6 +4,8 @@ @inject IPageService PageService @inject IThemeService ThemeService +@if (Themes != null) +{
@@ -139,8 +141,9 @@
- -Cancel + + Cancel +} @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 10cff51b..3eb2a97d 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -4,164 +4,167 @@ @inject IPageService PageService @inject IThemeService ThemeService - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- - - -
- - - -
- - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - -
+ + + +
+ + + +
+ + + -
- - - - @if (children != null && children.Count > 0 && (insert == "<" || insert == ">")) - { - - } -
- - - -
- - - -
- - - -
- - -
+ + + -
- - - + @if (children != null && children.Count > 0 && (insert == "<" || insert == ">")) { - + } - -
- - - -
- - - -
- -Cancel -
-
- +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + Cancel +
+
+ +} @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } diff --git a/Oqtane.Server/Controllers/NotificationController.cs b/Oqtane.Server/Controllers/NotificationController.cs index 8333f53f..2390297e 100644 --- a/Oqtane.Server/Controllers/NotificationController.cs +++ b/Oqtane.Server/Controllers/NotificationController.cs @@ -5,8 +5,8 @@ using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure; -using System.Security.Claims; using Microsoft.AspNetCore.Http; +using Oqtane.Security; namespace Oqtane.Controllers { @@ -14,13 +14,13 @@ namespace Oqtane.Controllers public class NotificationController : Controller { private readonly INotificationRepository Notifications; - private readonly IHttpContextAccessor Accessor; + private readonly IUserPermissions UserPermissions; private readonly ILogManager logger; - public NotificationController(INotificationRepository Notifications, IHttpContextAccessor Accessor, ILogManager logger) + public NotificationController(INotificationRepository Notifications, IUserPermissions UserPermissions, ILogManager logger) { this.Notifications = Notifications; - this.Accessor = Accessor; + this.UserPermissions = UserPermissions; this.logger = logger; } @@ -101,7 +101,7 @@ namespace Oqtane.Controllers bool authorized = true; if (userid != null) { - authorized = (int.Parse(Accessor.HttpContext.User.FindFirst(ClaimTypes.PrimarySid).Value) == userid); + authorized = (UserPermissions.GetUser(User).UserId == userid); } return authorized; } diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index ec5a7dbf..c7bb7ded 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -107,7 +107,7 @@ namespace Oqtane.Controllers { Page page = null; Page parent = Pages.GetPage(id); - if (parent != null && parent.IsPersonalizable && !string.IsNullOrEmpty(userid)) + if (parent != null && parent.IsPersonalizable && UserPermissions.GetUser(User).UserId == int.Parse(userid)) { page = new Page(); page.SiteId = parent.SiteId; diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index bad1ea83..fbc45cc4 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -6,7 +6,6 @@ using Oqtane.Shared; using Oqtane.Security; using Oqtane.Infrastructure; using System.Linq; -using System.Security.Claims; using Microsoft.AspNetCore.Http; namespace Oqtane.Controllers @@ -17,15 +16,13 @@ namespace Oqtane.Controllers private readonly ISettingRepository Settings; private readonly IPageModuleRepository PageModules; private readonly IUserPermissions UserPermissions; - private readonly IHttpContextAccessor Accessor; private readonly ILogManager logger; - public SettingController(ISettingRepository Settings, IPageModuleRepository PageModules, IUserPermissions UserPermissions, IHttpContextAccessor Accessor, ILogManager logger) + public SettingController(ISettingRepository Settings, IPageModuleRepository PageModules, IUserPermissions UserPermissions, ILogManager logger) { this.Settings = Settings; this.PageModules = PageModules; this.UserPermissions = UserPermissions; - this.Accessor = Accessor; this.logger = logger; } @@ -141,7 +138,7 @@ namespace Oqtane.Controllers authorized = true; if (PermissionName == "Edit") { - authorized = User.IsInRole(Constants.AdminRole) || (int.Parse(Accessor.HttpContext.User.FindFirst(ClaimTypes.PrimarySid).Value) == EntityId); + authorized = User.IsInRole(Constants.AdminRole) || (UserPermissions.GetUser(User).UserId == EntityId); } break; } diff --git a/Oqtane.Server/Infrastructure/LogManager.cs b/Oqtane.Server/Infrastructure/LogManager.cs index 3ec24b76..2beb795e 100644 --- a/Oqtane.Server/Infrastructure/LogManager.cs +++ b/Oqtane.Server/Infrastructure/LogManager.cs @@ -5,8 +5,8 @@ using System.Text.Json; using Oqtane.Repository; using Microsoft.Extensions.Configuration; using Microsoft.AspNetCore.Http; -using System.Security.Claims; using System.Collections.Generic; +using Oqtane.Security; namespace Oqtane.Infrastructure { @@ -15,13 +15,15 @@ namespace Oqtane.Infrastructure private readonly ILogRepository Logs; private readonly ITenantResolver TenantResolver; private readonly IConfigurationRoot Config; + private readonly IUserPermissions UserPermissions; private readonly IHttpContextAccessor Accessor; - public LogManager(ILogRepository Logs, ITenantResolver TenantResolver, IConfigurationRoot Config, IHttpContextAccessor Accessor) + public LogManager(ILogRepository Logs, ITenantResolver TenantResolver, IConfigurationRoot Config, IUserPermissions UserPermissions, IHttpContextAccessor Accessor) { this.Logs = Logs; this.TenantResolver = TenantResolver; this.Config = Config; + this.UserPermissions = UserPermissions; this.Accessor = Accessor; } @@ -37,9 +39,11 @@ namespace Oqtane.Infrastructure log.SiteId = alias.SiteId; log.PageId = null; log.ModuleId = null; - if (Accessor.HttpContext.User.FindFirst(ClaimTypes.PrimarySid) != null) + log.UserId = null; + User user = UserPermissions.GetUser(); + if (user != null) { - log.UserId = int.Parse(Accessor.HttpContext.User.FindFirst(ClaimTypes.PrimarySid).Value); + log.UserId = user.UserId; } HttpRequest request = Accessor.HttpContext.Request; if (request != null) diff --git a/Oqtane.Server/Security/IUserPermissions.cs b/Oqtane.Server/Security/IUserPermissions.cs index a04aff07..92e50d1d 100644 --- a/Oqtane.Server/Security/IUserPermissions.cs +++ b/Oqtane.Server/Security/IUserPermissions.cs @@ -1,4 +1,5 @@ -using System.Security.Claims; +using Oqtane.Models; +using System.Security.Claims; namespace Oqtane.Security { @@ -6,5 +7,7 @@ namespace Oqtane.Security { bool IsAuthorized(ClaimsPrincipal User, string EntityName, int EntityId, string PermissionName); bool IsAuthorized(ClaimsPrincipal User, string PermissionName, string Permissions); + User GetUser(ClaimsPrincipal User); + User GetUser(); } } diff --git a/Oqtane.Server/Security/UserPermissions.cs b/Oqtane.Server/Security/UserPermissions.cs index 43eb8e20..283d8738 100644 --- a/Oqtane.Server/Security/UserPermissions.cs +++ b/Oqtane.Server/Security/UserPermissions.cs @@ -1,4 +1,5 @@ -using Oqtane.Models; +using Microsoft.AspNetCore.Http; +using Oqtane.Models; using Oqtane.Repository; using System.Linq; using System.Security.Claims; @@ -8,10 +9,12 @@ namespace Oqtane.Security public class UserPermissions : IUserPermissions { private readonly IPermissionRepository Permissions; + private readonly IHttpContextAccessor Accessor; - public UserPermissions(IPermissionRepository Permissions) + public UserPermissions(IPermissionRepository Permissions, IHttpContextAccessor Accessor) { this.Permissions = Permissions; + this.Accessor = Accessor; } public bool IsAuthorized(ClaimsPrincipal User, string EntityName, int EntityId, string PermissionName) @@ -20,13 +23,22 @@ namespace Oqtane.Security } public bool IsAuthorized(ClaimsPrincipal User, string PermissionName, string Permissions) + { + return UserSecurity.IsAuthorized(GetUser(User), PermissionName, Permissions); + } + + public User GetUser(ClaimsPrincipal User) { User user = new User(); + user.Username = ""; + user.IsAuthenticated = false; user.UserId = -1; user.Roles = ""; if (User != null) { + user.Username = User.Identity.Name; + user.IsAuthenticated = User.Identity.IsAuthenticated; var idclaim = User.Claims.Where(item => item.Type == ClaimTypes.PrimarySid).FirstOrDefault(); if (idclaim != null) { @@ -39,7 +51,12 @@ namespace Oqtane.Security } } - return UserSecurity.IsAuthorized(user, PermissionName, Permissions); + return user; + } + + public User GetUser() + { + return GetUser(Accessor.HttpContext.User); } } } From bd2b355d361784fdc2241c7ad9007bd3346de659 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 18 Feb 2020 23:42:40 -0500 Subject: [PATCH 019/265] fix page management --- Oqtane.Client/Modules/Admin/Pages/Add.razor | 2 +- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 7a619b57..b5de908e 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -311,7 +311,7 @@ page.IsPersonalizable = (ispersonalizable == null ? false : Boolean.Parse(ispersonalizable)); page.UserId = null; - await PageService.AddPageAsync(page); + page = await PageService.AddPageAsync(page); await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId); await logger.LogInformation("Page Added {Page}", page); diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 3eb2a97d..2dc485d2 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -378,7 +378,7 @@ page.IsPersonalizable = (ispersonalizable == null ? false : Boolean.Parse(ispersonalizable)); page.UserId = null; - await PageService.UpdatePageAsync(page); + page = await PageService.UpdatePageAsync(page); await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId); if (currentparentid == "") { From 74d4b6412ef81d6039bc8b85223b01f17a26f707 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 19 Feb 2020 14:50:41 -0500 Subject: [PATCH 020/265] Ability to insert image in RichTextEditor --- .../Modules/Controls/RichTextEditor.razor | 54 ++++++++++++++----- .../Repository/ModuleDefinitionRepository.cs | 18 ++++--- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index 33f0b7e0..1f49e21d 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -2,6 +2,15 @@ @inherits ModuleBase @inject IJSRuntime JSRuntime +@if (filemanagervisible) +{ + + @((MarkupString)@message) +} +
+ +
+
@ToolbarContent
@@ -13,26 +22,24 @@ public RenderFragment ToolbarContent { get; set; } [Parameter] - public bool ReadOnly { get; set; } - = false; + public bool ReadOnly { get; set; } = false; [Parameter] - public string Placeholder { get; set; } - = "Compose an epic..."; + public string Placeholder { get; set; } = "Enter Your Content..."; [Parameter] - public string Theme { get; set; } - = "snow"; + public string Theme { get; set; } = "snow"; [Parameter] - public string DebugLevel { get; set; } - = "info"; + public string DebugLevel { get; set; } = "info"; private ElementReference EditorElement; private ElementReference ToolBar; + bool filemanagervisible = false; + FileManager filemanager; + string message = ""; - protected override async Task - OnAfterRenderAsync(bool firstRender) + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { @@ -82,10 +89,29 @@ EditorElement, mode); } - public async Task InsertImage(string ImageURL) + public async Task InsertImage() { - await RichTextEditorInterop.InsertImage( - JSRuntime, - EditorElement, ImageURL); + if (filemanagervisible) + { + int fileid = filemanager.GetFileId(); + if (fileid != -1) + { + await RichTextEditorInterop.InsertImage( + JSRuntime, + EditorElement, ContentUrl(fileid)); + filemanagervisible = false; + message = ""; + } + else + { + message = "
You Must Select An Image To Insert
"; + } + } + else + { + filemanagervisible = true; + message = ""; + } + StateHasChanged(); } } \ No newline at end of file diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index c1445046..c53c8396 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -159,7 +159,7 @@ namespace Oqtane.Repository ControlTypeTemplate = ModuleType + "." + Constants.ActionToken + ", " + typename[1], ControlTypeRoutes = "", AssemblyName = assembly.FullName.Split(",")[0], - Permissions = "[{\"PermissionName\":\"Utilize\",\"Permissions\":\"Administrators\"}]" + Permissions = "" }; } else @@ -181,13 +181,17 @@ namespace Oqtane.Repository ControlTypeTemplate = ModuleType + "." + Constants.ActionToken + ", " + typename[1], ControlTypeRoutes = "", AssemblyName = assembly.FullName.Split(",")[0], - Permissions = "[{\"PermissionName\":\"Utilize\",\"Permissions\":\"" + Constants.AdminRole + "\"}]" + Permissions = "" }; - // make non-admin modules available by default to registered users on personalized pages - if (moduledefinition.Categories != "Admin") - { - moduledefinition.Permissions = "[{\"PermissionName\":\"Utilize\",\"Permissions\":\"" + Constants.AdminRole + ";" + Constants.RegisteredRole + "\"}]"; - } + } + // permissions + if (moduledefinition.Categories == "Admin") + { + moduledefinition.Permissions = "[{\"PermissionName\":\"Utilize\",\"Permissions\":\"" + Constants.AdminRole + "\"}]"; + } + else + { + moduledefinition.Permissions = "[{\"PermissionName\":\"Utilize\",\"Permissions\":\"" + Constants.AdminRole + ";" + Constants.RegisteredRole + "\"}]"; } moduledefinitions.Add(moduledefinition); index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == QualifiedModuleType); From ece378c54fde41b42e556e886224025f04d40436 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Sat, 15 Feb 2020 09:07:29 +0300 Subject: [PATCH 021/265] Unable to deleted the root site --- Oqtane.Server/Controllers/SiteController.cs | 11 +++++++++-- Oqtane.Server/Repository/SiteRepository.cs | 11 +++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index 23b5c4d7..dc8361ec 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -87,8 +87,15 @@ namespace Oqtane.Controllers [Authorize(Roles = Constants.HostRole)] public void Delete(int id) { - Sites.DeleteSite(id); - logger.Log(LogLevel.Information, this, LogFunction.Delete, "Site Deleted {SiteId}", id); + if (Sites.GetSites().Count() > 1) + { + Sites.DeleteSite(id); + logger.Log(LogLevel.Information, this, LogFunction.Delete, "Site Deleted {SiteId}", id); + } + else + { + logger.Log(LogLevel.Warning, this, LogFunction.Delete, "Unable to delete the root site."); + } } } } diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index d206c763..6c569863 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -142,10 +142,13 @@ namespace Oqtane.Repository } public void DeleteSite(int siteId) - { - Site site = db.Site.Find(siteId); - db.Site.Remove(site); - db.SaveChanges(); + { + if (db.Site.Count() > 1) + { + var site = db.Site.Find(siteId); + db.Site.Remove(site); + db.SaveChanges(); + } } private void CreateSite(Site site) From d0e03ecbe2c982beb1a3554dfd2e41c228978fd1 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Sat, 15 Feb 2020 09:32:31 +0300 Subject: [PATCH 022/265] Disable the action button --- Oqtane.Client/Modules/Admin/Sites/Index.razor | 2 +- .../Modules/Controls/ActionDialog.razor | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Sites/Index.razor b/Oqtane.Client/Modules/Admin/Sites/Index.razor index 7133f8b5..9b758fdd 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Index.razor @@ -20,7 +20,7 @@ else - + @context.Name diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor index 3a8a94ce..aaffa1f7 100644 --- a/Oqtane.Client/Modules/Controls/ActionDialog.razor +++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor @@ -28,7 +28,14 @@ } @if (authorized) { - + if (Disabled) + { + + } + else + { + + } } @code { @@ -36,13 +43,13 @@ public string Header { get; set; } // required [Parameter] - public string Message { get; set; } // required + public string Message { get; set; } // required [Parameter] public string Text { get; set; } // optional - defaults to Action if not specified [Parameter] - public string Action { get; set; } // optional + public string Action { get; set; } // optional [Parameter] public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel @@ -50,6 +57,9 @@ [Parameter] public string Class { 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 From 0eef1790c3bc5fc71c12a971da87c7bfc62cfd8e Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Thu, 20 Feb 2020 17:16:14 +0300 Subject: [PATCH 023/265] Add Disabled property to ActionLink --- Oqtane.Client/Modules/Controls/ActionLink.razor | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index d3c3a08b..2ed9c3e7 100644 --- a/Oqtane.Client/Modules/Controls/ActionLink.razor +++ b/Oqtane.Client/Modules/Controls/ActionLink.razor @@ -4,7 +4,14 @@ @if (authorized) { - @text + if (Disabled) + { + @text + } + else + { + @text + } } @code { @@ -26,6 +33,9 @@ [Parameter] public string Style { 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 From a66ecbdfa2a6d16253c6bb32a7011a0608bfc065 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 20 Feb 2020 11:13:03 -0500 Subject: [PATCH 024/265] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8675866c..998e1b29 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ V.Next - Localization - Migrate to Code-Behind Pattern ( *.razor.cs ) - Generic Repository Pattern +- JwT token authentication # Background Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalker/) and is inspired by the DotNetNuke web application framework. Initially created as a proof of concept, Oqtane is a native Blazor application written from the ground up using modern .NET Core technology. It is a modular application framework offering a fully dynamic page compositing model, multi-site support, designer friendly templates (skins), and extensibility via third party modules. From 00914208ba24c6b5eb9610321998e8a167050ba2 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sat, 22 Feb 2020 09:56:28 -0500 Subject: [PATCH 025/265] added image preview to the file manager component --- .../Modules/Admin/ModuleDefinitions/Add.razor | 2 +- Oqtane.Client/Modules/Admin/Themes/Add.razor | 2 +- .../Modules/Admin/Upgrade/Index.razor | 2 +- .../Modules/Controls/FileManager.razor | 147 +++++++++++------- .../Modules/Controls/RichTextEditor.razor | 16 +- Oqtane.Server/Controllers/FileController.cs | 54 +++++-- Oqtane.Server/Oqtane.Server.csproj | 1 + Oqtane.Server/Repository/SiteRepository.cs | 2 +- Oqtane.Server/Scripts/00.00.00.sql | 2 + Oqtane.Shared/Models/File.cs | 2 + Oqtane.Shared/Models/Site.cs | 19 --- Oqtane.Shared/Shared/Constants.cs | 3 + 12 files changed, 164 insertions(+), 88 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index 4e534cc7..a051164f 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -11,7 +11,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor index 5ea8f8c5..70e03747 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Add.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -11,7 +11,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index 7448f435..d5e4c8a4 100644 --- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor +++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor @@ -11,7 +11,7 @@ - + diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index d61e05c2..2177f43e 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -6,60 +6,72 @@ @if (folders != null) { - - @if (showfiles) - { - + @if (string.IsNullOrEmpty(Folder)) + { + + } + @foreach (Folder folder in folders) + { + if (folder.FolderId == folderid) + { + + } + else + { + + } + } + + @if (showfiles) { - + } - else + @if (haseditpermission) { - +
+ @if (uploadmultiple) + { + + } + else + { + + } + + @if (showfiles && GetFileId() != -1) + { + + } + +
+ @((MarkupString)@message) } - } - - } - @if (haseditpermission) - { -
- @if (uploadmultiple) - { - - } - else - { - - } - - @if (showfiles && GetFileId() != -1) - { - - } - +
+
+ @if (@image != "") + { + @((MarkupString)@image) + } +
- @((MarkupString)@message) - } +
} @code { @@ -76,11 +88,12 @@ public string FileId { get; set; } // 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" + 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 + string id; List folders; int folderid = -1; @@ -94,6 +107,7 @@ bool uploadmultiple = false; bool haseditpermission = false; string message = ""; + string image = ""; protected override async Task OnInitializedAsync() { @@ -115,6 +129,7 @@ if (!string.IsNullOrEmpty(FileId)) { fileid = int.Parse(FileId); + await SetImage(); if (fileid != -1) { File file = await FileService.GetFileAsync(int.Parse(FileId)); @@ -131,7 +146,7 @@ if (!string.IsNullOrEmpty(Filter)) { - filter = Filter; + filter = "." + Filter.Replace(",",",."); } await GetFiles(); @@ -184,7 +199,7 @@ } } - private async void FolderChanged(ChangeEventArgs e) + private async Task FolderChanged(ChangeEventArgs e) { message = ""; try @@ -192,6 +207,7 @@ folderid = int.Parse((string)e.Value); await GetFiles(); fileid = -1; + image = ""; StateHasChanged(); } catch (Exception ex) @@ -201,13 +217,36 @@ } } - private void FileChanged(ChangeEventArgs e) + private async Task FileChanged(ChangeEventArgs e) { message = ""; fileid = int.Parse((string)e.Value); + await SetImage(); StateHasChanged(); } + private async Task SetImage() + { + image = ""; + if (fileid != -1) + { + File file = await FileService.GetFileAsync(fileid); + if (file.ImageHeight != 0 && file.ImageWidth != 0) + { + int maxwidth = 200; + int maxheight = 200; + + double ratioX = (double)maxwidth / (double)file.ImageWidth; + double ratioY = (double)maxheight / (double)file.ImageHeight; + double ratio = ratioX < ratioY ? ratioX : ratioY; + + image = "\"""; + } + } + } + private async Task UploadFile() { var interop = new Interop(jsRuntime); @@ -236,6 +275,7 @@ if (file != null) { fileid = file.FileId; + await SetImage(); } } StateHasChanged(); @@ -268,6 +308,7 @@ message = "
File Deleted
"; await GetFiles(); fileid = -1; + await SetImage(); StateHasChanged(); } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index 1f49e21d..affadeb7 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -4,11 +4,17 @@ @if (filemanagervisible) { - + @((MarkupString)@message) +
}
+ @if (filemanagervisible) + { + @((MarkupString)"  ") + + }

@@ -114,4 +120,12 @@ } StateHasChanged(); } + + public async Task CloseFileManager() + { + filemanagervisible = false; + message = ""; + StateHasChanged(); + } + } \ No newline at end of file diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index cf99306d..26a83c80 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using Oqtane.Security; using System.Linq; +using System.Drawing; namespace Oqtane.Controllers { @@ -25,7 +26,6 @@ namespace Oqtane.Controllers private readonly IUserPermissions UserPermissions; private readonly ITenantResolver Tenants; private readonly ILogManager logger; - private readonly string WhiteList = "jpg,jpeg,jpe,gif,bmp,png,mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg"; public FileController(IWebHostEnvironment environment, IFileRepository Files, IFolderRepository Folders, IUserPermissions UserPermissions, ITenantResolver Tenants, ILogManager logger) { @@ -139,16 +139,23 @@ namespace Oqtane.Controllers string folderpath = GetFolderPath(folder); CreateDirectory(folderpath); string filename = url.Substring(url.LastIndexOf("/") + 1); - try + // check for allowable file extensions + if (Constants.UploadableFiles.Contains(Path.GetExtension(filename).Replace(".", ""))) { - var client = new System.Net.WebClient(); - client.DownloadFile(url, folderpath + filename); - FileInfo fileinfo = new FileInfo(folderpath + filename); - file = Files.AddFile(new Models.File { Name = filename, FolderId = folder.FolderId, Extension = fileinfo.Extension.Replace(".",""), Size = (int)fileinfo.Length }); + try + { + var client = new System.Net.WebClient(); + client.DownloadFile(url, folderpath + filename); + Files.AddFile(CreateFile(filename, folder.FolderId, folderpath + filename)); + } + catch + { + logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url); + } } - catch + else { - logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url); + logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url); } } else @@ -193,8 +200,7 @@ namespace Oqtane.Controllers string upload = await MergeFile(folderpath, file.FileName); if (upload != "" && folderid != -1) { - FileInfo fileinfo = new FileInfo(folderpath + upload); - Files.AddFile(new Models.File { Name = upload, FolderId = folderid, Extension = fileinfo.Extension.Replace(".", ""), Size = (int)fileinfo.Length }); + Files.AddFile(CreateFile(upload, folderid, folderpath + upload)); } } else @@ -248,7 +254,7 @@ namespace Oqtane.Controllers } // check for allowable file extensions - if (!WhiteList.Contains(Path.GetExtension(filename).Replace(".", ""))) + if (!Constants.UploadableFiles.Contains(Path.GetExtension(filename).Replace(".", ""))) { System.IO.File.Delete(Path.Combine(folder, filename + ".tmp")); } @@ -357,5 +363,31 @@ namespace Oqtane.Controllers } } } + + private Models.File CreateFile(string filename, int folderid, string filepath) + { + Models.File file = new Models.File(); + file.Name = filename; + file.FolderId = folderid; + + FileInfo fileinfo = new FileInfo(filepath); + file.Extension = fileinfo.Extension.ToLower().Replace(".", ""); + file.Size = (int)fileinfo.Length; + file.ImageHeight = 0; + file.ImageWidth = 0; + + if (Constants.ImageFiles.Contains(file.Extension)) + { + FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read); + using (var image = Image.FromStream(stream)) + { + file.ImageHeight = image.Height; + file.ImageWidth = image.Width; + } + stream.Close(); + } + + return file; + } } } \ No newline at end of file diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index b9c304de..bdf1e2b9 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -44,6 +44,7 @@ + diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index d206c763..495930ba 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -176,7 +176,7 @@ namespace Oqtane.Repository FolderRepository.AddFolder(new Folder { SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = "Users\\", Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); if (site.Name == "Default Site") { - File file = FileRepository.AddFile(new File { FolderId = folder.FolderId, Name = "logo.png", Extension = "png", Size = 8192 }); + File file = FileRepository.AddFile(new File { FolderId = folder.FolderId, Name = "logo.png", Extension = "png", Size = 8192, ImageHeight = 80, ImageWidth = 250 }); site.LogoFileId = file.FileId; UpdateSite(site); } diff --git a/Oqtane.Server/Scripts/00.00.00.sql b/Oqtane.Server/Scripts/00.00.00.sql index 29637d45..e43b1e76 100644 --- a/Oqtane.Server/Scripts/00.00.00.sql +++ b/Oqtane.Server/Scripts/00.00.00.sql @@ -285,6 +285,8 @@ CREATE TABLE [dbo].[File]( [Name] [nvarchar](250) NOT NULL, [Extension] [nvarchar](50) NOT NULL, [Size] [int] NOT NULL, + [ImageHeight] [int] NOT NULL, + [ImageWidth] [int] NOT NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, diff --git a/Oqtane.Shared/Models/File.cs b/Oqtane.Shared/Models/File.cs index 44acbcbd..ae20fa11 100644 --- a/Oqtane.Shared/Models/File.cs +++ b/Oqtane.Shared/Models/File.cs @@ -9,6 +9,8 @@ namespace Oqtane.Models public string Name { get; set; } public string Extension { get; set; } public int Size { get; set; } + public int ImageHeight { get; set; } + public int ImageWidth { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs index 439f5131..bcb358df 100644 --- a/Oqtane.Shared/Models/Site.cs +++ b/Oqtane.Shared/Models/Site.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel.DataAnnotations.Schema; namespace Oqtane.Models { @@ -21,23 +20,5 @@ namespace Oqtane.Models public string DeletedBy { get; set; } public DateTime? DeletedOn { get; set; } public bool IsDeleted { get; set; } - - [NotMapped] - public string TenantRootPath - { - get - { - return "Tenants/" + TenantId.ToString() + "/"; - } - } - - [NotMapped] - public string SiteRootPath - { - get - { - return "Tenants/" + TenantId.ToString() + "/Sites/" + SiteId.ToString() + "/"; - } - } } } diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index e6c2fcca..fe4a12dd 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -31,5 +31,8 @@ public const string HostRole = "Host Users"; public const string AdminRole = "Administrators"; public const string RegisteredRole = "Registered Users"; + + public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png"; + public const string UploadableFiles = "jpg,jpeg,jpe,gif,bmp,png,mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg"; } } From a826c7d2715bd7189061a68af9e6abe316aacbf0 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sat, 22 Feb 2020 14:41:47 -0500 Subject: [PATCH 026/265] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 998e1b29..f3fa1aa3 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ Oqtane uses Blazor, a new web framework for .NET Core that lets you build intera **To get started with Oqtane:** - 1. Oqtane is currently compatible with **[.NET Core 3.1](https://dotnet.microsoft.com/download/dotnet-core/3.1)**. + 1. Install **[.NET Core 3.1 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.1)**. - 2. Install the latest edition of [Visual Studio 2019](https://visualstudio.com/vs/) (version 16.4 or higher) with the **ASP.NET and web development** workload. Installing the latest edition will also install the latest version of .NET Core 3.1. + 2. Install the latest edition of [Visual Studio 2019](https://visualstudio.com/vs/) (version 16.4 or higher) with the **ASP.NET and web development** workload. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**. 3. Download or Clone the Oqtane source code to your local system. Open the **Oqtane.sln** solution file. If you want to develop using **server-side** Blazor (which includes a full debugging experience in Visual Studio) you should choose to Build the solution using the default Debug configuration. If you want to develop using **client-side** Blazor (WebAssembly) you should first choose the "Wasm" configuration option in the Visual Studio toolbar and then Build. From d18b4d574acf059f7cc97d00303b0e1e5116baa0 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sun, 23 Feb 2020 10:45:32 -0500 Subject: [PATCH 027/265] Removed Repository methods which are not used and are not valid because they do not adhere to tenant scope boundaries --- .../Modules/Controls/FileManager.razor | 18 ++++++------- .../Modules/Controls/RichTextEditor.razor | 2 +- Oqtane.Server/Controllers/FileController.cs | 24 ++++++++++++++++-- .../Controllers/InstallationController.cs | 25 ++++++++++++------- Oqtane.Server/Repository/FolderRepository.cs | 5 ---- .../Interfaces/IFolderRepository.cs | 1 - .../Interfaces/IModuleRepository.cs | 2 +- .../Interfaces/IPageModuleRepository.cs | 1 - .../Repository/Interfaces/IPageRepository.cs | 1 - .../Interfaces/IProfileRepository.cs | 1 - .../Repository/Interfaces/IRoleRepository.cs | 1 - .../Interfaces/IUserRoleRepository.cs | 1 - Oqtane.Server/Repository/ModuleRepository.cs | 4 +-- .../Repository/PageModuleRepository.cs | 4 --- Oqtane.Server/Repository/PageRepository.cs | 5 ---- Oqtane.Server/Repository/ProfileRepository.cs | 5 ---- Oqtane.Server/Repository/RoleRepository.cs | 5 ---- .../Repository/UserRoleRepository.cs | 4 --- 18 files changed, 51 insertions(+), 58 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index 2177f43e..d0ebc303 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -8,7 +8,7 @@ {
-
+
@@ -54,22 +54,22 @@ { } - + @if (showfiles && GetFileId() != -1) { } -
+ @((MarkupString)@message) }
-
- @if (@image != "") - { + @if (@image != "") + { +
@((MarkupString)@image) - } -
+
+ }
} diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index affadeb7..60d4fe6d 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -121,7 +121,7 @@ StateHasChanged(); } - public async Task CloseFileManager() + public void CloseFileManager() { filemanagervisible = false; message = ""; diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 26a83c80..514c699a 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -145,6 +145,11 @@ namespace Oqtane.Controllers try { var client = new System.Net.WebClient(); + // remove file if it already exists + if (System.IO.File.Exists(folderpath + filename)) + { + System.IO.File.Delete(folderpath + filename); + } client.DownloadFile(url, folderpath + filename); Files.AddFile(CreateFile(filename, folder.FolderId, folderpath + filename)); } @@ -260,6 +265,11 @@ namespace Oqtane.Controllers } else { + // remove file if it already exists + if (System.IO.File.Exists(Path.Combine(folder, filename))) + { + System.IO.File.Delete(Path.Combine(folder, filename)); + } // rename file now that the entire process is completed System.IO.File.Move(Path.Combine(folder, filename + ".tmp"), Path.Combine(folder, filename)); logger.Log(LogLevel.Information, this, LogFunction.Create, "File Uploaded {File}", Path.Combine(folder, filename)); @@ -326,8 +336,18 @@ namespace Oqtane.Controllers Models.File file = Files.GetFile(id); if (file != null && UserPermissions.IsAuthorized(User, "View", file.Folder.Permissions)) { - byte[] filebytes = System.IO.File.ReadAllBytes(GetFolderPath(file.Folder) + file.Name); - return File(filebytes, "application/octet-stream", file.Name); + string filepath = GetFolderPath(file.Folder) + file.Name; + if (System.IO.File.Exists(filepath)) + { + byte[] filebytes = System.IO.File.ReadAllBytes(filepath); + return File(filebytes, "application/octet-stream", file.Name); + } + else + { + logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {File}", file); + HttpContext.Response.StatusCode = 404; + return null; + } } else { diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index fbfcaf51..684f7173 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -183,19 +183,26 @@ namespace Oqtane.Controllers string connectionString = Config.GetConnectionString("DefaultConnection"); connectionString = connectionString.Replace("|DataDirectory|", datadirectory); - SqlConnection connection = new SqlConnection(connectionString); - try + if (!string.IsNullOrEmpty(connectionString)) { - using (connection) + SqlConnection connection = new SqlConnection(connectionString); + try { - connection.Open(); + using (connection) + { + connection.Open(); + } + response.Success = true; + } + catch + { + // database does not exist + response.Message = "Database Does Not Exist"; } - response.Success = true; } - catch + else { - // database does not exist - response.Message = "Database Does Not Exist"; + response.Message = "Connection String Has Not Been Specified In Oqtane.Server\\appsettings.json"; } if (response.Success) @@ -243,7 +250,7 @@ namespace Oqtane.Controllers var result = dbUpgrade.PerformUpgrade(); if (!result.Successful) { - // TODO: log result.Error.Message; + // TODO: log result.Error.Message - problem is logger is not available here } } // iterate through Oqtane module assemblies and execute any database scripts diff --git a/Oqtane.Server/Repository/FolderRepository.cs b/Oqtane.Server/Repository/FolderRepository.cs index 3eb81471..75b44e29 100644 --- a/Oqtane.Server/Repository/FolderRepository.cs +++ b/Oqtane.Server/Repository/FolderRepository.cs @@ -16,11 +16,6 @@ namespace Oqtane.Repository this.Permissions = Permissions; } - public IEnumerable GetFolders() - { - return db.Folder.ToList(); - } - public IEnumerable GetFolders(int SiteId) { IEnumerable permissions = Permissions.GetPermissions(SiteId, "Folder").ToList(); diff --git a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs index 0d809ef1..86ac797f 100644 --- a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs @@ -5,7 +5,6 @@ namespace Oqtane.Repository { public interface IFolderRepository { - IEnumerable GetFolders(); IEnumerable GetFolders(int SiteId); Folder AddFolder(Folder Folder); Folder UpdateFolder(Folder Folder); diff --git a/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs index 262bcc90..5dd23858 100644 --- a/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs @@ -5,7 +5,7 @@ namespace Oqtane.Repository { public interface IModuleRepository { - IEnumerable GetModules(); + IEnumerable GetModules(int SiteId); Module AddModule(Module Module); Module UpdateModule(Module Module); Module GetModule(int ModuleId); diff --git a/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs b/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs index dc5f1d50..11b6d52f 100644 --- a/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs @@ -5,7 +5,6 @@ namespace Oqtane.Repository { public interface IPageModuleRepository { - IEnumerable GetPageModules(); IEnumerable GetPageModules(int SiteId); PageModule AddPageModule(PageModule PageModule); PageModule UpdatePageModule(PageModule PageModule); diff --git a/Oqtane.Server/Repository/Interfaces/IPageRepository.cs b/Oqtane.Server/Repository/Interfaces/IPageRepository.cs index e6d7cf8d..ee66b050 100644 --- a/Oqtane.Server/Repository/Interfaces/IPageRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPageRepository.cs @@ -5,7 +5,6 @@ namespace Oqtane.Repository { public interface IPageRepository { - IEnumerable GetPages(); IEnumerable GetPages(int SiteId); Page AddPage(Page Page); Page UpdatePage(Page Page); diff --git a/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs b/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs index 51f4c142..e5300b59 100644 --- a/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs @@ -5,7 +5,6 @@ namespace Oqtane.Repository { public interface IProfileRepository { - IEnumerable GetProfiles(); IEnumerable GetProfiles(int SiteId); Profile AddProfile(Profile Profile); Profile UpdateProfile(Profile Profile); diff --git a/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs b/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs index a83d7194..40541100 100644 --- a/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs @@ -5,7 +5,6 @@ namespace Oqtane.Repository { public interface IRoleRepository { - IEnumerable GetRoles(); IEnumerable GetRoles(int SiteId); IEnumerable GetRoles(int SiteId, bool IncludeGlobalRoles); Role AddRole(Role Role); diff --git a/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs b/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs index 3655a93a..14cb81a4 100644 --- a/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs @@ -5,7 +5,6 @@ namespace Oqtane.Repository { public interface IUserRoleRepository { - IEnumerable GetUserRoles(); IEnumerable GetUserRoles(int SiteId); IEnumerable GetUserRoles(int UserId, int SiteId); UserRole AddUserRole(UserRole UserRole); diff --git a/Oqtane.Server/Repository/ModuleRepository.cs b/Oqtane.Server/Repository/ModuleRepository.cs index 6d978ba4..f577e5f8 100644 --- a/Oqtane.Server/Repository/ModuleRepository.cs +++ b/Oqtane.Server/Repository/ModuleRepository.cs @@ -25,9 +25,9 @@ namespace Oqtane.Repository this.ServiceProvider = ServiceProvider; } - public IEnumerable GetModules() + public IEnumerable GetModules(int SiteId) { - return db.Module; + return db.Module.Where(item => item.SiteId == SiteId).ToList(); } public Models.Module AddModule(Models.Module Module) diff --git a/Oqtane.Server/Repository/PageModuleRepository.cs b/Oqtane.Server/Repository/PageModuleRepository.cs index 730d3c4c..9e35d6e8 100644 --- a/Oqtane.Server/Repository/PageModuleRepository.cs +++ b/Oqtane.Server/Repository/PageModuleRepository.cs @@ -16,10 +16,6 @@ namespace Oqtane.Repository this.Permissions = Permissions; } - public IEnumerable GetPageModules() - { - return db.PageModule; - } public IEnumerable GetPageModules(int SiteId) { IEnumerable pagemodules = db.PageModule diff --git a/Oqtane.Server/Repository/PageRepository.cs b/Oqtane.Server/Repository/PageRepository.cs index 6e4fc2ff..7e19c13d 100644 --- a/Oqtane.Server/Repository/PageRepository.cs +++ b/Oqtane.Server/Repository/PageRepository.cs @@ -18,11 +18,6 @@ namespace Oqtane.Repository this.PageModules = PageModules; } - public IEnumerable GetPages() - { - return db.Page.ToList(); - } - public IEnumerable GetPages(int SiteId) { IEnumerable permissions = Permissions.GetPermissions(SiteId, "Page").ToList(); diff --git a/Oqtane.Server/Repository/ProfileRepository.cs b/Oqtane.Server/Repository/ProfileRepository.cs index df895fe9..448dc913 100644 --- a/Oqtane.Server/Repository/ProfileRepository.cs +++ b/Oqtane.Server/Repository/ProfileRepository.cs @@ -14,11 +14,6 @@ namespace Oqtane.Repository db = context; } - public IEnumerable GetProfiles() - { - return db.Profile; - } - public IEnumerable GetProfiles(int SiteId) { return db.Profile.Where(item => item.SiteId == SiteId || item.SiteId == null); diff --git a/Oqtane.Server/Repository/RoleRepository.cs b/Oqtane.Server/Repository/RoleRepository.cs index 97958b07..adeb779f 100644 --- a/Oqtane.Server/Repository/RoleRepository.cs +++ b/Oqtane.Server/Repository/RoleRepository.cs @@ -14,11 +14,6 @@ namespace Oqtane.Repository db = context; } - public IEnumerable GetRoles() - { - return db.Role; - } - public IEnumerable GetRoles(int SiteId) { return db.Role.Where(item => item.SiteId == SiteId); diff --git a/Oqtane.Server/Repository/UserRoleRepository.cs b/Oqtane.Server/Repository/UserRoleRepository.cs index bf2c6613..3789c05f 100644 --- a/Oqtane.Server/Repository/UserRoleRepository.cs +++ b/Oqtane.Server/Repository/UserRoleRepository.cs @@ -14,10 +14,6 @@ namespace Oqtane.Repository db = context; } - public IEnumerable GetUserRoles() - { - return db.UserRole; - } public IEnumerable GetUserRoles(int SiteId) { return db.UserRole From a075826277b1972333a856de25e37d689495db58 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Wed, 26 Feb 2020 00:59:23 +0300 Subject: [PATCH 028/265] Use checklist --- README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index f3fa1aa3..cb560846 100644 --- a/README.md +++ b/README.md @@ -19,27 +19,27 @@ Oqtane uses Blazor, a new web framework for .NET Core that lets you build intera This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist. V1 (MVP) -- Multi-Tenant ( Shared Database & Isolated Database ) **done** -- Modular Architecture / Headless API **done** -- Dynamic Page Compositing Model / Site & Page Management **done** -- Authentication / User Management / Profile Management **done** -- Authorization / Roles Management / Granular Permissions **done** -- Dynamic Routing **done** -- Extensibility via Custom Modules **done** -- Extensibility via Custom Themes **done** -- Event Logging **done** -- Folder / File Management **done** -- Recycle Bin **done** -- Scheduled Jobs ( Background Processing ) **done** -- Notifications / Email Delivery **done** -- Auto-Upgrade Framework **done** +- [x] Multi-Tenant ( Shared Database & Isolated Database ) +- [x] Modular Architecture / Headless API +- [x] Dynamic Page Compositing Model / Site & Page Management +- [x] Authentication / User Management / Profile Management +- [x] Authorization / Roles Management / Granular Permissions +- [x] Dynamic Routing +- [x] Extensibility via Custom Modules +- [x] Extensibility via Custom Themes +- [x] Event Logging +- [x] Folder / File Management +- [x] Recycle Bin +- [x] Scheduled Jobs ( Background Processing ) +- [x] Notifications / Email Delivery +- [x] Auto-Upgrade Framework V.Next -- Optional Encryption of Settings Values ( ie. via an IsSecure flag ) -- Localization -- Migrate to Code-Behind Pattern ( *.razor.cs ) -- Generic Repository Pattern -- JwT token authentication +- [ ] Optional Encryption of Settings Values ( ie. via an IsSecure flag ) +- [ ] Localization +- [ ] Migrate to Code-Behind Pattern ( *.razor.cs ) +- [ ] Generic Repository Pattern +- [ ] JwT token authentication # Background Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalker/) and is inspired by the DotNetNuke web application framework. Initially created as a proof of concept, Oqtane is a native Blazor application written from the ground up using modern .NET Core technology. It is a modular application framework offering a fully dynamic page compositing model, multi-site support, designer friendly templates (skins), and extensibility via third party modules. From 451a1a353871a7c37a98fb6a0821c3257643e135 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Wed, 26 Feb 2020 01:02:23 +0300 Subject: [PATCH 029/265] Group screenshots into a single folder --- README.md | 14 +++++++------- screenshot0.png => screenshots/screenshot0.png | Bin screenshot1.png => screenshots/screenshot1.png | Bin screenshot2.png => screenshots/screenshot2.png | Bin screenshot3.png => screenshots/screenshot3.png | Bin screenshot4.png => screenshots/screenshot4.png | Bin screenshot5.png => screenshots/screenshot5.png | Bin screenshot6.png => screenshots/screenshot6.png | Bin 8 files changed, 7 insertions(+), 7 deletions(-) rename screenshot0.png => screenshots/screenshot0.png (100%) rename screenshot1.png => screenshots/screenshot1.png (100%) rename screenshot2.png => screenshots/screenshot2.png (100%) rename screenshot3.png => screenshots/screenshot3.png (100%) rename screenshot4.png => screenshots/screenshot4.png (100%) rename screenshot5.png => screenshots/screenshot5.png (100%) rename screenshot6.png => screenshots/screenshot6.png (100%) diff --git a/README.md b/README.md index cb560846..0a51890c 100644 --- a/README.md +++ b/README.md @@ -56,28 +56,28 @@ Install Wizard: Default view after installation: -![Home](https://github.com/oqtane/framework/blob/master/screenshot0.png?raw=true "Home") +![Home](https://github.com/oqtane/framework/blob/master/screenshots/screenshot0.png?raw=true "Home") A seamless login flow utilizing .NET Core Identity services: -![Login](https://github.com/oqtane/framework/blob/master/screenshot1.png?raw=true "Login") +![Login](https://github.com/oqtane/framework/blob/master/screenshots/screenshot1.png?raw=true "Login") Main view for authorized users, allowing full management of modules and content: -![Admin View](https://github.com/oqtane/framework/blob/master/screenshot2.png?raw=true "Admin View") +![Admin View](https://github.com/oqtane/framework/blob/master/screenshots/screenshot2.png?raw=true "Admin View") Content editing user experience using modal dialog: -![Edit Content](https://github.com/oqtane/framework/blob/master/screenshot3.png?raw=true "Edit Content") +![Edit Content](https://github.com/oqtane/framework/blob/master/screenshots/screenshot3.png?raw=true "Edit Content") Context menu for managing specific module on page: -![Manage Module](https://github.com/oqtane/framework/blob/master/screenshot4.png?raw=true "Manage Module") +![Manage Module](https://github.com/oqtane/framework/blob/master/screenshots/screenshot4.png?raw=true "Manage Module") Control panel for adding, editing, and deleting pages as well as adding new modules to a page: -![Manage Page](https://github.com/oqtane/framework/blob/master/screenshot5.png?raw=true "Manage Page") +![Manage Page](https://github.com/oqtane/framework/blob/master/screenshots/screenshot5.png?raw=true "Manage Page") Admin dashboard for accessing the variuous administrative features of the framework: -![Admin Dashboard](https://github.com/oqtane/framework/blob/master/screenshot6.png?raw=true "Admin Dashboard") +![Admin Dashboard](https://github.com/oqtane/framework/blob/master/screenshots/screenshot6.png?raw=true "Admin Dashboard") diff --git a/screenshot0.png b/screenshots/screenshot0.png similarity index 100% rename from screenshot0.png rename to screenshots/screenshot0.png diff --git a/screenshot1.png b/screenshots/screenshot1.png similarity index 100% rename from screenshot1.png rename to screenshots/screenshot1.png diff --git a/screenshot2.png b/screenshots/screenshot2.png similarity index 100% rename from screenshot2.png rename to screenshots/screenshot2.png diff --git a/screenshot3.png b/screenshots/screenshot3.png similarity index 100% rename from screenshot3.png rename to screenshots/screenshot3.png diff --git a/screenshot4.png b/screenshots/screenshot4.png similarity index 100% rename from screenshot4.png rename to screenshots/screenshot4.png diff --git a/screenshot5.png b/screenshots/screenshot5.png similarity index 100% rename from screenshot5.png rename to screenshots/screenshot5.png diff --git a/screenshot6.png b/screenshots/screenshot6.png similarity index 100% rename from screenshot6.png rename to screenshots/screenshot6.png From b4ddc45829d9660a3a92ed2bbec34637400a8dfb Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 25 Feb 2020 18:53:36 -0500 Subject: [PATCH 030/265] resolved a number of issues with site creation #224 --- .../{Sites/Edit.razor => Site/Index.razor} | 56 ++++-- Oqtane.Client/Modules/Admin/Sites/Add.razor | 16 +- Oqtane.Client/Modules/Admin/Sites/Index.razor | 19 +- Oqtane.Client/Modules/Admin/Tenants/Add.razor | 172 +++++++++++++----- .../Modules/Controls/FileManager.razor | 1 - .../Services/Interfaces/ISiteService.cs | 4 - .../Services/Interfaces/IUserService.cs | 2 +- Oqtane.Client/Services/SiteService.cs | 26 +-- Oqtane.Client/Shared/Installer.razor | 2 +- Oqtane.Server/Controllers/AliasController.cs | 2 + Oqtane.Server/Controllers/SiteController.cs | 9 +- Oqtane.Server/Controllers/UserController.cs | 2 +- .../Infrastructure/Interfaces/ILogManager.cs | 2 + Oqtane.Server/Infrastructure/LogManager.cs | 107 +++++++---- Oqtane.Server/Repository/SiteRepository.cs | 3 + Oqtane.Server/Scripts/00.00.00.sql | 2 +- Oqtane.Shared/Models/Log.cs | 2 +- 17 files changed, 264 insertions(+), 163 deletions(-) rename Oqtane.Client/Modules/Admin/{Sites/Edit.razor => Site/Index.razor} (87%) diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor similarity index 87% rename from Oqtane.Client/Modules/Admin/Sites/Edit.razor rename to Oqtane.Client/Modules/Admin/Site/Index.razor index 5a07fce7..4e73d383 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -1,13 +1,14 @@ -@namespace Oqtane.Modules.Admin.Sites +@namespace Oqtane.Modules.Admin.Site @inherits ModuleBase @inject NavigationManager NavigationManager @inject ISiteService SiteService +@inject ITenantService TenantService @inject IAliasService AliasService @inject IThemeService ThemeService @inject ISettingService SettingService @if (themes != null) -{ +{ + + + +
@@ -17,6 +18,14 @@
+ + + +
@@ -96,7 +105,7 @@
@@ -142,25 +151,29 @@
- +
Cancel + @if (UserSecurity.IsAuthorized(PageState.User, Constants.HostRole)) + { + + }

} @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } Dictionary themes; Dictionary panelayouts; Dictionary containers; List Themes; - Alias Alias; - int siteid; string name = ""; + List tenants; + string tenant = ""; List aliases; string urls = ""; int logofileid = -1; @@ -189,12 +202,12 @@ { Themes = await ThemeService.GetThemesAsync(); aliases = await AliasService.GetAliasesAsync(); - Alias = aliases.Where(item => item.AliasId == int.Parse(PageState.QueryString["id"])).FirstOrDefault(); - siteid = Alias.SiteId; - Site site = await SiteService.GetSiteAsync(siteid, Alias); + Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId); if (site != null) { name = site.Name; + tenants = await TenantService.GetTenantsAsync(); + tenant = tenants.Find(item => item.TenantId == site.TenantId).Name; foreach (Alias alias in aliases.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList()) { urls += alias.Name + "\n"; @@ -229,7 +242,7 @@ } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Site {SiteId} {Error}", siteid, ex.Message); + await logger.LogError(ex, "Error Loading Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message); AddModuleMessage(ex.Message, MessageType.Error); } } @@ -262,7 +275,7 @@ { if (name != "" && urls != "" && !string.IsNullOrEmpty(themetype) && (panelayouts.Count == 0 || !string.IsNullOrEmpty(layouttype)) && !string.IsNullOrEmpty(containertype)) { - Site site = await SiteService.GetSiteAsync(siteid, Alias); + Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId); if (site != null) { site.Name = name; @@ -277,7 +290,7 @@ site.DefaultContainerType = containertype; site.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted)); - site = await SiteService.UpdateSiteAsync(site, Alias); + site = await SiteService.UpdateSiteAsync(site); urls = urls.Replace("\n", ","); string[] names = urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); @@ -320,8 +333,23 @@ } catch (Exception ex) { - await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", siteid, ex.Message); + await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message); AddModuleMessage("Error Saving Site", MessageType.Error); } } + + private async Task DeleteSite() + { + try + { + await SiteService.DeleteSiteAsync(PageState.Site.SiteId); + await logger.LogInformation("Sited Deleted {SiteId}", PageState.Site.SiteId); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message); + AddModuleMessage("Error Deleting Site", MessageType.Error); + } + } } diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index b1e6820f..7afb961f 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -44,14 +44,6 @@ else +
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ @if (!RichTextEditorMode) + { + + } + else + { + + } + + Cancel +
+
+ +
+
+ +
+
@code { @@ -78,8 +78,30 @@ else get { return "Edit Html/Text"; } } - bool RichTextEditorMode = true; - bool _richTextEditorLoading = false; + public bool RichTextEditorMode + { + get => _richTextEditorMode; + set + { + _richTextEditorMode = value; + + if (_richTextEditorMode) + { + _visibleText = "d-none"; + _visibleRich = ""; + } + else + { + _visibleText = ""; + _visibleRich = "d-none"; + } + } + } + + string _visibleText; + string _visibleRich; + bool _richTextEditorMode; + RichTextEditor RichTextEditorHtml; string content; string createdby; @@ -95,17 +117,10 @@ else { if (content == null) { + RichTextEditorMode = true; await LoadText(); } } - else - { - if (_richTextEditorLoading) - { - await RichTextEditorHtml.LoadContent(content); - _richTextEditorLoading = false; - } - } } catch (Exception ex) { @@ -137,18 +152,15 @@ else private async Task RichTextEditor() { RichTextEditorMode = true; - _richTextEditorLoading = true; + await RichTextEditorHtml.LoadContent(content); StateHasChanged(); } private async Task RawHtmlEditor() { - if (RichTextEditorMode) - { - content = await this.RichTextEditorHtml.GetHTML(); - } + content = await this.RichTextEditorHtml.GetHTML(); RichTextEditorMode = false; - // await LoadText(); + StateHasChanged(); } private async Task SaveContent() From 14aeee1e4b5ea5e5b0e59f0d07613c4c14343635 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sat, 7 Mar 2020 00:28:39 +0100 Subject: [PATCH 051/265] Naming fix --- .../Modules/Controls/ModuleMessage.razor | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/ModuleMessage.razor b/Oqtane.Client/Modules/Controls/ModuleMessage.razor index 6c1a75f0..a957506d 100644 --- a/Oqtane.Client/Modules/Controls/ModuleMessage.razor +++ b/Oqtane.Client/Modules/Controls/ModuleMessage.razor @@ -1,9 +1,9 @@ @namespace Oqtane.Modules.Controls @inherits ModuleBase -@if (!string.IsNullOrEmpty(message)) +@if (!string.IsNullOrEmpty(_message)) { - +
} @@ -14,22 +14,22 @@ [Parameter] public MessageType Type { get; set; } - string message = ""; - string classname = "alert alert-danger"; + string _message = ""; + string _classname = "alert alert-danger"; protected override void OnParametersSet() { if (!string.IsNullOrEmpty(Message)) { - message = Message; - classname = GetMessageType(Type); + _message = Message; + _classname = GetMessageType(Type); } } public void SetModuleMessage(string message, MessageType type) { - message = message; - classname = GetMessageType(type); + _message = message; + _classname = GetMessageType(type); StateHasChanged(); } From 92444ccf759ceb766bf42619ad1a1d3b2fc23927 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sat, 7 Mar 2020 01:36:25 +0100 Subject: [PATCH 052/265] GetFiles and GetFolder by folder path --- Oqtane.Client/Services/FileService.cs | 23 +++++++++++-- Oqtane.Client/Services/FolderService.cs | 24 ++++++++++++-- .../Services/Interfaces/IFileService.cs | 1 + .../Services/Interfaces/IFolderService.cs | 2 ++ Oqtane.Server/Controllers/FileController.cs | 32 ++++++++++++++++++- Oqtane.Server/Controllers/FolderController.cs | 27 ++++++++++++++++ 6 files changed, 104 insertions(+), 5 deletions(-) diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index 3a3bc8a8..d377d62e 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -1,9 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.Logging; using Microsoft.JSInterop; using Oqtane.Models; using Oqtane.Shared; @@ -12,12 +14,14 @@ namespace Oqtane.Services { public class FileService : ServiceBase, IFileService { + + private readonly ILogger _logger; private readonly HttpClient _http; private readonly SiteState _siteState; private readonly NavigationManager _navigationManager; private readonly IJSRuntime _jsRuntime; - public FileService(HttpClient http, SiteState siteState, NavigationManager navigationManager, IJSRuntime jsRuntime) + public FileService(HttpClient http, SiteState siteState, NavigationManager navigationManager, IJSRuntime jsRuntime, ILogger Logger) { _http = http; _siteState = siteState; @@ -40,6 +44,21 @@ namespace Oqtane.Services return await _http.GetJsonAsync>(apiurl + "?folder=" + Folder); } + public async Task> GetFilesAsync(int siteId, string folderPath) + { + try + { + if (!folderPath.EndsWith("\\")) folderPath += "\\"; + var path = WebUtility.UrlEncode(folderPath); + return await _http.GetJsonAsync>($"{apiurl}/{siteId}/{path}"); + } + catch (Exception e) + { + _logger.LogDebug(e,"Folder not found: {path}"); + } + return null; + } + public async Task GetFileAsync(int FileId) { return await _http.GetJsonAsync(apiurl + "/" + FileId.ToString()); diff --git a/Oqtane.Client/Services/FolderService.cs b/Oqtane.Client/Services/FolderService.cs index 762ff7d8..630231fe 100644 --- a/Oqtane.Client/Services/FolderService.cs +++ b/Oqtane.Client/Services/FolderService.cs @@ -6,6 +6,9 @@ using Microsoft.AspNetCore.Components; using System.Collections.Generic; using Oqtane.Shared; using System; +using System.Diagnostics.CodeAnalysis; +using System.Net; +using Microsoft.Extensions.Logging; namespace Oqtane.Services { @@ -14,12 +17,14 @@ namespace Oqtane.Services private readonly HttpClient _http; private readonly SiteState _siteState; private readonly NavigationManager _navigationManager; - - public FolderService(HttpClient http, SiteState siteState, NavigationManager navigationManager) + private readonly ILogger _logger; + + public FolderService(HttpClient http, SiteState siteState, NavigationManager navigationManager, ILogger logger) { _http = http; _siteState = siteState; _navigationManager = navigationManager; + _logger = logger; } private string apiurl @@ -38,6 +43,21 @@ namespace Oqtane.Services { return await _http.GetJsonAsync(apiurl + "/" + FolderId.ToString()); } + + public async Task GetFolderAsync(int siteId, [NotNull]string folderPath) + { + try + { + if (!folderPath.EndsWith("\\")) folderPath += "\\"; + var path = WebUtility.UrlEncode(folderPath); + return await _http.GetJsonAsync($"{apiurl}/{siteId}/{path}"); + } + catch (Exception e) + { + _logger.LogDebug(e,"Folder not found: {path}"); + } + return null; + } public async Task AddFolderAsync(Folder Folder) { diff --git a/Oqtane.Client/Services/Interfaces/IFileService.cs b/Oqtane.Client/Services/Interfaces/IFileService.cs index 2a6234b6..c3107e6a 100644 --- a/Oqtane.Client/Services/Interfaces/IFileService.cs +++ b/Oqtane.Client/Services/Interfaces/IFileService.cs @@ -17,5 +17,6 @@ namespace Oqtane.Services Task UploadFilesAsync(string Folder, string[] Files, string FileUploadName); Task DownloadFileAsync(int FileId); + Task> GetFilesAsync(int siteId, string folderPath); } } diff --git a/Oqtane.Client/Services/Interfaces/IFolderService.cs b/Oqtane.Client/Services/Interfaces/IFolderService.cs index d91e253e..bbad463c 100644 --- a/Oqtane.Client/Services/Interfaces/IFolderService.cs +++ b/Oqtane.Client/Services/Interfaces/IFolderService.cs @@ -1,5 +1,6 @@ using Oqtane.Models; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace Oqtane.Services @@ -12,5 +13,6 @@ namespace Oqtane.Services Task UpdateFolderAsync(Folder Folder); Task UpdateFolderOrderAsync(int SiteId, int FolderId, int? ParentId); Task DeleteFolderAsync(int FolderId); + Task GetFolderAsync(int siteId, [NotNull]string folderPath); } } diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 418c0829..35da2162 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -14,6 +14,7 @@ using System.Threading.Tasks; using Oqtane.Security; using System.Linq; using System.Drawing; +using System.Net; namespace Oqtane.Controllers { @@ -67,6 +68,35 @@ namespace Oqtane.Controllers } return files; } + + // GET: api//siteId/folderPath + [HttpGet("{siteId}/{path}")] + public IEnumerable Get(int siteId, string path) + { + var folderPath = WebUtility.UrlDecode(path); + Folder folder = _folders.GetFolder(siteId, folderPath); + List files; + if (folder != null) + if (_userPermissions.IsAuthorized(User, "Browse", folder.Permissions)) + { + files = _files.GetFiles(folder.FolderId).ToList(); + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Folder {folder}", + folder); + HttpContext.Response.StatusCode = 401; + return null; + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Read, "Folder not found {path}", + path); + HttpContext.Response.StatusCode = 401; + return null; + } + return files; + } // GET api//5 [HttpGet("{id}")] diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index cbb48fb8..4b076e7f 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -5,6 +5,7 @@ using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using System.Linq; +using System.Net; using Oqtane.Infrastructure; using Oqtane.Security; @@ -56,6 +57,32 @@ namespace Oqtane.Controllers } } + [HttpGet("{siteId}/{path}")] + public Folder GetByPath(int siteId, string path) + { + var folderPath = WebUtility.UrlDecode(path); + Folder folder = _folders.GetFolder(siteId, folderPath); + if (folder != null) + if (_userPermissions.IsAuthorized(User, "Browse", folder.Permissions)) + { + return folder; + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Folder {Folder}", + folder); + HttpContext.Response.StatusCode = 401; + return null; + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Read, "Folder not found {path}", + path); + HttpContext.Response.StatusCode = 401; + return null; + } + } + // POST api/ [HttpPost] [Authorize(Roles = Constants.RegisteredRole)] From 563580881311569627a7193de1fecb2744586703 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sat, 7 Mar 2020 01:37:52 +0100 Subject: [PATCH 053/265] GetFiles and GetFolder by folder path --- Oqtane.Server/Controllers/FileController.cs | 26 ++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 35da2162..bf19bd64 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -40,14 +40,14 @@ namespace Oqtane.Controllers // GET: api/?folder=x [HttpGet] - public IEnumerable Get(string folder) + public IEnumerable Get(string path) { List files = new List(); int folderid; - if (int.TryParse(folder, out folderid)) + if (int.TryParse(path, out folderid)) { - Folder Folder = _folders.GetFolder(folderid); - if (Folder != null && _userPermissions.IsAuthorized(User, "Browse", Folder.Permissions)) + Folder folder = _folders.GetFolder(folderid); + if (folder != null && _userPermissions.IsAuthorized(User, "Browse", folder.Permissions)) { files = _files.GetFiles(folderid).ToList(); } @@ -56,10 +56,10 @@ namespace Oqtane.Controllers { if (User.IsInRole(Constants.HostRole)) { - folder = GetFolderPath(folder); - if (Directory.Exists(folder)) + path = GetFolderPath(path); + if (Directory.Exists(path)) { - foreach (string file in Directory.GetFiles(folder)) + foreach (string file in Directory.GetFiles(path)) { files.Add(new Models.File { Name = Path.GetFileName(file), Extension = Path.GetExtension(file).Replace(".","") }); } @@ -139,17 +139,17 @@ namespace Oqtane.Controllers [Authorize(Roles = Constants.RegisteredRole)] public void Delete(int id) { - Models.File File = _files.GetFile(id); - if (_userPermissions.IsAuthorized(User, "Folder", File.Folder.FolderId, "Edit")) + Models.File file = _files.GetFile(id); + if (_userPermissions.IsAuthorized(User, "Folder", file.Folder.FolderId, "Edit")) { _files.DeleteFile(id); - string filepath = Path.Combine(GetFolderPath(File.Folder) + File.Name); + string filepath = Path.Combine(GetFolderPath(file.Folder) + file.Name); if (System.IO.File.Exists(filepath)) { System.IO.File.Delete(filepath); } - _logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", File); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file); } else { @@ -440,4 +440,4 @@ namespace Oqtane.Controllers return file; } } -} \ No newline at end of file +} From 45dabe554ae3b56d153204c36c1fd5f8b11452fd Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sun, 8 Mar 2020 15:45:44 +0100 Subject: [PATCH 054/265] Control panel html facelift + naming fix --- .../Themes/Controls/ControlPanel.razor | 388 +++++++++--------- 1 file changed, 199 insertions(+), 189 deletions(-) diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index d731b99e..070afde9 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -11,7 +11,8 @@ @if (UserSecurity.IsAuthorized(PageState.User, "Edit", PageState.Page.Permissions)) { -
+
+
Control Panel @@ -20,132 +21,137 @@
- -
- - - - - - - - - - - - - - - - - -
- - - - @if (moduletype == "new") - { - @if (moduledefinitions != null) - { - - - } - } - else - { - - + + + } +
+
+
+ + + @if (_moduleType == "new") + { + @if (_moduleDefinitions != null) + { + + } -
- - - -
- - - + + @foreach (Page p in _pages) { - + } -
- - - + + @foreach (Module module in _modules) { - + } -
+ } +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ - @((MarkupString)@message) + @((MarkupString) _message)
@@ -184,6 +190,7 @@ } @code { + [Parameter] public string ButtonClass { get; set; } @@ -196,22 +203,22 @@ [Parameter] public string BodyClass { get; set; } - bool deleteconfirmation = false; - string moduletype = "new"; - List categories = new List(); - List ModuleDefinitions; - List moduledefinitions; - List pages = new List(); - string pageid = "-"; - string moduleid = "-"; - List modules = new List(); - Dictionary containers = new Dictionary(); - string moduledefinitionname = "-"; - string pane = ""; - string title = ""; - string containertype = ""; - string display = "display: none;"; - string message = ""; + bool _deleteConfirmation = false; + string _moduleType = "new"; + List _categories = new List(); + List _allModuleDefinitions; + List _moduleDefinitions; + List _pages = new List(); + string _pageId = "-"; + string _moduleId = "-"; + List _modules = new List(); + Dictionary _containers = new Dictionary(); + string _moduleDefinitionName = "-"; + string _pane = ""; + string _title = ""; + string _containerType = ""; + string _display = "display: none;"; + string _message = ""; protected override async Task OnParametersSetAsync() { @@ -234,68 +241,68 @@ if (UserSecurity.IsAuthorized(PageState.User, "Edit", PageState.Page.Permissions)) { - pages?.Clear(); + _pages?.Clear(); - ModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); - foreach (ModuleDefinition moduledefinition in ModuleDefinitions) + _allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); + foreach (ModuleDefinition moduledefinition in _allModuleDefinitions) { if (moduledefinition.Categories != "") { foreach (string category in moduledefinition.Categories.Split(',')) { - if (!categories.Contains(category)) + if (!_categories.Contains(category)) { - categories.Add(category); + _categories.Add(category); } } } } - moduledefinitions = ModuleDefinitions.Where(item => item.Categories == "").ToList(); + _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories == "").ToList(); foreach (Page p in PageState.Pages) { if (UserSecurity.IsAuthorized(PageState.User, "View", p.Permissions)) { - pages.Add(p); + _pages.Add(p); } } - var panes = PageState.Page.Panes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); - pane = panes.Count() == 1 ? panes.SingleOrDefault() : ""; + var panes = PageState.Page.Panes.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries); + _pane = panes.Count() == 1 ? panes.SingleOrDefault() : ""; List themes = await ThemeService.GetThemesAsync(); - containers = ThemeService.GetContainerTypes(themes); - containertype = PageState.Site.DefaultContainerType; + _containers = ThemeService.GetContainerTypes(themes); + _containerType = PageState.Site.DefaultContainerType; } } private void CategoryChanged(ChangeEventArgs e) { - string category = (string)e.Value; + string category = (string) e.Value; if (category == "-") { - moduledefinitions = ModuleDefinitions.Where(item => item.Categories == "").ToList(); + _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories == "").ToList(); } else { - moduledefinitions = ModuleDefinitions.Where(item => item.Categories.Contains(category)).ToList(); + _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(category)).ToList(); } - moduledefinitionname = "-"; + _moduleDefinitionName = "-"; StateHasChanged(); } private void PageChanged(ChangeEventArgs e) { - pageid = (string)e.Value; - modules?.Clear(); - if (pageid != "-") + _pageId = (string) e.Value; + _modules?.Clear(); + if (_pageId != "-") { - foreach (Module module in PageState.Modules.Where(item => item.PageId == int.Parse(pageid) && !item.IsDeleted)) + foreach (Module module in PageState.Modules.Where(item => item.PageId == int.Parse(_pageId) && !item.IsDeleted)) { if (UserSecurity.IsAuthorized(PageState.User, "View", module.Permissions)) { - modules.Add(module); + _modules.Add(module); } } } - moduleid = "-"; + _moduleId = "-"; StateHasChanged(); } @@ -303,65 +310,67 @@ { if (UserSecurity.IsAuthorized(PageState.User, "Edit", PageState.Page.Permissions)) { - if ((moduletype == "new" && moduledefinitionname != "-") || (moduletype != "new" && moduleid != "-")) + if ((_moduleType == "new" && _moduleDefinitionName != "-") || (_moduleType != "new" && _moduleId != "-")) { - if (moduletype == "new") + if (_moduleType == "new") { Module module = new Module(); module.SiteId = PageState.Site.SiteId; module.PageId = PageState.Page.PageId; - module.ModuleDefinitionName = moduledefinitionname; + module.ModuleDefinitionName = _moduleDefinitionName; module.Permissions = PageState.Page.Permissions; module = await ModuleService.AddModuleAsync(module); - moduleid = module.ModuleId.ToString(); + _moduleId = module.ModuleId.ToString(); } - PageModule pagemodule = new PageModule(); - pagemodule.PageId = (pageid == "-") ? PageState.Page.PageId : int.Parse(pageid); - pagemodule.ModuleId = int.Parse(moduleid); - pagemodule.Title = title; - if (pagemodule.Title == "") + var pageModule = new PageModule { - if (moduletype == "new") + PageId = (_pageId == "-") ? PageState.Page.PageId : int.Parse(_pageId), + ModuleId = int.Parse(_moduleId), + Title = _title + }; + if (pageModule.Title == "") + { + if (_moduleType == "new") { - pagemodule.Title = moduledefinitions.Where(item => item.ModuleDefinitionName == moduledefinitionname).FirstOrDefault().Name; + pageModule.Title = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == _moduleDefinitionName)?.Name; } else { - pagemodule.Title = modules.Where(item => item.ModuleId == int.Parse(moduleid)).FirstOrDefault().Title; + pageModule.Title = _modules.FirstOrDefault(item => item.ModuleId == int.Parse(_moduleId))?.Title; } } - pagemodule.Pane = pane; - pagemodule.Order = int.MaxValue; - pagemodule.ContainerType = containertype; + pageModule.Pane = _pane; + pageModule.Order = int.MaxValue; + pageModule.ContainerType = _containerType; - if (pagemodule.ContainerType == PageState.Site.DefaultContainerType) + if (pageModule.ContainerType == PageState.Site.DefaultContainerType) { - pagemodule.ContainerType = ""; + pageModule.ContainerType = ""; } - await PageModuleService.AddPageModuleAsync(pagemodule); - await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); + await PageModuleService.AddPageModuleAsync(pageModule); + await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane); - message = "
Module Added To Page
"; + _message = "
Module Added To Page
"; - moduledefinitionname = "-"; - pane = ""; - title = ""; - containertype = ""; - pageid = "-"; - moduleid = "-"; + _moduleDefinitionName = "-"; + _pane = ""; + _title = ""; + _containerType = ""; + _pageId = "-"; + _moduleId = "-"; NavigationManager.NavigateTo(NavigateUrl(Reload.Page)); } else { - message = "
You Must Select A Module
"; + _message = "
You Must Select A Module
"; } } else { - message = "
Not Authorized
"; + _message = "
Not Authorized
"; } } @@ -392,15 +401,15 @@ private void ShowControlPanel() { - message = ""; - display = "width: 25%;"; + _message = ""; + _display = "width: 25%; minwidth:300px"; StateHasChanged(); } private void HideControlPanel() { - message = ""; - display = "width: 0%;"; + _message = ""; + _display = "width: 0%;"; StateHasChanged(); } @@ -411,8 +420,8 @@ switch (location) { case "Admin": - // get admin dashboard moduleid - module = PageState.Modules.Where(item => item.ModuleDefinitionName == Constants.AdminDashboardModule).FirstOrDefault(); + // get admin dashboard moduleid + module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule); if (module != null) { NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, module.ModuleId, "Index", "")); @@ -421,8 +430,8 @@ case "Add": case "Edit": string url = ""; - // get page management moduleid - module = PageState.Modules.Where(item => item.ModuleDefinitionName == Constants.PageManagementModule).FirstOrDefault(); + // get page management moduleid + module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.PageManagementModule); if (module != null) { switch (location) @@ -445,7 +454,7 @@ private void ConfirmDelete() { - deleteconfirmation = !deleteconfirmation; + _deleteConfirmation = !_deleteConfirmation; StateHasChanged(); } @@ -474,4 +483,5 @@ await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, ex, "Page Deleted {Page} {Error}", page, ex.Message); } } + } From 979463b3651fca657fd5ca2244ac3770523535bd Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sun, 8 Mar 2020 22:55:42 +0100 Subject: [PATCH 055/265] FileController parameter fix --- Oqtane.Server/Controllers/FileController.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index bf19bd64..d1606eaa 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -40,14 +40,14 @@ namespace Oqtane.Controllers // GET: api/?folder=x [HttpGet] - public IEnumerable Get(string path) + public IEnumerable Get(string folder) { List files = new List(); int folderid; - if (int.TryParse(path, out folderid)) + if (int.TryParse(folder, out folderid)) { - Folder folder = _folders.GetFolder(folderid); - if (folder != null && _userPermissions.IsAuthorized(User, "Browse", folder.Permissions)) + Folder f = _folders.GetFolder(folderid); + if (f != null && _userPermissions.IsAuthorized(User, "Browse", f.Permissions)) { files = _files.GetFiles(folderid).ToList(); } @@ -56,10 +56,10 @@ namespace Oqtane.Controllers { if (User.IsInRole(Constants.HostRole)) { - path = GetFolderPath(path); - if (Directory.Exists(path)) + folder = GetFolderPath(folder); + if (Directory.Exists(folder)) { - foreach (string file in Directory.GetFiles(path)) + foreach (string file in Directory.GetFiles(folder)) { files.Add(new Models.File { Name = Path.GetFileName(file), Extension = Path.GetExtension(file).Replace(".","") }); } From 5d575c95cab910641fc1e7a10cf064d2c72ca185 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Mon, 9 Mar 2020 19:04:34 +0100 Subject: [PATCH 056/265] ILogger reference removed --- Oqtane.Client/Services/FileService.cs | 27 +++++-------- Oqtane.Client/Services/FolderService.cs | 50 ++++++++++--------------- 2 files changed, 30 insertions(+), 47 deletions(-) diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index d377d62e..265ccb67 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -5,7 +5,6 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; -using Microsoft.Extensions.Logging; using Microsoft.JSInterop; using Oqtane.Models; using Oqtane.Shared; @@ -14,14 +13,13 @@ namespace Oqtane.Services { public class FileService : ServiceBase, IFileService { - - private readonly ILogger _logger; private readonly HttpClient _http; private readonly SiteState _siteState; private readonly NavigationManager _navigationManager; private readonly IJSRuntime _jsRuntime; - public FileService(HttpClient http, SiteState siteState, NavigationManager navigationManager, IJSRuntime jsRuntime, ILogger Logger) + public FileService(HttpClient http, SiteState siteState, NavigationManager navigationManager, + IJSRuntime jsRuntime) { _http = http; _siteState = siteState; @@ -46,19 +44,11 @@ namespace Oqtane.Services public async Task> GetFilesAsync(int siteId, string folderPath) { - try - { - if (!folderPath.EndsWith("\\")) folderPath += "\\"; - var path = WebUtility.UrlEncode(folderPath); - return await _http.GetJsonAsync>($"{apiurl}/{siteId}/{path}"); - } - catch (Exception e) - { - _logger.LogDebug(e,"Folder not found: {path}"); - } - return null; + if (!folderPath.EndsWith("\\")) folderPath += "\\"; + var path = WebUtility.UrlEncode(folderPath); + return await _http.GetJsonAsync>($"{apiurl}/{siteId}/{path}"); } - + public async Task GetFileAsync(int FileId) { return await _http.GetJsonAsync(apiurl + "/" + FileId.ToString()); @@ -81,7 +71,8 @@ namespace Oqtane.Services public async Task UploadFileAsync(string Url, int FolderId) { - return await _http.GetJsonAsync(apiurl + "/upload?url=" + WebUtility.UrlEncode(Url) + "&folderid=" + FolderId.ToString()); + return await _http.GetJsonAsync(apiurl + "/upload?url=" + WebUtility.UrlEncode(Url) + "&folderid=" + + FolderId.ToString()); } public async Task UploadFilesAsync(int FolderId, string[] Files, string Id) @@ -117,8 +108,10 @@ namespace Oqtane.Services } } } + attempts += 1; } + if (!success) { result = result.Substring(0, result.Length - 1); diff --git a/Oqtane.Client/Services/FolderService.cs b/Oqtane.Client/Services/FolderService.cs index 630231fe..a457b021 100644 --- a/Oqtane.Client/Services/FolderService.cs +++ b/Oqtane.Client/Services/FolderService.cs @@ -8,7 +8,6 @@ using Oqtane.Shared; using System; using System.Diagnostics.CodeAnalysis; using System.Net; -using Microsoft.Extensions.Logging; namespace Oqtane.Services { @@ -17,66 +16,55 @@ namespace Oqtane.Services private readonly HttpClient _http; private readonly SiteState _siteState; private readonly NavigationManager _navigationManager; - private readonly ILogger _logger; - - public FolderService(HttpClient http, SiteState siteState, NavigationManager navigationManager, ILogger logger) + + public FolderService(HttpClient http, SiteState siteState, NavigationManager navigationManager) { _http = http; _siteState = siteState; _navigationManager = navigationManager; - _logger = logger; } - private string apiurl - { - get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "Folder"); } - } + private string ApiUrl => CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "Folder"); public async Task> GetFoldersAsync(int SiteId) { - List folders = await _http.GetJsonAsync>(apiurl + "?siteid=" + SiteId.ToString()); + List folders = await _http.GetJsonAsync>(ApiUrl + "?siteid=" + SiteId.ToString()); folders = GetFoldersHierarchy(folders); return folders; } public async Task GetFolderAsync(int FolderId) { - return await _http.GetJsonAsync(apiurl + "/" + FolderId.ToString()); + return await _http.GetJsonAsync(ApiUrl + "/" + FolderId.ToString()); } - - public async Task GetFolderAsync(int siteId, [NotNull]string folderPath) + + public async Task GetFolderAsync(int siteId, [NotNull] string folderPath) { - try - { - if (!folderPath.EndsWith("\\")) folderPath += "\\"; - var path = WebUtility.UrlEncode(folderPath); - return await _http.GetJsonAsync($"{apiurl}/{siteId}/{path}"); - } - catch (Exception e) - { - _logger.LogDebug(e,"Folder not found: {path}"); - } - return null; + if (!folderPath.EndsWith("\\")) folderPath += "\\"; + var path = WebUtility.UrlEncode(folderPath); + return await _http.GetJsonAsync($"{ApiUrl}/{siteId}/{path}"); } public async Task AddFolderAsync(Folder Folder) { - return await _http.PostJsonAsync(apiurl, Folder); + return await _http.PostJsonAsync(ApiUrl, Folder); } public async Task UpdateFolderAsync(Folder Folder) { - return await _http.PutJsonAsync(apiurl + "/" + Folder.FolderId.ToString(), Folder); + return await _http.PutJsonAsync(ApiUrl + "/" + Folder.FolderId.ToString(), Folder); } public async Task UpdateFolderOrderAsync(int SiteId, int FolderId, int? ParentId) { - await _http.PutJsonAsync(apiurl + "/?siteid=" + SiteId.ToString() + "&folderid=" + FolderId.ToString() + "&parentid=" + ((ParentId == null) ? "" : ParentId.ToString()), null); + await _http.PutJsonAsync( + ApiUrl + "/?siteid=" + SiteId.ToString() + "&folderid=" + FolderId.ToString() + "&parentid=" + + ((ParentId == null) ? "" : ParentId.ToString()), null); } public async Task DeleteFolderAsync(int FolderId) { - await _http.DeleteAsync(apiurl + "/" + FolderId.ToString()); + await _http.DeleteAsync(ApiUrl + "/" + FolderId.ToString()); } private static List GetFoldersHierarchy(List Folders) @@ -97,10 +85,11 @@ namespace Oqtane.Services level = folder.Level; children = Folders.Where(item => item.ParentId == folder.FolderId); } + foreach (Folder child in children) { child.Level = level + 1; - child.HasChildren = Folders.Where(item => item.ParentId == child.FolderId).Any(); + child.HasChildren = Folders.Any(item => item.ParentId == child.FolderId); hierarchy.Add(child); GetPath(folders, child); } @@ -109,13 +98,14 @@ namespace Oqtane.Services GetPath(Folders, null); // add any non-hierarchical items to the end of the list - foreach(Folder folder in Folders) + foreach (Folder folder in Folders) { if (hierarchy.Find(item => item.FolderId == folder.FolderId) == null) { hierarchy.Add(folder); } } + return hierarchy; } } From d7b3b444b525a7d42f9a363873288db47e04b622 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 9 Mar 2020 15:37:49 -0400 Subject: [PATCH 057/265] infrastructure for dealing with client cache invalidation in a multi-user environment --- Oqtane.Client/Modules/ModuleBase.cs | 2 +- Oqtane.Client/Services/AliasService.cs | 4 +- .../Services/Interfaces/IAliasService.cs | 3 +- Oqtane.Client/Shared/PageState.cs | 1 + Oqtane.Client/Shared/SiteRouter.razor | 52 ++++++++++++------- Oqtane.Server/Controllers/AliasController.cs | 14 +++-- Oqtane.Server/Controllers/PageController.cs | 5 +- .../Infrastructure/Interfaces/ISyncManager.cs | 13 +++++ Oqtane.Server/Infrastructure/SyncManager.cs | 44 ++++++++++++++++ Oqtane.Server/Startup.cs | 1 + Oqtane.Shared/Models/Alias.cs | 6 +++ Oqtane.Shared/Models/SyncEvent.cs | 12 +++++ 12 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs create mode 100644 Oqtane.Server/Infrastructure/SyncManager.cs create mode 100644 Oqtane.Shared/Models/SyncEvent.cs diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index ecfe4f8e..690dd4a1 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -125,7 +125,7 @@ namespace Oqtane.Modules // logging methods public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args) { - int pageId = PageState.Page.PageId; + int pageId = ModuleState.PageId; int moduleId = ModuleState.ModuleId; int? userId = null; if (PageState.User != null) diff --git a/Oqtane.Client/Services/AliasService.cs b/Oqtane.Client/Services/AliasService.cs index 03c0b845..87ff5e43 100644 --- a/Oqtane.Client/Services/AliasService.cs +++ b/Oqtane.Client/Services/AliasService.cs @@ -39,7 +39,7 @@ namespace Oqtane.Services return await _http.GetJsonAsync(apiurl + "/" + AliasId.ToString()); } - public async Task GetAliasAsync(string Url) + public async Task GetAliasAsync(string Url, DateTime LastSyncDate) { Uri uri = new Uri(Url); string name = uri.Authority; @@ -51,7 +51,7 @@ namespace Oqtane.Services { name = name.Substring(0, name.Length - 1); } - return await _http.GetJsonAsync(apiurl + "/name/" + WebUtility.UrlEncode(name)); + return await _http.GetJsonAsync(apiurl + "/name/" + WebUtility.UrlEncode(name) + "?lastsyncdate=" + LastSyncDate.ToString("yyyyMMddHHmmssfff")); } public async Task AddAliasAsync(Alias alias) diff --git a/Oqtane.Client/Services/Interfaces/IAliasService.cs b/Oqtane.Client/Services/Interfaces/IAliasService.cs index 9ecfa221..1c94d4bd 100644 --- a/Oqtane.Client/Services/Interfaces/IAliasService.cs +++ b/Oqtane.Client/Services/Interfaces/IAliasService.cs @@ -1,4 +1,5 @@ using Oqtane.Models; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -10,7 +11,7 @@ namespace Oqtane.Services Task GetAliasAsync(int AliasId); - Task GetAliasAsync(string Url); + Task GetAliasAsync(string Url, DateTime LastSyncDate); Task AddAliasAsync(Alias Alias); diff --git a/Oqtane.Client/Shared/PageState.cs b/Oqtane.Client/Shared/PageState.cs index c8003159..fc2eec10 100644 --- a/Oqtane.Client/Shared/PageState.cs +++ b/Oqtane.Client/Shared/PageState.cs @@ -17,5 +17,6 @@ namespace Oqtane.Shared public int ModuleId { get; set; } public string Action { get; set; } public bool EditMode { get; set; } + public DateTime LastSyncDate { get; set; } } } diff --git a/Oqtane.Client/Shared/SiteRouter.razor b/Oqtane.Client/Shared/SiteRouter.razor index d6b99d02..95518631 100644 --- a/Oqtane.Client/Shared/SiteRouter.razor +++ b/Oqtane.Client/Shared/SiteRouter.razor @@ -66,16 +66,17 @@ private async Task Refresh() { - Alias alias; + Alias alias = null; Site site; List pages; Page page; - User user; + User user = null; List modules; int moduleid = -1; string action = ""; bool editmode = false; Reload reload = Reload.None; + DateTime lastsyncdate = DateTime.Now; // get Url path and querystring ( and remove anchors ) string path = new Uri(_absoluteUri).PathAndQuery.Substring(1); @@ -99,26 +100,15 @@ if (PageState != null) { editmode = PageState.EditMode; + lastsyncdate = PageState.LastSyncDate; + user = PageState.User; } - if (PageState == null || reload == Reload.Application) - { - alias = null; - } - else - { - alias = PageState.Alias; - } + alias = await AliasService.GetAliasAsync(_absoluteUri, lastsyncdate); + SiteState.Alias = alias; // set state for services + lastsyncdate = alias.SyncDate; - // check if site has changed - Alias current = await AliasService.GetAliasAsync(_absoluteUri); - if (alias == null || current.Name != alias.Name) - { - alias = current; - SiteState.Alias = alias; // set state for services - reload = Reload.Site; - } - if (PageState == null || reload <= Reload.Site) + if (PageState == null || alias.SiteId != PageState.Alias.SiteId) { site = await SiteService.GetSiteAsync(alias.SiteId, alias); } @@ -128,11 +118,34 @@ } if (site != null) { + // process sync events + if (alias.SyncEvents.Any()) + { + if (PageState != null && alias.SyncEvents.Exists(item => item.EntityName == "Page" && item.EntityId == PageState.Page.PageId)) + { + reload = Reload.Page; + } + if (alias.SyncEvents.Exists(item => item.EntityName == "Site" && item.EntityId == alias.SiteId)) + { + reload = Reload.Site; + } + if (user != null && alias.SyncEvents.Exists(item => item.EntityName == "User" && item.EntityId == user.UserId)) + { + reload = Reload.Site; + } + } + if (PageState == null || reload >= Reload.Site) { #if WASM ModuleDefinitionService.LoadModuleDefinitionsAsync(site.SiteId); // download assemblies to browser when running client-side #endif + + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + if (authState.User.Identity.IsAuthenticated) + { + user = await UserService.GetUserAsync(authState.User.Identity.Name, site.SiteId); + } pages = await PageService.GetPagesAsync(site.SiteId); } else @@ -249,6 +262,7 @@ } pagestate.Modules = modules; pagestate.EditMode = editmode; + pagestate.LastSyncDate = lastsyncdate; OnStateChange?.Invoke(pagestate); } diff --git a/Oqtane.Server/Controllers/AliasController.cs b/Oqtane.Server/Controllers/AliasController.cs index 332a8e3e..e8242bee 100644 --- a/Oqtane.Server/Controllers/AliasController.cs +++ b/Oqtane.Server/Controllers/AliasController.cs @@ -8,6 +8,7 @@ using Oqtane.Infrastructure; using System.Linq; using System; using System.Net; +using System.Globalization; namespace Oqtane.Controllers { @@ -15,11 +16,13 @@ namespace Oqtane.Controllers public class AliasController : Controller { private readonly IAliasRepository _aliases; + private readonly ISyncManager _syncManager; private readonly ILogManager _logger; - public AliasController(IAliasRepository aliases, ILogManager logger) + public AliasController(IAliasRepository aliases, ISyncManager syncManager, ILogManager logger) { _aliases = aliases; + _syncManager = syncManager; _logger = logger; } @@ -39,9 +42,9 @@ namespace Oqtane.Controllers return _aliases.GetAlias(id); } - // GET api//name/localhost:12345 + // GET api//name/localhost:12345?lastsyncdate=yyyyMMddHHmmssfff [HttpGet("name/{name}")] - public Alias Get(string name) + public Alias Get(string name, string lastsyncdate) { name = WebUtility.UrlDecode(name); List aliases = _aliases.GetAliases().ToList(); @@ -57,6 +60,11 @@ namespace Oqtane.Controllers // use first alias if name does not exist alias = aliases.FirstOrDefault(); } + + // get sync events + alias.SyncDate = DateTime.Now; + alias.SyncEvents = _syncManager.GetSyncEvents(DateTime.ParseExact(lastsyncdate, "yyyyMMddHHmmssfff", CultureInfo.InvariantCulture)); + return alias; } diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index ad404f8f..09025b22 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -17,14 +17,16 @@ namespace Oqtane.Controllers private readonly IModuleRepository _modules; private readonly IPageModuleRepository _pageModules; private readonly IUserPermissions _userPermissions; + private readonly ISyncManager _syncManager; private readonly ILogManager _logger; - public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IUserPermissions userPermissions, ILogManager logger) + public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger) { _pages = pages; _modules = modules; _pageModules = pageModules; _userPermissions = userPermissions; + _syncManager = syncManager; _logger = logger; } @@ -88,6 +90,7 @@ namespace Oqtane.Controllers if (_userPermissions.IsAuthorized(User, "Edit", permissions)) { Page = _pages.AddPage(Page); + _syncManager.AddSyncEvent("Site", Page.SiteId); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", Page); } else diff --git a/Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs b/Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs new file mode 100644 index 00000000..f5bee6df --- /dev/null +++ b/Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs @@ -0,0 +1,13 @@ +using Oqtane.Models; +using Oqtane.Shared; +using System; +using System.Collections.Generic; + +namespace Oqtane.Infrastructure +{ + public interface ISyncManager + { + List GetSyncEvents(DateTime LastSyncDate); + void AddSyncEvent(string EntityName, int EntityId); + } +} diff --git a/Oqtane.Server/Infrastructure/SyncManager.cs b/Oqtane.Server/Infrastructure/SyncManager.cs new file mode 100644 index 00000000..5ae9f5cc --- /dev/null +++ b/Oqtane.Server/Infrastructure/SyncManager.cs @@ -0,0 +1,44 @@ +using Microsoft.Extensions.DependencyInjection; +using Oqtane.Models; +using Oqtane.Repository; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Oqtane.Infrastructure +{ + public class SyncManager : ISyncManager + { + private readonly IServiceScopeFactory ServiceScopeFactory; + private List SyncEvents { get; set; } + + public SyncManager(IServiceScopeFactory ServiceScopeFactory) + { + this.ServiceScopeFactory = ServiceScopeFactory; + SyncEvents = new List(); + } + + private int TenantId + { + get + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + return scope.ServiceProvider.GetRequiredService().GetTenant().TenantId; + } + } + } + + public List GetSyncEvents(DateTime LastSyncDate) + { + return SyncEvents.Where(item => item.TenantId == TenantId && item.ModifiedOn >= LastSyncDate).ToList(); + } + + public void AddSyncEvent(string EntityName, int EntityId) + { + SyncEvents.Add(new SyncEvent { TenantId = TenantId, EntityName = EntityName, EntityId = EntityId, ModifiedOn = DateTime.Now }); + // trim sync events + SyncEvents.RemoveAll(item => item.ModifiedOn < DateTime.Now.AddHours(-1)); + } + } +} diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 2ea27dd4..08a2a24b 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -162,6 +162,7 @@ namespace Oqtane.Server // register singleton scoped core services services.AddSingleton(Configuration); services.AddSingleton(); + services.AddSingleton(); // register transient scoped core services services.AddTransient(); diff --git a/Oqtane.Shared/Models/Alias.cs b/Oqtane.Shared/Models/Alias.cs index 2d49f21a..5e54e382 100644 --- a/Oqtane.Shared/Models/Alias.cs +++ b/Oqtane.Shared/Models/Alias.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; namespace Oqtane.Models @@ -15,6 +16,11 @@ namespace Oqtane.Models public string ModifiedBy { get; set; } public DateTime ModifiedOn { get; set; } + [NotMapped] + public DateTime SyncDate { get; set; } + [NotMapped] + public List SyncEvents { get; set; } + [NotMapped] public string Path { diff --git a/Oqtane.Shared/Models/SyncEvent.cs b/Oqtane.Shared/Models/SyncEvent.cs new file mode 100644 index 00000000..1d546d86 --- /dev/null +++ b/Oqtane.Shared/Models/SyncEvent.cs @@ -0,0 +1,12 @@ +using System; + +namespace Oqtane.Models +{ + public class SyncEvent + { + public int TenantId { get; set; } + public string EntityName { get; set; } + public int EntityId { get; set; } + public DateTime ModifiedOn { get; set; } + } +} From 155c4e12d9d01b1c6938b84a3d235207125fe18b Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 10 Mar 2020 10:37:42 -0400 Subject: [PATCH 058/265] completed client state invalidation in multi-user environment --- Oqtane.Client/Modules/Admin/Files/Edit.razor | 2 +- Oqtane.Client/Modules/Admin/Login/Index.razor | 2 +- .../Modules/Admin/ModuleDefinitions/Add.razor | 2 +- .../Admin/ModuleDefinitions/Edit.razor | 2 +- .../Admin/ModuleDefinitions/Index.razor | 4 +-- .../Modules/Admin/Modules/Import.razor | 3 +- .../Modules/Admin/Modules/Settings.razor | 2 +- Oqtane.Client/Modules/Admin/Pages/Add.razor | 2 +- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 2 +- Oqtane.Client/Modules/Admin/Pages/Index.razor | 2 +- .../Modules/Admin/RecycleBin/Index.razor | 4 +-- Oqtane.Client/Modules/Admin/Site/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Sites/Edit.razor | 2 +- Oqtane.Client/Modules/Admin/Sites/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Themes/Add.razor | 2 +- .../Modules/Admin/Themes/Index.razor | 4 +-- .../Modules/Admin/Upgrade/Index.razor | 4 +-- Oqtane.Client/Modules/HtmlText/Edit.razor | 2 +- .../Modules}/MessageType.cs | 2 +- Oqtane.Client/Modules/ModuleBase.cs | 21 ++--------- Oqtane.Client/Services/FileService.cs | 1 + Oqtane.Client/Themes/ContainerBase.cs | 22 ++---------- .../Themes/Controls/ControlPanel.razor | 35 +++++++++++-------- Oqtane.Client/Themes/Controls/Login.razor | 2 +- .../Themes/Controls/ModuleActions.razor | 2 +- Oqtane.Client/Themes/LayoutBase.cs | 1 + Oqtane.Client/Themes/ThemeBase.cs | 20 ++--------- Oqtane.Client/Themes/ThemeControlBase.cs | 20 ++--------- .../{Shared => UI}/ContainerBuilder.razor | 2 +- Oqtane.Client/{Shared => UI}/Installer.razor | 2 +- Oqtane.Client/{Shared => UI}/Interop.cs | 2 +- .../{Shared => UI}/ModuleInstance.razor | 2 +- Oqtane.Client/{Shared => UI}/PageState.cs | 2 +- Oqtane.Client/{Shared => UI}/Pane.razor | 2 +- Oqtane.Client/{Shared => UI}/PaneLayout.razor | 2 +- .../Enums => Oqtane.Client/UI}/Reload.cs | 2 +- .../{Shared => UI}/RichTextEditorInterop.cs | 2 +- Oqtane.Client/{Shared => UI}/SiteRouter.razor | 6 +--- .../{Shared => UI}/ThemeBuilder.razor | 2 +- Oqtane.Client/_Imports.razor | 3 +- Oqtane.Server/Controllers/PageController.cs | 13 ++++--- .../Controllers/PageModuleController.cs | 8 ++++- Oqtane.Server/Controllers/SiteController.cs | 5 ++- Oqtane.Server/Controllers/UserController.cs | 7 +++- Oqtane.Shared/Shared/Constants.cs | 4 +-- .../Shared/SiteState.cs | 2 +- Oqtane.Shared/Shared/Utilities.cs | 8 ++--- 47 files changed, 104 insertions(+), 143 deletions(-) rename {Oqtane.Shared/Enums => Oqtane.Client/Modules}/MessageType.cs (79%) rename Oqtane.Client/{Shared => UI}/ContainerBuilder.razor (97%) rename Oqtane.Client/{Shared => UI}/Installer.razor (99%) rename Oqtane.Client/{Shared => UI}/Interop.cs (99%) rename Oqtane.Client/{Shared => UI}/ModuleInstance.razor (98%) rename Oqtane.Client/{Shared => UI}/PageState.cs (96%) rename Oqtane.Client/{Shared => UI}/Pane.razor (99%) rename Oqtane.Client/{Shared => UI}/PaneLayout.razor (95%) rename {Oqtane.Shared/Enums => Oqtane.Client/UI}/Reload.cs (78%) rename Oqtane.Client/{Shared => UI}/RichTextEditorInterop.cs (98%) rename Oqtane.Client/{Shared => UI}/SiteRouter.razor (99%) rename Oqtane.Client/{Shared => UI}/ThemeBuilder.razor (96%) rename {Oqtane.Client => Oqtane.Shared}/Shared/SiteState.cs (55%) diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index 668b0ba7..360ac6b6 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -152,7 +152,7 @@ } await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); await logger.LogInformation("Folder Saved {Folder}", folder); - NavigationManager.NavigateTo(NavigateUrl(Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl()); } else { diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index f2ffa020..99d297b0 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -117,7 +117,7 @@ { await logger.LogInformation("Login Successful For Username {Username}", Username); authstateprovider.NotifyAuthenticationChanged(); - NavigationManager.NavigateTo(NavigateUrl(ReturnUrl, Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl(ReturnUrl)); } else { diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index a051164f..30cfd525 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -72,7 +72,7 @@ try { await ModuleDefinitionService.InstallModuleDefinitionsAsync(); - NavigationManager.NavigateTo(NavigateUrl(Reload.Application)); + NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index 68d535cd..f767c33b 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -71,7 +71,7 @@ moduledefinition.Permissions = permissiongrid.GetPermissions(); await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition); await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition); - NavigationManager.NavigateTo(NavigateUrl(Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor index 3375eb3d..7d40389f 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor @@ -78,7 +78,7 @@ else await PackageService.DownloadPackageAsync(moduledefinitionname, version, "Modules"); await ModuleDefinitionService.InstallModuleDefinitionsAsync(); await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", moduledefinitionname, version); - NavigationManager.NavigateTo(NavigateUrl(Reload.Application)); + NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { @@ -93,7 +93,7 @@ else { await ModuleDefinitionService.DeleteModuleDefinitionAsync(ModuleDefinition.ModuleDefinitionId, ModuleDefinition.SiteId); await logger.LogInformation("Module Deleted {ModuleDefinition}", ModuleDefinition); - NavigationManager.NavigateTo(NavigateUrl(Reload.Application)); + NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/Modules/Import.razor b/Oqtane.Client/Modules/Admin/Modules/Import.razor index 2db62162..56549632 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Import.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Import.razor @@ -33,7 +33,8 @@ try { await ModuleService.ImportModuleAsync(ModuleState.ModuleId, content); - NavigationManager.NavigateTo(NavigateUrl(Reload.Page)); + StateHasChanged(); + NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index 14d8f9eb..18659e7e 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -115,7 +115,7 @@ moduleType.GetMethod("UpdateSettings").Invoke(settings, null); // method must be public in settings component } - NavigationManager.NavigateTo(NavigateUrl(Reload.Page)); + NavigationManager.NavigateTo(NavigateUrl()); } } diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index b5de908e..46543bca 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -315,7 +315,7 @@ await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId); await logger.LogInformation("Page Added {Page}", page); - NavigationManager.NavigateTo(NavigateUrl(page.Path, Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl(page.Path)); } else { diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 2dc485d2..e0743b13 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -400,7 +400,7 @@ } await logger.LogInformation("Page Saved {Page}", page); - NavigationManager.NavigateTo(NavigateUrl(page.Path, Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl(page.Path)); } else { diff --git a/Oqtane.Client/Modules/Admin/Pages/Index.razor b/Oqtane.Client/Modules/Admin/Pages/Index.razor index bb6e030e..85f9f12f 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Index.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Index.razor @@ -31,7 +31,7 @@ Page.IsDeleted = true; await PageService.UpdatePageAsync(Page); await logger.LogInformation("Page Deleted {Page}", Page); - NavigationManager.NavigateTo(NavigateUrl("admin/pages", Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl("admin/pages")); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor index 9ba7b95b..72409259 100644 --- a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor +++ b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor @@ -117,7 +117,7 @@ await logger.LogInformation("Page Restored {Page}", Page); await Load(); StateHasChanged(); - NavigationManager.NavigateTo(NavigateUrl(Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { @@ -134,7 +134,7 @@ await logger.LogInformation("Page Permanently Deleted {Page}", Page); await Load(); StateHasChanged(); - NavigationManager.NavigateTo(NavigateUrl(Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index a2d6c815..f88898de 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -329,7 +329,7 @@ await logger.LogInformation("Site Saved {Site}", site); - NavigationManager.NavigateTo(NavigateUrl(Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl()); } } else diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor index 70f5cac0..edd6359f 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor @@ -243,7 +243,7 @@ await Log(Alias, LogLevel.Information, "Edit", null, "Site Saved {Site}", site); - NavigationManager.NavigateTo(NavigateUrl(Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl()); } } else diff --git a/Oqtane.Client/Modules/Admin/Sites/Index.razor b/Oqtane.Client/Modules/Admin/Sites/Index.razor index fa71d910..b00730fa 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Index.razor @@ -61,7 +61,7 @@ else { await AliasService.DeleteAliasAsync(alias.AliasId); } - NavigationManager.NavigateTo(NavigateUrl(Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl()); } else { diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor index 70e03747..08cd3d6e 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Add.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -61,7 +61,7 @@ private async Task InstallThemes() { await ThemeService.InstallThemesAsync(); - NavigationManager.NavigateTo(NavigateUrl(Reload.Application)); + NavigationManager.NavigateTo(NavigateUrl()); } private async Task DownloadTheme(string packageid, string version) diff --git a/Oqtane.Client/Modules/Admin/Themes/Index.razor b/Oqtane.Client/Modules/Admin/Themes/Index.razor index 501dec1b..09ec9823 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Index.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Index.razor @@ -66,13 +66,13 @@ else await PackageService.DownloadPackageAsync(themename, version, "Themes"); await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version); await ThemeService.InstallThemesAsync(); - NavigationManager.NavigateTo(NavigateUrl(Reload.Application)); + NavigationManager.NavigateTo(NavigateUrl()); } private async Task DeleteTheme(Theme Theme) { await ThemeService.DeleteThemeAsync(Theme.ThemeName); await logger.LogInformation("Theme Deleted {Theme}", Theme); - NavigationManager.NavigateTo(NavigateUrl(Reload.Application)); + NavigationManager.NavigateTo(NavigateUrl()); } } \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index d5e4c8a4..5093db2d 100644 --- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor +++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor @@ -47,13 +47,13 @@ private async Task Upgrade() { await InstallationService.Upgrade(); - NavigationManager.NavigateTo(NavigateUrl(Reload.Application)); + NavigationManager.NavigateTo(NavigateUrl()); } private async Task Download(string packageid, string version) { await PackageService.DownloadPackageAsync(packageid, version, "Framework"); await InstallationService.Upgrade(); - NavigationManager.NavigateTo(NavigateUrl(Reload.Application)); + NavigationManager.NavigateTo(NavigateUrl()); } } \ No newline at end of file diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index c5829d05..ef89a891 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -189,7 +189,7 @@ await htmltextservice.AddHtmlTextAsync(htmltext); } await logger.LogInformation("Html/Text Content Saved {HtmlText}", htmltext); - NavigationManager.NavigateTo(NavigateUrl(Reload.Page)); + NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { diff --git a/Oqtane.Shared/Enums/MessageType.cs b/Oqtane.Client/Modules/MessageType.cs similarity index 79% rename from Oqtane.Shared/Enums/MessageType.cs rename to Oqtane.Client/Modules/MessageType.cs index 4610fdf4..a78f95f9 100644 --- a/Oqtane.Shared/Enums/MessageType.cs +++ b/Oqtane.Client/Modules/MessageType.cs @@ -1,4 +1,4 @@ -namespace Oqtane.Shared +namespace Oqtane.Modules { public enum MessageType { diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 690dd4a1..ba0c637b 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -2,9 +2,9 @@ using Oqtane.Shared; using Oqtane.Models; using System.Threading.Tasks; -using System.Linq; using Oqtane.Services; using System; +using Oqtane.UI; namespace Oqtane.Modules { @@ -49,29 +49,14 @@ namespace Oqtane.Modules return NavigateUrl(PageState.Page.Path); } - public string NavigateUrl(Reload reload) - { - return NavigateUrl(PageState.Page.Path, reload); - } - public string NavigateUrl(string path) { - return NavigateUrl(path, "", Reload.None); - } - - public string NavigateUrl(string path, Reload reload) - { - return NavigateUrl(path, "", reload); + return NavigateUrl(path, ""); } public string NavigateUrl(string path, string parameters) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, Reload.None); - } - - public string NavigateUrl(string path, string parameters, Reload reload) - { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, reload); + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); } public string EditUrl(string action) diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index 265ccb67..89dfee81 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using Oqtane.Models; using Oqtane.Shared; +using Oqtane.UI; namespace Oqtane.Services { diff --git a/Oqtane.Client/Themes/ContainerBase.cs b/Oqtane.Client/Themes/ContainerBase.cs index 2e55f969..d55b718f 100644 --- a/Oqtane.Client/Themes/ContainerBase.cs +++ b/Oqtane.Client/Themes/ContainerBase.cs @@ -2,8 +2,7 @@ using Microsoft.JSInterop; using Oqtane.Shared; using Oqtane.Models; -using System.Threading.Tasks; -using System.Linq; +using Oqtane.UI; namespace Oqtane.Themes { @@ -30,29 +29,14 @@ namespace Oqtane.Themes return NavigateUrl(PageState.Page.Path); } - public string NavigateUrl(Reload reload) - { - return NavigateUrl(PageState.Page.Path, reload); - } - public string NavigateUrl(string path) { - return NavigateUrl(path, "", Reload.None); - } - - public string NavigateUrl(string path, Reload reload) - { - return NavigateUrl(path, "", reload); + return NavigateUrl(path, ""); } public string NavigateUrl(string path, string parameters) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, Reload.None); - } - - public string NavigateUrl(string path, string parameters, Reload reload) - { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, reload); + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); } public string EditUrl(string action, string parameters) diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index 070afde9..4f4c7a77 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -29,9 +29,12 @@ + +
+
-
- +
+
@@ -69,10 +72,10 @@
} -
+
-
+
+
-
+
@foreach (KeyValuePair container in _containers) @@ -150,6 +153,8 @@
+
+ @((MarkupString) _message)
@@ -325,7 +330,7 @@ var pageModule = new PageModule { - PageId = (_pageId == "-") ? PageState.Page.PageId : int.Parse(_pageId), + PageId = PageState.Page.PageId, ModuleId = int.Parse(_moduleId), Title = _title }; @@ -361,7 +366,7 @@ _pageId = "-"; _moduleId = "-"; - NavigationManager.NavigateTo(NavigateUrl(Reload.Page)); + NavigationManager.NavigateTo(NavigateUrl()); } else { @@ -386,7 +391,7 @@ { PageState.EditMode = true; } - NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0"), Reload.Page)); + NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0"))); } else { @@ -394,7 +399,7 @@ { await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId); PageState.EditMode = true; - NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0"), Reload.Page)); + NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0"))); } } } @@ -469,13 +474,13 @@ page.IsDeleted = true; await PageService.UpdatePageAsync(page); await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page); - NavigationManager.NavigateTo(NavigateUrl("", Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl("")); } else // personalized page { await PageService.DeletePageAsync(page.PageId); await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page); - NavigationManager.NavigateTo(NavigateUrl(Reload.Page)); + NavigationManager.NavigateTo(NavigateUrl()); } } catch (Exception ex) diff --git a/Oqtane.Client/Themes/Controls/Login.razor b/Oqtane.Client/Themes/Controls/Login.razor index c2dff055..7abcebd0 100644 --- a/Oqtane.Client/Themes/Controls/Login.razor +++ b/Oqtane.Client/Themes/Controls/Login.razor @@ -46,7 +46,7 @@ { // client-side Blazor authstateprovider.NotifyAuthenticationChanged(); - NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "logout", Reload.Site)); + NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "logout")); } } } diff --git a/Oqtane.Client/Themes/Controls/ModuleActions.razor b/Oqtane.Client/Themes/Controls/ModuleActions.razor index 5ca37b44..09355535 100644 --- a/Oqtane.Client/Themes/Controls/ModuleActions.razor +++ b/Oqtane.Client/Themes/Controls/ModuleActions.razor @@ -70,7 +70,7 @@ { PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); - string url = NavigateUrl(Reload.Page); + string url = NavigateUrl(); switch (action) { case "<<": diff --git a/Oqtane.Client/Themes/LayoutBase.cs b/Oqtane.Client/Themes/LayoutBase.cs index 27bac9a6..cd8d92e4 100644 --- a/Oqtane.Client/Themes/LayoutBase.cs +++ b/Oqtane.Client/Themes/LayoutBase.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Components; using Oqtane.Shared; +using Oqtane.UI; namespace Oqtane.Themes { diff --git a/Oqtane.Client/Themes/ThemeBase.cs b/Oqtane.Client/Themes/ThemeBase.cs index f0d05016..2ee51b39 100644 --- a/Oqtane.Client/Themes/ThemeBase.cs +++ b/Oqtane.Client/Themes/ThemeBase.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using Oqtane.Shared; +using Oqtane.UI; using System.Threading.Tasks; namespace Oqtane.Themes @@ -34,29 +35,14 @@ namespace Oqtane.Themes return NavigateUrl(PageState.Page.Path); } - public string NavigateUrl(Reload reload) - { - return NavigateUrl(PageState.Page.Path, reload); - } - public string NavigateUrl(string path) { - return NavigateUrl(path, "", Reload.None); - } - - public string NavigateUrl(string path, Reload reload) - { - return NavigateUrl(path, "", reload); + return NavigateUrl(path, ""); } public string NavigateUrl(string path, string parameters) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, Reload.None); - } - - public string NavigateUrl(string path, string parameters, Reload reload) - { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, reload); + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); } public string EditUrl(int moduleid, string action) diff --git a/Oqtane.Client/Themes/ThemeControlBase.cs b/Oqtane.Client/Themes/ThemeControlBase.cs index baed86cf..13cc1923 100644 --- a/Oqtane.Client/Themes/ThemeControlBase.cs +++ b/Oqtane.Client/Themes/ThemeControlBase.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Components; using Oqtane.Shared; +using Oqtane.UI; namespace Oqtane.Themes { @@ -13,29 +14,14 @@ namespace Oqtane.Themes return NavigateUrl(PageState.Page.Path); } - public string NavigateUrl(Reload reload) - { - return NavigateUrl(PageState.Page.Path, reload); - } - public string NavigateUrl(string path) { - return NavigateUrl(path, "", Reload.None); - } - - public string NavigateUrl(string path, Reload reload) - { - return NavigateUrl(path, "", reload); + return NavigateUrl(path, ""); } public string NavigateUrl(string path, string parameters) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, Reload.None); - } - - public string NavigateUrl(string path, string parameters, Reload reload) - { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters, reload); + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); } public string EditUrl(int moduleid, string action) diff --git a/Oqtane.Client/Shared/ContainerBuilder.razor b/Oqtane.Client/UI/ContainerBuilder.razor similarity index 97% rename from Oqtane.Client/Shared/ContainerBuilder.razor rename to Oqtane.Client/UI/ContainerBuilder.razor index 3e2e19ed..01dee688 100644 --- a/Oqtane.Client/Shared/ContainerBuilder.razor +++ b/Oqtane.Client/UI/ContainerBuilder.razor @@ -1,4 +1,4 @@ -@namespace Oqtane.Shared +@namespace Oqtane.UI @DynamicComponent diff --git a/Oqtane.Client/Shared/Installer.razor b/Oqtane.Client/UI/Installer.razor similarity index 99% rename from Oqtane.Client/Shared/Installer.razor rename to Oqtane.Client/UI/Installer.razor index 71b5a846..cbaf3952 100644 --- a/Oqtane.Client/Shared/Installer.razor +++ b/Oqtane.Client/UI/Installer.razor @@ -1,4 +1,4 @@ -@namespace Oqtane.Shared +@namespace Oqtane.UI @inject NavigationManager NavigationManager @inject IInstallationService InstallationService @inject ISiteService SiteService diff --git a/Oqtane.Client/Shared/Interop.cs b/Oqtane.Client/UI/Interop.cs similarity index 99% rename from Oqtane.Client/Shared/Interop.cs rename to Oqtane.Client/UI/Interop.cs index 82bf37a6..2ab8673e 100644 --- a/Oqtane.Client/Shared/Interop.cs +++ b/Oqtane.Client/UI/Interop.cs @@ -3,7 +3,7 @@ using Microsoft.JSInterop; using System; using System.Threading.Tasks; -namespace Oqtane.Shared +namespace Oqtane.UI { public class Interop { diff --git a/Oqtane.Client/Shared/ModuleInstance.razor b/Oqtane.Client/UI/ModuleInstance.razor similarity index 98% rename from Oqtane.Client/Shared/ModuleInstance.razor rename to Oqtane.Client/UI/ModuleInstance.razor index 8d563405..7487d2de 100644 --- a/Oqtane.Client/Shared/ModuleInstance.razor +++ b/Oqtane.Client/UI/ModuleInstance.razor @@ -1,4 +1,4 @@ -@namespace Oqtane.Shared +@namespace Oqtane.UI diff --git a/Oqtane.Client/Shared/PageState.cs b/Oqtane.Client/UI/PageState.cs similarity index 96% rename from Oqtane.Client/Shared/PageState.cs rename to Oqtane.Client/UI/PageState.cs index fc2eec10..e801ad73 100644 --- a/Oqtane.Client/Shared/PageState.cs +++ b/Oqtane.Client/UI/PageState.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Shared +namespace Oqtane.UI { public class PageState { diff --git a/Oqtane.Client/Shared/Pane.razor b/Oqtane.Client/UI/Pane.razor similarity index 99% rename from Oqtane.Client/Shared/Pane.razor rename to Oqtane.Client/UI/Pane.razor index e1340677..755d7417 100644 --- a/Oqtane.Client/Shared/Pane.razor +++ b/Oqtane.Client/UI/Pane.razor @@ -1,4 +1,4 @@ -@namespace Oqtane.Shared +@namespace Oqtane.UI @inject IUserService UserService @inject IModuleService ModuleService @inject IModuleDefinitionService ModuleDefinitionService diff --git a/Oqtane.Client/Shared/PaneLayout.razor b/Oqtane.Client/UI/PaneLayout.razor similarity index 95% rename from Oqtane.Client/Shared/PaneLayout.razor rename to Oqtane.Client/UI/PaneLayout.razor index 799b15a9..add8cc94 100644 --- a/Oqtane.Client/Shared/PaneLayout.razor +++ b/Oqtane.Client/UI/PaneLayout.razor @@ -1,4 +1,4 @@ -@namespace Oqtane.Shared +@namespace Oqtane.UI @DynamicComponent diff --git a/Oqtane.Shared/Enums/Reload.cs b/Oqtane.Client/UI/Reload.cs similarity index 78% rename from Oqtane.Shared/Enums/Reload.cs rename to Oqtane.Client/UI/Reload.cs index 18e8f448..6fb632c6 100644 --- a/Oqtane.Shared/Enums/Reload.cs +++ b/Oqtane.Client/UI/Reload.cs @@ -1,4 +1,4 @@ -namespace Oqtane.Shared +namespace Oqtane.UI { public enum Reload { diff --git a/Oqtane.Client/Shared/RichTextEditorInterop.cs b/Oqtane.Client/UI/RichTextEditorInterop.cs similarity index 98% rename from Oqtane.Client/Shared/RichTextEditorInterop.cs rename to Oqtane.Client/UI/RichTextEditorInterop.cs index 0552df47..50a46e48 100644 --- a/Oqtane.Client/Shared/RichTextEditorInterop.cs +++ b/Oqtane.Client/UI/RichTextEditorInterop.cs @@ -2,7 +2,7 @@ using Microsoft.JSInterop; using System.Threading.Tasks; -namespace Oqtane.Modules.Controls +namespace Oqtane.UI { public static class RichTextEditorInterop { diff --git a/Oqtane.Client/Shared/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor similarity index 99% rename from Oqtane.Client/Shared/SiteRouter.razor rename to Oqtane.Client/UI/SiteRouter.razor index 95518631..b1344e32 100644 --- a/Oqtane.Client/Shared/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -1,4 +1,4 @@ -@namespace Oqtane.Shared +@namespace Oqtane.UI @inject AuthenticationStateProvider AuthenticationStateProvider @inject SiteState SiteState @inject NavigationManager NavigationManager @@ -92,10 +92,6 @@ querystring = ParseQueryString(path); path = path.Substring(0, path.IndexOf("?")); } - if (querystring.ContainsKey("reload")) - { - reload = (Reload)int.Parse(querystring["reload"]); - } if (PageState != null) { diff --git a/Oqtane.Client/Shared/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor similarity index 96% rename from Oqtane.Client/Shared/ThemeBuilder.razor rename to Oqtane.Client/UI/ThemeBuilder.razor index 145e7449..dd12b058 100644 --- a/Oqtane.Client/Shared/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -1,4 +1,4 @@ -@namespace Oqtane.Shared +@namespace Oqtane.UI @inject IJSRuntime jsRuntime @DynamicComponent diff --git a/Oqtane.Client/_Imports.razor b/Oqtane.Client/_Imports.razor index cba766ad..22d11669 100644 --- a/Oqtane.Client/_Imports.razor +++ b/Oqtane.Client/_Imports.razor @@ -16,4 +16,5 @@ @using Oqtane.Services @using Oqtane.Shared @using Oqtane.Themes -@using Oqtane.Themes.Controls \ No newline at end of file +@using Oqtane.Themes.Controls +@using Oqtane.UI \ No newline at end of file diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 09025b22..8d3bff3a 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -130,6 +130,7 @@ namespace Oqtane.Controllers page.IsPersonalizable = false; page.UserId = int.Parse(userid); page = _pages.AddPage(page); + _syncManager.AddSyncEvent("Site", page.SiteId); // copy modules List pagemodules = _pageModules.GetPageModules(page.SiteId).ToList(); @@ -173,6 +174,7 @@ namespace Oqtane.Controllers if (ModelState.IsValid && _userPermissions.IsAuthorized(User, "Page", Page.PageId, "Edit")) { Page = _pages.UpdatePage(Page); + _syncManager.AddSyncEvent("Site", Page.SiteId); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", Page); } else @@ -202,6 +204,7 @@ namespace Oqtane.Controllers } order += 2; } + _syncManager.AddSyncEvent("Site", siteid); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Order Updated {SiteId} {PageId} {ParentId}", siteid, pageid, parentid); } else @@ -216,14 +219,16 @@ namespace Oqtane.Controllers [Authorize(Roles = Constants.RegisteredRole)] public void Delete(int id) { - if (_userPermissions.IsAuthorized(User, "Page", id, "Edit")) + Page page = _pages.GetPage(id); + if (_userPermissions.IsAuthorized(User, "Page", page.PageId, "Edit")) { - _pages.DeletePage(id); - _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Deleted {PageId}", id); + _pages.DeletePage(page.PageId); + _syncManager.AddSyncEvent("Site", page.SiteId); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Deleted {PageId}", page.PageId); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Page {PageId}", id); + _logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Page {PageId}", page.PageId); HttpContext.Response.StatusCode = 401; } } diff --git a/Oqtane.Server/Controllers/PageModuleController.cs b/Oqtane.Server/Controllers/PageModuleController.cs index c8e54a5d..1e2222cd 100644 --- a/Oqtane.Server/Controllers/PageModuleController.cs +++ b/Oqtane.Server/Controllers/PageModuleController.cs @@ -16,13 +16,15 @@ namespace Oqtane.Controllers private readonly IPageModuleRepository _pageModules; private readonly IModuleRepository _modules; private readonly IUserPermissions _userPermissions; + private readonly ISyncManager _syncManager; private readonly ILogManager _logger; - public PageModuleController(IPageModuleRepository pageModules, IModuleRepository modules, IUserPermissions userPermissions, ILogManager logger) + public PageModuleController(IPageModuleRepository pageModules, IModuleRepository modules, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger) { _pageModules = pageModules; _modules = modules; _userPermissions = userPermissions; + _syncManager = syncManager; _logger = logger; } @@ -68,6 +70,7 @@ namespace Oqtane.Controllers if (ModelState.IsValid && _userPermissions.IsAuthorized(User, "Page", PageModule.PageId, "Edit")) { PageModule = _pageModules.AddPageModule(PageModule); + _syncManager.AddSyncEvent("Page", PageModule.PageId); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Module Added {PageModule}", PageModule); } else @@ -87,6 +90,7 @@ namespace Oqtane.Controllers if (ModelState.IsValid && _userPermissions.IsAuthorized(User, "Module", PageModule.ModuleId, "Edit")) { PageModule = _pageModules.UpdatePageModule(PageModule); + _syncManager.AddSyncEvent("Page", PageModule.PageId); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Updated {PageModule}", PageModule); } else @@ -116,6 +120,7 @@ namespace Oqtane.Controllers } order += 2; } + _syncManager.AddSyncEvent("Page", pageid); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Order Updated {PageId} {Pane}", pageid, pane); } else @@ -134,6 +139,7 @@ namespace Oqtane.Controllers if (_userPermissions.IsAuthorized(User, "Page", pagemodule.PageId, "Edit")) { _pageModules.DeletePageModule(id); + _syncManager.AddSyncEvent("Page", pagemodule.PageId); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Module Deleted {PageModuleId}", id); } else diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index ca4992c2..361da726 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -17,13 +17,15 @@ namespace Oqtane.Controllers private readonly ISiteRepository _sites; private readonly ITenantResolver _tenants; private readonly IWebHostEnvironment _environment; + private readonly ISyncManager _syncManager; private readonly ILogManager _logger; - public SiteController(ISiteRepository sites, ITenantResolver tenants, IWebHostEnvironment environment, ILogManager logger) + public SiteController(ISiteRepository sites, ITenantResolver tenants, IWebHostEnvironment environment, ISyncManager syncManager, ILogManager logger) { _sites = sites; _tenants = tenants; _environment = environment; + _syncManager = syncManager; _logger = logger; } @@ -77,6 +79,7 @@ namespace Oqtane.Controllers if (ModelState.IsValid) { Site = _sites.UpdateSite(Site); + _syncManager.AddSyncEvent("Site", Site.SiteId); _logger.Log(Site.SiteId, LogLevel.Information, this, LogFunction.Update, "Site Updated {Site}", Site); } return Site; diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index e157062e..d75f2921 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -27,9 +27,10 @@ namespace Oqtane.Controllers private readonly ITenantResolver _tenants; private readonly INotificationRepository _notifications; private readonly IFolderRepository _folders; + private readonly ISyncManager _syncManager; private readonly ILogManager _logger; - public UserController(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager identityUserManager, SignInManager identitySignInManager, ITenantResolver tenants, INotificationRepository notifications, IFolderRepository folders, ILogManager logger) + public UserController(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager identityUserManager, SignInManager identitySignInManager, ITenantResolver tenants, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ILogManager logger) { _users = users; _roles = roles; @@ -39,6 +40,7 @@ namespace Oqtane.Controllers _tenants = tenants; _folders = folders; _notifications = notifications; + _syncManager = syncManager; _logger = logger; } @@ -185,6 +187,7 @@ namespace Oqtane.Controllers } } User = _users.UpdateUser(User); + _syncManager.AddSyncEvent("User", User.UserId); User.Password = ""; // remove sensitive information _logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", User); } @@ -240,6 +243,7 @@ namespace Oqtane.Controllers user.LastLoginOn = DateTime.Now; user.LastIPAddress = HttpContext.Connection.RemoteIpAddress.ToString(); _users.UpdateUser(user); + _syncManager.AddSyncEvent("User", User.UserId); _logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", User.Username); if (SetCookie) { @@ -268,6 +272,7 @@ namespace Oqtane.Controllers public async Task Logout([FromBody] User User) { await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme); + _syncManager.AddSyncEvent("User", User.UserId); _logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout {Username}", User.Username); } diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 12305ca7..09f5f334 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -5,8 +5,8 @@ public const string PackageId = "Oqtane.Framework"; public const string Version = "0.0.1"; - public const string PageComponent = "Oqtane.Shared.ThemeBuilder, Oqtane.Client"; - public const string ContainerComponent = "Oqtane.Shared.ContainerBuilder, Oqtane.Client"; + public const string PageComponent = "Oqtane.UI.ThemeBuilder, Oqtane.Client"; + public const string ContainerComponent = "Oqtane.UI.ContainerBuilder, Oqtane.Client"; public const string DefaultTheme = "Oqtane.Themes.BlazorTheme.Default, Oqtane.Client"; public const string DefaultLayout = ""; diff --git a/Oqtane.Client/Shared/SiteState.cs b/Oqtane.Shared/Shared/SiteState.cs similarity index 55% rename from Oqtane.Client/Shared/SiteState.cs rename to Oqtane.Shared/Shared/SiteState.cs index 89de8abe..159d29e6 100644 --- a/Oqtane.Client/Shared/SiteState.cs +++ b/Oqtane.Shared/Shared/SiteState.cs @@ -2,7 +2,7 @@ namespace Oqtane.Shared { - // this class is used for passing state between Blazor components and Services + // this class is used for passing state between components and services, or controllers and repositories public class SiteState { public Alias Alias { get; set; } diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 2bfff132..a4d10ce5 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -5,7 +5,7 @@ namespace Oqtane.Shared { public class Utilities { - public static string NavigateUrl(string alias, string path, string parameters, Reload reload) + public static string NavigateUrl(string alias, string path, string parameters) { string url = ""; if (alias != "") @@ -24,10 +24,6 @@ namespace Oqtane.Shared { url += "?" + parameters; } - if (reload != Reload.None) - { - url += ((string.IsNullOrEmpty(parameters)) ? "?" : "&") + "reload=" + ((int)reload).ToString(); - } if (!url.StartsWith("/")) { url = "/" + url; @@ -37,7 +33,7 @@ namespace Oqtane.Shared public static string EditUrl(string alias, string path, int moduleid, string action, string parameters) { - string url = NavigateUrl(alias, path, "", Reload.None); + string url = NavigateUrl(alias, path, ""); if (url == "/") url = ""; if (moduleid != -1) { From 805c25330ccb5624543bb1c881385bb1c6cf4ae0 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Tue, 10 Mar 2020 18:33:52 +0100 Subject: [PATCH 059/265] HTML text fix --- Oqtane.Client/Modules/HtmlText/Edit.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index c5829d05..0046e7c2 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -98,7 +98,7 @@ } } - string _visibleText; + string _visibleText = "d-none"; string _visibleRich; bool _richTextEditorMode; From 2436f7483051ec327bc6a96d7b6c7c0d1336c3cb Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 10 Mar 2020 14:44:50 -0400 Subject: [PATCH 060/265] fixes for client-side Blazor --- .../Services/ModuleDefinitionService.cs | 54 +++++++++++-------- Oqtane.Client/Themes/Controls/Login.razor | 2 +- Oqtane.Client/UI/SiteRouter.razor | 45 +++++++--------- Oqtane.Server/Controllers/AliasController.cs | 2 +- Oqtane.Server/Controllers/UserController.cs | 2 +- Oqtane.Server/Infrastructure/SyncManager.cs | 4 +- Oqtane.Server/Startup.cs | 1 + 7 files changed, 56 insertions(+), 54 deletions(-) diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index 9be0c222..a2952e4c 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Components; using System; using System.Reflection; using Oqtane.Shared; +using Oqtane.Providers; namespace Oqtane.Services { @@ -15,12 +16,14 @@ namespace Oqtane.Services private readonly HttpClient _http; private readonly SiteState _siteState; private readonly NavigationManager _navigationManager; + private readonly IServiceProvider _serviceProvider; - public ModuleDefinitionService(HttpClient http, SiteState siteState, NavigationManager navigationManager) + public ModuleDefinitionService(HttpClient http, SiteState siteState, NavigationManager navigationManager, IServiceProvider serviceProvider) { _http = http; _siteState = siteState; _navigationManager = navigationManager; + _serviceProvider = serviceProvider; } private string apiurl @@ -56,34 +59,39 @@ namespace Oqtane.Services public async Task LoadModuleDefinitionsAsync(int SiteId) { - // get list of modules from the server - List moduledefinitions = await GetModuleDefinitionsAsync(SiteId); - - // 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) + // download assemblies to browser when running client-side Blazor + var authstateprovider = (IdentityAuthenticationStateProvider)_serviceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); + if (authstateprovider != null) { - // if a module has dependencies, check if they are loaded - if (moduledefinition.Dependencies != "") + // get list of modules from the server + List moduledefinitions = await GetModuleDefinitionsAsync(SiteId); + + // 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) { - foreach (string dependency in moduledefinition.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + // if a module has dependencies, check if they are loaded + if (moduledefinition.Dependencies != "") { - string assemblyname = dependency.Replace(".dll", ""); - if (assemblies.Where(item => item.FullName.StartsWith(assemblyname + ",")).FirstOrDefault() == null) + foreach (string dependency in moduledefinition.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { - // download assembly from server and load - var bytes = await _http.GetByteArrayAsync(apiurl + "/load/" + assemblyname + ".dll"); - Assembly.Load(bytes); + 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); + // 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); + } } } } diff --git a/Oqtane.Client/Themes/Controls/Login.razor b/Oqtane.Client/Themes/Controls/Login.razor index 7abcebd0..522bd6e3 100644 --- a/Oqtane.Client/Themes/Controls/Login.razor +++ b/Oqtane.Client/Themes/Controls/Login.razor @@ -46,7 +46,7 @@ { // client-side Blazor authstateprovider.NotifyAuthenticationChanged(); - NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "logout")); + NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path)); } } } diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index b1344e32..a21fbe39 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -75,8 +75,9 @@ int moduleid = -1; string action = ""; bool editmode = false; + int userid = -1; Reload reload = Reload.None; - DateTime lastsyncdate = DateTime.Now; + DateTime lastsyncdate = DateTime.UtcNow; // get Url path and querystring ( and remove anchors ) string path = new Uri(_absoluteUri).PathAndQuery.Substring(1); @@ -97,7 +98,10 @@ { editmode = PageState.EditMode; lastsyncdate = PageState.LastSyncDate; - user = PageState.User; + if (PageState.User != null) + { + userid = PageState.User.UserId; + } } alias = await AliasService.GetAliasAsync(_absoluteUri, lastsyncdate); @@ -114,6 +118,17 @@ } if (site != null) { + // get user + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + if (authState.User.Identity.IsAuthenticated) + { + user = await UserService.GetUserAsync(authState.User.Identity.Name, site.SiteId); + if (user != null) + { + userid = user.UserId; + } + } + // process sync events if (alias.SyncEvents.Any()) { @@ -125,7 +140,7 @@ { reload = Reload.Site; } - if (user != null && alias.SyncEvents.Exists(item => item.EntityName == "User" && item.EntityId == user.UserId)) + if (alias.SyncEvents.Exists(item => item.EntityName == "User" && item.EntityId == userid)) { reload = Reload.Site; } @@ -133,15 +148,7 @@ if (PageState == null || reload >= Reload.Site) { -#if WASM - ModuleDefinitionService.LoadModuleDefinitionsAsync(site.SiteId); // download assemblies to browser when running client-side -#endif - - var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - if (authState.User.Identity.IsAuthenticated) - { - user = await UserService.GetUserAsync(authState.User.Identity.Name, site.SiteId); - } + await ModuleDefinitionService.LoadModuleDefinitionsAsync(site.SiteId); pages = await PageService.GetPagesAsync(site.SiteId); } else @@ -205,20 +212,6 @@ } } - user = null; - if (PageState == null || reload >= Reload.Page) - { - var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - if (authState.User.Identity.IsAuthenticated) - { - user = await UserService.GetUserAsync(authState.User.Identity.Name, site.SiteId); - } - } - else - { - user = PageState.User; - } - if (page != null) { if (PageState == null) diff --git a/Oqtane.Server/Controllers/AliasController.cs b/Oqtane.Server/Controllers/AliasController.cs index e8242bee..20eeba79 100644 --- a/Oqtane.Server/Controllers/AliasController.cs +++ b/Oqtane.Server/Controllers/AliasController.cs @@ -62,7 +62,7 @@ namespace Oqtane.Controllers } // get sync events - alias.SyncDate = DateTime.Now; + alias.SyncDate = DateTime.UtcNow; alias.SyncEvents = _syncManager.GetSyncEvents(DateTime.ParseExact(lastsyncdate, "yyyyMMddHHmmssfff", CultureInfo.InvariantCulture)); return alias; diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index d75f2921..1a8cdef2 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -243,7 +243,7 @@ namespace Oqtane.Controllers user.LastLoginOn = DateTime.Now; user.LastIPAddress = HttpContext.Connection.RemoteIpAddress.ToString(); _users.UpdateUser(user); - _syncManager.AddSyncEvent("User", User.UserId); + _syncManager.AddSyncEvent("User", user.UserId); _logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", User.Username); if (SetCookie) { diff --git a/Oqtane.Server/Infrastructure/SyncManager.cs b/Oqtane.Server/Infrastructure/SyncManager.cs index 5ae9f5cc..b9495e22 100644 --- a/Oqtane.Server/Infrastructure/SyncManager.cs +++ b/Oqtane.Server/Infrastructure/SyncManager.cs @@ -36,9 +36,9 @@ namespace Oqtane.Infrastructure public void AddSyncEvent(string EntityName, int EntityId) { - SyncEvents.Add(new SyncEvent { TenantId = TenantId, EntityName = EntityName, EntityId = EntityId, ModifiedOn = DateTime.Now }); + SyncEvents.Add(new SyncEvent { TenantId = TenantId, EntityName = EntityName, EntityId = EntityId, ModifiedOn = DateTime.UtcNow }); // trim sync events - SyncEvents.RemoveAll(item => item.ModifiedOn < DateTime.Now.AddHours(-1)); + SyncEvents.RemoveAll(item => item.ModifiedOn < DateTime.UtcNow.AddHours(-1)); } } } diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 08a2a24b..55e2391a 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -313,6 +313,7 @@ namespace Oqtane.Server // register singleton scoped core services services.AddSingleton(Configuration); services.AddSingleton(); + services.AddSingleton(); // register transient scoped core services services.AddTransient(); From edf3a816c16d6d5ee6511db7077f52ad4f616c14 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Wed, 11 Mar 2020 15:39:09 +0100 Subject: [PATCH 061/265] Changed root namespace in project to reflect namespaces in files --- Oqtane.Client/Oqtane.Client.csproj | 1 + Oqtane.Server/Oqtane.Server.csproj | 1 + Oqtane.Shared/Oqtane.Shared.csproj | 1 + 3 files changed, 3 insertions(+) diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index 07fdce6e..150eb596 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -21,6 +21,7 @@ https://github.com/oqtane Git Not for production use. + Oqtane diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index bdf1e2b9..2a243897 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -19,6 +19,7 @@ https://github.com/oqtane Git Not for production use. + Oqtane diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index 8cbb6d3d..79522771 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -14,6 +14,7 @@ https://github.com/oqtane Git Not for production use. + Oqtane From fe98084324b00c2ad113f4c5e0d19f3ef3ca28d9 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 11 Mar 2020 14:39:49 -0400 Subject: [PATCH 062/265] optimizations and fixes --- Oqtane.Client/Modules/Admin/Login/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Tenants/Add.razor | 2 +- .../Modules/Admin/UserProfile/Add.razor | 2 +- .../Modules/Admin/UserProfile/View.razor | 2 +- Oqtane.Client/Modules/Weather/Index.razor | 2 +- .../Services/Interfaces/IPageService.cs | 1 + .../Services/ModuleDefinitionService.cs | 6 +- Oqtane.Client/Services/PageService.cs | 13 +++ .../Themes/Controls/ControlPanel.razor | 2 +- Oqtane.Client/Themes/Controls/Login.razor | 2 +- Oqtane.Client/UI/Installer.razor | 20 ++--- Oqtane.Client/UI/SiteRouter.razor | 87 +++++++++++-------- Oqtane.Server/Controllers/FileController.cs | 4 +- .../Controllers/InstallationController.cs | 2 +- Oqtane.Server/Controllers/PageController.cs | 26 ++++++ Oqtane.Server/Controllers/UserController.cs | 8 +- .../Controllers/UserRoleController.cs | 10 ++- .../Infrastructure/Jobs/HostedServiceBase.cs | 12 +-- .../Infrastructure/Jobs/NotificationJob.cs | 2 +- .../Repository/Context/MasterDBContext.cs | 2 +- .../Repository/Interfaces/IPageRepository.cs | 1 + .../Repository/ModuleDefinitionRepository.cs | 28 +++--- Oqtane.Server/Repository/PageRepository.cs | 11 +++ 23 files changed, 159 insertions(+), 88 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index 99d297b0..ebf204b5 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -117,7 +117,7 @@ { await logger.LogInformation("Login Successful For Username {Username}", Username); authstateprovider.NotifyAuthenticationChanged(); - NavigationManager.NavigateTo(NavigateUrl(ReturnUrl)); + NavigationManager.NavigateTo(NavigateUrl(ReturnUrl, "reload")); } else { diff --git a/Oqtane.Client/Modules/Admin/Tenants/Add.razor b/Oqtane.Client/Modules/Admin/Tenants/Add.razor index 6acbe5be..90fb1e05 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Add.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Add.razor @@ -85,7 +85,7 @@ string name = ""; string type = "LocalDB"; string server = "(LocalDb)\\MSSQLLocalDB"; - string database = "Oqtane-" + DateTime.Now.ToString("yyyyMMddHHmm"); + string database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm"); string username = ""; string password = ""; string schema = ""; diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor index a5c3656f..f66ac87f 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor @@ -81,7 +81,7 @@ notification.Subject = subject; notification.Body = body; notification.ParentId = null; - notification.CreatedOn = DateTime.Now; + notification.CreatedOn = DateTime.UtcNow; notification.IsDelivered = false; notification.DeliveredOn = null; diff --git a/Oqtane.Client/Modules/Admin/UserProfile/View.razor b/Oqtane.Client/Modules/Admin/UserProfile/View.razor index 076e55e8..951ac04d 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/View.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/View.razor @@ -140,7 +140,7 @@ notification.Subject = subject; notification.Body = body; notification.ParentId = notificationid; - notification.CreatedOn = DateTime.Now; + notification.CreatedOn = DateTime.UtcNow; notification.IsDelivered = false; notification.DeliveredOn = null; diff --git a/Oqtane.Client/Modules/Weather/Index.razor b/Oqtane.Client/Modules/Weather/Index.razor index 88e346f8..081469b7 100644 --- a/Oqtane.Client/Modules/Weather/Index.razor +++ b/Oqtane.Client/Modules/Weather/Index.razor @@ -37,6 +37,6 @@ else protected override async Task OnInitializedAsync() { WeatherForecastService forecastservice = new WeatherForecastService(); - forecasts = await forecastservice.GetForecastAsync(DateTime.Now); + forecasts = await forecastservice.GetForecastAsync(DateTime.UtcNow); } } \ No newline at end of file diff --git a/Oqtane.Client/Services/Interfaces/IPageService.cs b/Oqtane.Client/Services/Interfaces/IPageService.cs index 35ae6a2f..3a2ab862 100644 --- a/Oqtane.Client/Services/Interfaces/IPageService.cs +++ b/Oqtane.Client/Services/Interfaces/IPageService.cs @@ -9,6 +9,7 @@ namespace Oqtane.Services Task> GetPagesAsync(int SiteId); Task GetPageAsync(int PageId); Task GetPageAsync(int PageId, int UserId); + Task GetPageAsync(string Path, int SiteId); Task AddPageAsync(Page Page); Task AddPageAsync(int PageId, int UserId); Task UpdatePageAsync(Page Page); diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index a2952e4c..f2ddb714 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -59,13 +59,13 @@ namespace Oqtane.Services public async Task LoadModuleDefinitionsAsync(int SiteId) { + // get list of modules from the server + List moduledefinitions = await GetModuleDefinitionsAsync(SiteId); + // download assemblies to browser when running client-side Blazor var authstateprovider = (IdentityAuthenticationStateProvider)_serviceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); if (authstateprovider != null) { - // get list of modules from the server - List moduledefinitions = await GetModuleDefinitionsAsync(SiteId); - // 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(); diff --git a/Oqtane.Client/Services/PageService.cs b/Oqtane.Client/Services/PageService.cs index 9ad8cbbc..4b757600 100644 --- a/Oqtane.Client/Services/PageService.cs +++ b/Oqtane.Client/Services/PageService.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Components; using System.Collections.Generic; using Oqtane.Shared; using System; +using System.Net; namespace Oqtane.Services { @@ -44,6 +45,18 @@ namespace Oqtane.Services return await _http.GetJsonAsync(apiurl + "/" + PageId.ToString() + "?userid=" + UserId.ToString()); } + public async Task GetPageAsync(string Path, int SiteId) + { + try + { + return await _http.GetJsonAsync(apiurl + "/path/" + SiteId.ToString() + "?path=" + WebUtility.UrlEncode(Path)); + } + catch + { + return null; + } + } + public async Task AddPageAsync(Page Page) { return await _http.PostJsonAsync(apiurl, Page); diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index 4f4c7a77..f30d2435 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -76,7 +76,7 @@
- + + } diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor index 11b8b1b9..a035b620 100644 --- a/Oqtane.Client/Modules/Admin/Users/Add.razor +++ b/Oqtane.Client/Modules/Admin/Users/Add.razor @@ -66,7 +66,7 @@ - + } diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index e8c40d2d..88eb815c 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -82,7 +82,7 @@ - + } diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor index aaffa1f7..9b781360 100644 --- a/Oqtane.Client/Modules/Controls/ActionDialog.razor +++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor @@ -117,10 +117,10 @@ authorized = true; break; case SecurityAccessLevel.View: - authorized = UserSecurity.IsAuthorized(PageState.User, "View", ModuleState.Permissions); + authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, ModuleState.Permissions); break; case SecurityAccessLevel.Edit: - authorized = UserSecurity.IsAuthorized(PageState.User, "Edit", ModuleState.Permissions); + authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions); break; case SecurityAccessLevel.Admin: authorized = UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole); diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index 2ed9c3e7..8b12c8cc 100644 --- a/Oqtane.Client/Modules/Controls/ActionLink.razor +++ b/Oqtane.Client/Modules/Controls/ActionLink.razor @@ -110,10 +110,10 @@ authorized = true; break; case SecurityAccessLevel.View: - authorized = UserSecurity.IsAuthorized(PageState.User, "View", ModuleState.Permissions); + authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, ModuleState.Permissions); break; case SecurityAccessLevel.Edit: - authorized = UserSecurity.IsAuthorized(PageState.User, "Edit", ModuleState.Permissions); + authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions); break; case SecurityAccessLevel.Admin: authorized = UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole); diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index 185d6bd5..49abc7d0 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -181,7 +181,7 @@ Folder folder = folders.Where(item => item.FolderId == folderid).FirstOrDefault(); if (folder != null) { - haseditpermission = UserSecurity.IsAuthorized(PageState.User, "Edit", folder.Permissions); + haseditpermission = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, folder.Permissions); files = await FileService.GetFilesAsync(folderid); } else diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index f30d2435..685a11ef 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -9,7 +9,7 @@ @inject IPageModuleService PageModuleService @inject ILogService logger -@if (UserSecurity.IsAuthorized(PageState.User, "Edit", PageState.Page.Permissions)) +@if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions)) {
@@ -96,7 +96,7 @@ @foreach (var moduledefinition in _moduleDefinitions) { - if (UserSecurity.IsAuthorized(PageState.User, "Utilize", moduledefinition.Permissions)) + if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Utilize, moduledefinition.Permissions)) { } @@ -162,7 +162,7 @@
} -@if (UserSecurity.IsAuthorized(PageState.User, "Edit", PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null)) +@if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null)) { @if (PageState.Page.EditMode) { @@ -187,7 +187,7 @@ } } -@if (UserSecurity.IsAuthorized(PageState.User, "Edit", PageState.Page.Permissions)) +@if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions)) {

- +
    @@ -49,23 +49,23 @@ else @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - List Jobs; + List _jobs; protected override async Task OnParametersSetAsync() { - Jobs = await JobService.GetJobsAsync(); + _jobs = await JobService.GetJobsAsync(); } - private string DisplayStatus(bool IsEnabled, bool IsExecuting) + private string DisplayStatus(bool isEnabled, bool isExecuting) { string status = ""; - if (!IsEnabled) + if (!isEnabled) { status = "Disabled"; } else { - if (IsExecuting) + if (isExecuting) { status = "Executing"; } @@ -79,59 +79,59 @@ else } - private string DisplayFrequency(int Interval, string Frequency) + private string DisplayFrequency(int interval, string frequency) { - string frequency = "Every " + Interval.ToString() + " "; - switch (Frequency) + string result = "Every " + interval.ToString() + " "; + switch (frequency) { case "m": - frequency += "Minute"; + result += "Minute"; break; case "H": - frequency += "Hour"; + result += "Hour"; break; case "d": - frequency += "Day"; + result += "Day"; break; case "M": - frequency += "Month"; + result += "Month"; break; } - if (Interval > 1) + if (interval > 1) { - frequency += "s"; + result += "s"; } - return frequency; + return result; } - private async Task DeleteJob(Job Job) + private async Task DeleteJob(Job job) { try { - await JobService.DeleteJobAsync(Job.JobId); - await logger.LogInformation("Job Deleted {Job}", Job); + await JobService.DeleteJobAsync(job.JobId); + await logger.LogInformation("Job Deleted {Job}", job); StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Deleting Job {Job} {Error}", Job, ex.Message); + await logger.LogError(ex, "Error Deleting Job {Job} {Error}", job, ex.Message); AddModuleMessage("Error Deleting Job", MessageType.Error); } } - private async Task StartJob(int JobId) + private async Task StartJob(int jobId) { - await JobService.StartJobAsync(JobId); + await JobService.StartJobAsync(jobId); } - private async Task StopJob(int JobId) + private async Task StopJob(int jobId) { - await JobService.StopJobAsync(JobId); + await JobService.StopJobAsync(jobId); } private async Task Refresh() { - Jobs = await JobService.GetJobsAsync(); + _jobs = await JobService.GetJobsAsync(); StateHasChanged(); } -} \ No newline at end of file +} diff --git a/Oqtane.Client/Modules/Admin/Jobs/Log.razor b/Oqtane.Client/Modules/Admin/Jobs/Log.razor index d2177e94..af0d5d5e 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Log.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Log.razor @@ -2,13 +2,13 @@ @inherits ModuleBase @inject IJobLogService JobLogService -@if (JobLogs == null) +@if (_jobLogs == null) {

Loading...

} else { - +
Name Status @@ -30,28 +30,28 @@ else @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - List JobLogs; + List _jobLogs; protected override async Task OnParametersSetAsync() { - JobLogs = await JobLogService.GetJobLogsAsync(); + _jobLogs = await JobLogService.GetJobLogsAsync(); if (PageState.QueryString.ContainsKey("id")) { - JobLogs = JobLogs.Where(item => item.JobId == Int32.Parse(PageState.QueryString["id"])).ToList(); + _jobLogs = _jobLogs.Where(item => item.JobId == Int32.Parse(PageState.QueryString["id"])).ToList(); } - JobLogs = JobLogs.OrderByDescending(item => item.JobLogId).ToList(); + _jobLogs = _jobLogs.OrderByDescending(item => item.JobLogId).ToList(); } - private string DisplayStatus(bool IsExecuting, bool? Succeeded) + private string DisplayStatus(bool isExecuting, bool? succeeded) { string status = ""; - if (IsExecuting) + if (isExecuting) { status = "Executing"; } else { - if (Succeeded.Value) + if (succeeded != null && succeeded.Value) { status = "Succeeded"; } @@ -62,4 +62,4 @@ else } return status; } -} \ No newline at end of file +} diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index ebf204b5..5310049d 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -1,13 +1,13 @@ @namespace Oqtane.Modules.Admin.Login @inherits ModuleBase @inject NavigationManager NavigationManager -@inject IJSRuntime jsRuntime +@inject IJSRuntime JsRuntime @inject IUserService UserService @inject IServiceProvider ServiceProvider -@if (Message != "") +@if (_message != "") { - + } @@ -20,16 +20,16 @@
- +
- +
  - +
@@ -43,38 +43,38 @@ @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Anonymous; } } - string ReturnUrl = ""; - public string Message = ""; - public MessageType Type = MessageType.Info; - public string Username = ""; - public string Password = ""; - public bool Remember = false; + string _returnUrl = ""; + string _message = ""; + MessageType _type = MessageType.Info; + string _username = ""; + string _password = ""; + bool _remember = false; protected override async Task OnInitializedAsync() { if (PageState.QueryString.ContainsKey("returnurl")) { - ReturnUrl = PageState.QueryString["returnurl"]; + _returnUrl = PageState.QueryString["returnurl"]; } if (PageState.QueryString.ContainsKey("name")) { - Username = PageState.QueryString["name"]; + _username = PageState.QueryString["name"]; } if (PageState.QueryString.ContainsKey("token")) { User user = new User(); user.SiteId = PageState.Site.SiteId; - user.Username = Username; + user.Username = _username; user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]); if (user != null) { - Message = "User Account Verified Successfully. You Can Now Login With Your Username And Password Below."; + _message = "User Account Verified Successfully. You Can Now Login With Your Username And Password Below."; } else { - Message = "User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions."; - Type = MessageType.Warning; + _message = "User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions."; + _type = MessageType.Warning; } } } @@ -87,21 +87,21 @@ // server-side Blazor User user = new User(); user.SiteId = PageState.Site.SiteId; - user.Username = Username; - user.Password = Password; + user.Username = _username; + user.Password = _password; user = await UserService.LoginUserAsync(user, false, false); if (user.IsAuthenticated) { - await logger.LogInformation("Login Successful For Username {Username}", Username); + await logger.LogInformation("Login Successful For Username {Username}", _username); // complete the login on the server so that the cookies are set correctly on SignalR - var interop = new Interop(jsRuntime); + var interop = new Interop(JsRuntime); string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken"); - var fields = new { __RequestVerificationToken = antiforgerytoken, username = Username, password = Password, remember = Remember, returnurl = ReturnUrl }; + var fields = new { __RequestVerificationToken = antiforgerytoken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl }; await interop.SubmitForm("/pages/login/", fields); } else { - await logger.LogInformation("Login Failed For Username {Username}", Username); + await logger.LogInformation("Login Failed For Username {Username}", _username); AddModuleMessage("Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Email Verification When They Initially Created.", MessageType.Error); } } @@ -110,18 +110,18 @@ // client-side Blazor User user = new User(); user.SiteId = PageState.Site.SiteId; - user.Username = Username; - user.Password = Password; - user = await UserService.LoginUserAsync(user, true, Remember); + user.Username = _username; + user.Password = _password; + user = await UserService.LoginUserAsync(user, true, _remember); if (user.IsAuthenticated) { - await logger.LogInformation("Login Successful For Username {Username}", Username); + await logger.LogInformation("Login Successful For Username {Username}", _username); authstateprovider.NotifyAuthenticationChanged(); - NavigationManager.NavigateTo(NavigateUrl(ReturnUrl, "reload")); + NavigationManager.NavigateTo(NavigateUrl(_returnUrl, "reload")); } else { - await logger.LogInformation("Login Failed For Username {Username}", Username); + await logger.LogInformation("Login Failed For Username {Username}", _username); AddModuleMessage("Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email.", MessageType.Error); } } @@ -129,28 +129,28 @@ private void Cancel() { - NavigationManager.NavigateTo(ReturnUrl); + NavigationManager.NavigateTo(_returnUrl); } private async Task Forgot() { - if (Username != "") + if (_username != "") { - User user = await UserService.GetUserAsync(Username, PageState.Site.SiteId); + User user = await UserService.GetUserAsync(_username, PageState.Site.SiteId); if (user != null) { await UserService.ForgotPasswordAsync(user); - Message = "Please Check The Email Address Associated To Your User Account For A Password Reset Notification"; + _message = "Please Check The Email Address Associated To Your User Account For A Password Reset Notification"; } else { - Message = "User Does Not Exist"; - Type = MessageType.Warning; + _message = "User Does Not Exist"; + _type = MessageType.Warning; } } else { - Message = "Please Enter The Username Related To Your Account And Then Select The Forgot Password Option Again"; + _message = "Please Enter The Username Related To Your Account And Then Select The Forgot Password Option Again"; } StateHasChanged(); } diff --git a/Oqtane.Client/Modules/Admin/Logs/Detail.razor b/Oqtane.Client/Modules/Admin/Logs/Detail.razor index 28aa59a8..0fd6333a 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Detail.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Detail.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Logs +@using System.Globalization @inherits ModuleBase @inject NavigationManager NavigationManager @inject ILogService LogService @@ -12,7 +13,7 @@ - + @@ -20,7 +21,7 @@ - + @@ -28,7 +29,7 @@ - + @@ -36,7 +37,7 @@ - + @@ -44,39 +45,39 @@ - + - @if (pagename != "") + @if (_pageName != "") { - + } - @if (moduletitle != "") + @if (_moduleTitle != "") { - + } - @if (username != "") + @if (_username != "") { - + } @@ -85,7 +86,7 @@ - + @@ -93,7 +94,7 @@ - + @@ -101,17 +102,17 @@ - - @if (!string.IsNullOrEmpty(exception)) + @if (!string.IsNullOrEmpty(_exception)) { - } @@ -120,7 +121,7 @@ - @@ -128,7 +129,7 @@ - + @@ -137,41 +138,41 @@ @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - int logid; - string logdate = ""; - string level = ""; - string feature = ""; - string function = ""; - string category = ""; - string pagename = ""; - string moduletitle = ""; - string username = ""; - string url = ""; - string template = ""; - string message = ""; - string exception = ""; - string properties = ""; - string server = ""; + int _logId; + string _logDate = ""; + string _level = ""; + string _feature = ""; + string _function = ""; + string _category = ""; + string _pageName = ""; + string _moduleTitle = ""; + string _username = ""; + string _url = ""; + string _template = ""; + string _message = ""; + string _exception = ""; + string _properties = ""; + string _server = ""; protected override async Task OnInitializedAsync() { try { - logid = Int32.Parse(PageState.QueryString["id"]); - Log log = await LogService.GetLogAsync(logid); + _logId = Int32.Parse(PageState.QueryString["id"]); + Log log = await LogService.GetLogAsync(_logId); if (log != null) { - logdate = log.LogDate.ToString(); - level = log.Level; - feature = log.Feature; - function = log.Function; - category = log.Category; + _logDate = log.LogDate.ToString(CultureInfo.CurrentCulture); + _level = log.Level; + _feature = log.Feature; + _function = log.Function; + _category = log.Category; if (log.PageId != null) { Page page = await PageService.GetPageAsync(log.PageId.Value); if (page != null) { - pagename = page.Name; + _pageName = page.Name; } } if (log.PageId != null && log.ModuleId != null) @@ -179,7 +180,7 @@ PageModule pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value); if (pagemodule != null) { - moduletitle = pagemodule.Title; + _moduleTitle = pagemodule.Title; } } if (log.UserId != null) @@ -187,20 +188,20 @@ User user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId); if (user != null) { - username = user.Username; + _username = user.Username; } } - url = log.Url; - template = log.MessageTemplate; - message = log.Message; - exception = log.Exception; - properties = log.Properties; - server = log.Server; + _url = log.Url; + _template = log.MessageTemplate; + _message = log.Message; + _exception = log.Exception; + _properties = log.Properties; + _server = log.Server; } } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Log {LogId} {Error}", logid, ex.Message); + await logger.LogError(ex, "Error Loading Log {LogId} {Error}", _logId, ex.Message); AddModuleMessage("Error Loading Log", MessageType.Error); } } diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor index 4b89cafb..77fd5d4c 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor @@ -2,7 +2,7 @@ @inherits ModuleBase @inject ILogService LogService -@if (Logs == null) +@if (_logs == null) {

Loading...

} @@ -45,9 +45,9 @@ else - @if (Logs.Any()) + @if (_logs.Any()) { - +
  Date @@ -73,10 +73,10 @@ else @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - string level = "-"; - string function = "-"; - string rows = "10"; - List Logs; + string _level = "-"; + string _function = "-"; + string _rows = "10"; + List _logs; protected override async Task OnInitializedAsync() { @@ -95,7 +95,7 @@ else { try { - level = (string)e.Value; + _level = (string)e.Value; await GetLogs(); StateHasChanged(); } @@ -110,7 +110,7 @@ else { try { - function = (string)e.Value; + _function = (string)e.Value; await GetLogs(); StateHasChanged(); } @@ -126,7 +126,7 @@ else { try { - rows = (string)e.Value; + _rows = (string)e.Value; await GetLogs(); StateHasChanged(); } @@ -139,7 +139,7 @@ else private async Task GetLogs() { - Logs = await LogService.GetLogsAsync(PageState.Site.SiteId, ((level == "-") ? "" : level), ((function == "-") ? "" : function), int.Parse(rows)); + _logs = await LogService.GetLogsAsync(PageState.Site.SiteId, ((_level == "-") ? "" : _level), ((_function == "-") ? "" : _function), int.Parse(_rows)); } private string GetClass(string function) diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index 30cfd525..bce9e1eb 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -8,7 +8,7 @@
- + @@ -16,12 +16,12 @@
-@if (packages != null) +@if (_packages != null) {

Available Modules

- +
Name Version @@ -44,19 +44,19 @@ @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - List packages; + List _packages; protected override async Task OnInitializedAsync() { try { List moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); - packages = await PackageService.GetPackagesAsync("module"); - foreach(Package package in packages.ToArray()) + _packages = await PackageService.GetPackagesAsync("module"); + foreach(Package package in _packages.ToArray()) { if (moduledefinitions.Exists(item => Utilities.GetTypeName(item.ModuleDefinitionName) == package.PackageId)) { - packages.Remove(package); + _packages.Remove(package); } } } diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index f767c33b..e4fb1680 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -6,18 +6,18 @@
- + - +
- + - +
@@ -25,40 +25,40 @@ Cancel

- + @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - int ModuleDefinitionId; - string name; - string permissions; - string createdby; - DateTime createdon; - string modifiedby; - DateTime modifiedon; + int _moduleDefinitionId; + string _name; + string _permissions; + string _createdby; + DateTime _createdon; + string _modifiedby; + DateTime _modifiedon; - PermissionGrid permissiongrid; + PermissionGrid _permissiongrid; protected override async Task OnInitializedAsync() { try { - ModuleDefinitionId = Int32.Parse(PageState.QueryString["id"]); - ModuleDefinition moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(ModuleDefinitionId, ModuleState.SiteId); - if (moduledefinition != null) + _moduleDefinitionId = Int32.Parse(PageState.QueryString["id"]); + ModuleDefinition moduleDefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId); + if (moduleDefinition != null) { - name = moduledefinition.Name; - permissions = moduledefinition.Permissions; - createdby = moduledefinition.CreatedBy; - createdon = moduledefinition.CreatedOn; - modifiedby = moduledefinition.ModifiedBy; - modifiedon = moduledefinition.ModifiedOn; + _name = moduleDefinition.Name; + _permissions = moduleDefinition.Permissions; + _createdby = moduleDefinition.CreatedBy; + _createdon = moduleDefinition.CreatedOn; + _modifiedby = moduleDefinition.ModifiedBy; + _modifiedon = moduleDefinition.ModifiedOn; } } catch (Exception ex) { - await logger.LogError(ex, "Error Loading ModuleDefinition {ModuleDefinitionId} {Error}", ModuleDefinitionId, ex.Message); + await logger.LogError(ex, "Error Loading ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message); AddModuleMessage("Error Loading Module", MessageType.Error); } } @@ -67,15 +67,15 @@ { try { - ModuleDefinition moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(ModuleDefinitionId, ModuleState.SiteId); - moduledefinition.Permissions = permissiongrid.GetPermissions(); + ModuleDefinition moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId); + moduledefinition.Permissions = _permissiongrid.GetPermissions(); await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition); await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition); NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { - await logger.LogError(ex, "Error Saving ModuleDefinition {ModuleDefinitionId} {Error}", ModuleDefinitionId, ex.Message); + await logger.LogError(ex, "Error Saving ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message); AddModuleMessage("Error Saving Module", MessageType.Error); } } diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor index 7d40389f..3efd4ff8 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor @@ -4,7 +4,7 @@ @inject IModuleDefinitionService ModuleDefinitionService @inject IPackageService PackageService -@if (moduledefinitions == null) +@if (_moduleDefinitions == null) {

Loading...

} @@ -12,7 +12,7 @@ else { - +
    @@ -43,15 +43,15 @@ else @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - List moduledefinitions; - List packages; + List _moduleDefinitions; + List _packages; protected override async Task OnInitializedAsync() { try { - moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); - packages = await PackageService.GetPackagesAsync("module"); + _moduleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); + _packages = await PackageService.GetPackagesAsync("module"); } catch (Exception ex) { @@ -63,7 +63,7 @@ else private bool UpgradeAvailable(string moduledefinitionname, string version) { bool upgradeavailable = false; - Package package = packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault(); + Package package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault(); if (package != null) { upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0); @@ -87,18 +87,18 @@ else } } - private async Task DeleteModule(ModuleDefinition ModuleDefinition) + private async Task DeleteModule(ModuleDefinition moduleDefinition) { try { - await ModuleDefinitionService.DeleteModuleDefinitionAsync(ModuleDefinition.ModuleDefinitionId, ModuleDefinition.SiteId); - await logger.LogInformation("Module Deleted {ModuleDefinition}", ModuleDefinition); + await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId); + await logger.LogInformation("Module Deleted {ModuleDefinition}", moduleDefinition); NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { - await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", ModuleDefinition, ex.Message); + await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message); AddModuleMessage("Error Deleting Module", MessageType.Error); } } -} \ No newline at end of file +} diff --git a/Oqtane.Client/Modules/Admin/Modules/Export.razor b/Oqtane.Client/Modules/Admin/Modules/Export.razor index c8368d40..5ac95386 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Export.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Export.razor @@ -7,10 +7,10 @@ - + - @@ -23,10 +23,10 @@ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } public override string Title { get { return "Export Module"; } } - string content = ""; + string _content = ""; private async Task ExportModule() { - content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId); + _content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId); } } diff --git a/Oqtane.Client/Modules/Admin/Modules/Import.razor b/Oqtane.Client/Modules/Admin/Modules/Import.razor index 56549632..5fe730fd 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Import.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Import.razor @@ -10,7 +10,7 @@ - @@ -23,16 +23,16 @@ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } public override string Title { get { return "Import Module"; } } - string content = ""; + string _content = ""; private async Task ImportModule() { - if (content != "") + if (_content != "") { try { - await ModuleService.ImportModuleAsync(ModuleState.ModuleId, content); + await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content); StateHasChanged(); NavigationManager.NavigateTo(NavigateUrl()); } diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index 18659e7e..84e41e4d 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -12,7 +12,7 @@ - + @@ -20,9 +20,9 @@ - - @foreach (KeyValuePair container in containers) + @foreach (KeyValuePair container in _containers) { } @@ -34,7 +34,7 @@ - + @@ -42,7 +42,7 @@ - @foreach (Page p in PageState.Pages) { @@ -63,26 +63,26 @@ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Edit; } } public override string Title { get { return "Module Settings"; } } - Dictionary containers = new Dictionary(); - string title; - string containertype; - string permissionnames = ""; - string permissions; - string pageid; + Dictionary _containers = new Dictionary(); + string _title; + string _containerType; + string _permissionNames = ""; + string _permissions; + string _pageId; - PermissionGrid permissiongrid; + PermissionGrid _permissiongrid; RenderFragment DynamicComponent { get; set; } - object settings; + object _settings; protected override async Task OnInitializedAsync() { - title = ModuleState.Title; - containers = ThemeService.GetContainerTypes(await ThemeService.GetThemesAsync()); - containertype = ModuleState.ContainerType; - permissions = ModuleState.Permissions; - permissionnames = ModuleState.ModuleDefinition.PermissionNames; - pageid = ModuleState.PageId.ToString(); + _title = ModuleState.Title; + _containers = ThemeService.GetContainerTypes(await ThemeService.GetThemesAsync()); + _containerType = ModuleState.ContainerType; + _permissions = ModuleState.Permissions; + _permissionNames = ModuleState.ModuleDefinition.PermissionNames; + _pageId = ModuleState.PageId.ToString(); DynamicComponent = builder => { @@ -90,7 +90,7 @@ if (moduleType != null) { builder.OpenComponent(0, moduleType); - builder.AddComponentReferenceCapture(1, inst => { settings = Convert.ChangeType(inst, moduleType); }); + builder.AddComponentReferenceCapture(1, inst => { _settings = Convert.ChangeType(inst, moduleType); }); builder.CloseComponent(); } }; @@ -99,20 +99,20 @@ private async Task SaveModule() { Module module = ModuleState; - module.Permissions = permissiongrid.GetPermissions(); + module.Permissions = _permissiongrid.GetPermissions(); await ModuleService.UpdateModuleAsync(module); PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); - pagemodule.PageId = int.Parse(pageid); - pagemodule.Title = title; - pagemodule.ContainerType = containertype; + pagemodule.PageId = int.Parse(_pageId); + pagemodule.Title = _title; + pagemodule.ContainerType = _containerType; await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); Type moduleType = Type.GetType(ModuleState.ModuleType); if (moduleType != null) { - moduleType.GetMethod("UpdateSettings").Invoke(settings, null); // method must be public in settings component + moduleType.GetMethod("UpdateSettings").Invoke(_settings, null); // method must be public in settings component } NavigationManager.NavigateTo(NavigateUrl()); diff --git a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor index a1eaff65..dfdbe432 100644 --- a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor @@ -6,7 +6,7 @@ @@ -33,7 +33,7 @@ @@ -41,7 +41,7 @@ @@ -49,7 +49,7 @@ @@ -57,7 +57,7 @@ @@ -65,7 +65,7 @@ @@ -27,25 +27,25 @@ else @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - List Profiles; + List _profiles; protected override async Task OnInitializedAsync() { - Profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId); + _profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId); } - private async Task DeleteProfile(int ProfileId) + private async Task DeleteProfile(int profileId) { try { - await ProfileService.DeleteProfileAsync(ProfileId); - await logger.LogInformation("Profile Deleted {ProfileId}", ProfileId); + await ProfileService.DeleteProfileAsync(profileId); + await logger.LogInformation("Profile Deleted {ProfileId}", profileId); AddModuleMessage("Profile Deleted", MessageType.Success); } catch (Exception ex) { - await logger.LogError(ex, "Error Deleting Profile {ProfileId} {Error}", ProfileId, ex.Message); + await logger.LogError(ex, "Error Deleting Profile {ProfileId} {Error}", profileId, ex.Message); AddModuleMessage("Error Deleting Profile", MessageType.Error); } } -} \ No newline at end of file +} diff --git a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor index 72409259..012a5495 100644 --- a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor +++ b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor @@ -23,14 +23,14 @@
- @if (pages == null) + @if (_pages == null) {

No Deleted Pages

} else { - +
@@ -49,14 +49,14 @@ }
- @if (modules == null) + @if (_modules == null) {

No Deleted Modules

} else { - +
@@ -83,8 +83,8 @@ @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - List pages; - List modules; + List _pages; + List _modules; protected override async Task OnInitializedAsync() { @@ -101,84 +101,84 @@ private async Task Load() { - pages = await PageService.GetPagesAsync(PageState.Site.SiteId); - pages = pages.Where(item => item.IsDeleted).ToList(); + _pages = await PageService.GetPagesAsync(PageState.Site.SiteId); + _pages = _pages.Where(item => item.IsDeleted).ToList(); - modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId); - modules = modules.Where(item => item.IsDeleted).ToList(); + _modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId); + _modules = _modules.Where(item => item.IsDeleted).ToList(); } - private async Task RestorePage(Page Page) + private async Task RestorePage(Page page) { try { - Page.IsDeleted = false; - await PageService.UpdatePageAsync(Page); - await logger.LogInformation("Page Restored {Page}", Page); + page.IsDeleted = false; + await PageService.UpdatePageAsync(page); + await logger.LogInformation("Page Restored {Page}", page); await Load(); StateHasChanged(); NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { - await logger.LogError(ex, "Error Restoring Deleted Page {Page} {Error}", Page, ex.Message); + await logger.LogError(ex, "Error Restoring Deleted Page {Page} {Error}", page, ex.Message); AddModuleMessage("Error Restoring Deleted Page", MessageType.Error); } } - private async Task DeletePage(Page Page) + private async Task DeletePage(Page page) { try { - await PageService.DeletePageAsync(Page.PageId); - await logger.LogInformation("Page Permanently Deleted {Page}", Page); + await PageService.DeletePageAsync(page.PageId); + await logger.LogInformation("Page Permanently Deleted {Page}", page); await Load(); StateHasChanged(); NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { - await logger.LogError(ex, "Error Permanently Deleting Page {Page} {Error}", Page, ex.Message); + await logger.LogError(ex, "Error Permanently Deleting Page {Page} {Error}", page, ex.Message); AddModuleMessage(ex.Message, MessageType.Error); } } - private async Task RestoreModule(Module Module) + private async Task RestoreModule(Module module) { try { - PageModule pagemodule = await PageModuleService.GetPageModuleAsync(Module.PageModuleId); + PageModule pagemodule = await PageModuleService.GetPageModuleAsync(module.PageModuleId); pagemodule.IsDeleted = false; await PageModuleService.UpdatePageModuleAsync(pagemodule); - await logger.LogInformation("Module Restored {Module}", Module); + await logger.LogInformation("Module Restored {Module}", module); await Load(); StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Restoring Deleted Module {Module} {Error}", Module, ex.Message); + await logger.LogError(ex, "Error Restoring Deleted Module {Module} {Error}", module, ex.Message); AddModuleMessage("Error Restoring Deleted Module", MessageType.Error); } } - private async Task DeleteModule(Module Module) + private async Task DeleteModule(Module module) { try { - await PageModuleService.DeletePageModuleAsync(Module.PageModuleId); + await PageModuleService.DeletePageModuleAsync(module.PageModuleId); // check if there are any remaining module instances in the site - modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId); - if (!modules.Exists(item => item.ModuleId == Module.ModuleId)) + _modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId); + if (!_modules.Exists(item => item.ModuleId == module.ModuleId)) { - await ModuleService.DeleteModuleAsync(Module.ModuleId); + await ModuleService.DeleteModuleAsync(module.ModuleId); } - await logger.LogInformation("Module Permanently Deleted {Module}", Module); + await logger.LogInformation("Module Permanently Deleted {Module}", module); await Load(); StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Permanently Deleting Module {Module} {Error}", Module, ex.Message); + await logger.LogError(ex, "Error Permanently Deleting Module {Module} {Error}", module, ex.Message); AddModuleMessage("Error Permanently Deleting Module", MessageType.Error); } } diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index 40b59b89..46fdca01 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -3,31 +3,31 @@ @inject NavigationManager NavigationManager @inject IUserService UserService -@if (Message != "") +@if (_message != "") { - + }
- +
- +
- - + +
- - + +
- +
@@ -36,38 +36,40 @@ @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Anonymous; } } - string Message = "Please Note That Registration Requires A Valid Email Address In Order To Verify Your Identity"; - string Username = ""; - string Password = ""; - string Confirm = ""; - string Email = ""; - string DisplayName = ""; + string _message = "Please Note That Registration Requires A Valid Email Address In Order To Verify Your Identity"; + string _username = ""; + string _password = ""; + string _confirm = ""; + string _email = ""; + string _displayName = ""; private async Task Register() { try { - Message = ""; - if (Username != "" && Password != "" && Confirm != "" && Email != "") + _message = ""; + if (_username != "" && _password != "" && _confirm != "" && _email != "") { - if (Password == Confirm) + if (_password == _confirm) { - User user = new User(); - user.SiteId = PageState.Site.SiteId; - user.Username = Username; - user.DisplayName = (DisplayName == "" ? Username : DisplayName); - user.Email = Email; - user.Password = Password; + User user = new User + { + SiteId = PageState.Site.SiteId, + Username = _username, + DisplayName = (_displayName == "" ? _username : _displayName), + Email = _email, + Password = _password + }; user = await UserService.AddUserAsync(user); if (user != null) { - await logger.LogInformation("User Created {Username} {Email}", Username, Email); + await logger.LogInformation("User Created {Username} {Email}", _username, _email); AddModuleMessage("User Account Created. Please Check Your Email For Verification Instructions.", MessageType.Info); } else { - await logger.LogError("Error Adding User {Username} {Email}", Username, Email); + await logger.LogError("Error Adding User {Username} {Email}", _username, _email); AddModuleMessage("Error Adding User. Please Ensure Password Meets Complexity Requirements And Username Is Not Already In Use.", MessageType.Error); } } @@ -83,7 +85,7 @@ } catch (Exception ex) { - await logger.LogError(ex, "Error Adding User {Username} {Email} {Error}", Username, Email, ex.Message); + await logger.LogError(ex, "Error Adding User {Username} {Email} {Error}", _username, _email, ex.Message); AddModuleMessage("Error Adding User", MessageType.Error); } } diff --git a/Oqtane.Client/Modules/Admin/Roles/Add.razor b/Oqtane.Client/Modules/Admin/Roles/Add.razor index fccd1981..e908dd1c 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Add.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Add.razor @@ -17,7 +17,7 @@
diff --git a/Oqtane.Client/Modules/Admin/Roles/Edit.razor b/Oqtane.Client/Modules/Admin/Roles/Edit.razor index 5d884975..71bc826a 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Edit.razor @@ -9,7 +9,7 @@ @@ -17,7 +17,7 @@ @@ -25,7 +25,7 @@ @@ -27,25 +27,25 @@ else @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - List Roles; + List _roles; protected override async Task OnParametersSetAsync() { - Roles = await RoleService.GetRolesAsync(PageState.Site.SiteId); + _roles = await RoleService.GetRolesAsync(PageState.Site.SiteId); } - private async Task DeleteRole(Role Role) + private async Task DeleteRole(Role role) { try { - await RoleService.DeleteRoleAsync(Role.RoleId); - await logger.LogInformation("Role Deleted {Role}", Role); + await RoleService.DeleteRoleAsync(role.RoleId); + await logger.LogInformation("Role Deleted {Role}", role); StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Deleting Role {Role} {Error}", Role, ex.Message); + await logger.LogError(ex, "Error Deleting Role {Role} {Error}", role, ex.Message); AddModuleMessage("Error Deleting Role", MessageType.Error); } } -} \ No newline at end of file +} diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index 496d411f..b3692a49 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -31,7 +31,7 @@ diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 944610d1..a692d081 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -7,7 +7,7 @@ @inject IThemeService ThemeService @inject IUserService UserService -@if (tenants == null) +@if (_tenants == null) {

Loading...

} @@ -21,7 +21,7 @@ else @@ -41,7 +41,7 @@ else @@ -51,7 +51,7 @@ else - @if (!isinitialized) + @if (!_isinitialized) { @@ -101,7 +101,7 @@ else } @@ -113,50 +113,50 @@ else @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - Dictionary themes = new Dictionary(); - Dictionary panelayouts = new Dictionary(); - Dictionary containers = new Dictionary(); + Dictionary _themes = new Dictionary(); + Dictionary _panelayouts = new Dictionary(); + Dictionary _containers = new Dictionary(); - List Themes; - List tenants; - string tenantid = "-1"; - string name = ""; - string urls = ""; - string themetype = ""; - string layouttype = ""; - string containertype = ""; - bool isinitialized = true; - string username = ""; - string password = ""; + List _themeList; + List _tenants; + string _tenantid = "-1"; + string _name = ""; + string _urls = ""; + string _themetype = ""; + string _layouttype = ""; + string _containertype = ""; + bool _isinitialized = true; + string _username = ""; + string _password = ""; protected override async Task OnInitializedAsync() { - Themes = await ThemeService.GetThemesAsync(); - tenants = await TenantService.GetTenantsAsync(); - urls = PageState.Alias.Name; - themes = ThemeService.GetThemeTypes(Themes); - containers = ThemeService.GetContainerTypes(Themes); - username = Constants.HostUser; + _themeList = await ThemeService.GetThemesAsync(); + _tenants = await TenantService.GetTenantsAsync(); + _urls = PageState.Alias.Name; + _themes = ThemeService.GetThemeTypes(_themeList); + _containers = ThemeService.GetContainerTypes(_themeList); + _username = Constants.HostUser; } private async void TenantChanged(ChangeEventArgs e) { try { - tenantid = (string)e.Value; - if (tenantid != "-1") + _tenantid = (string)e.Value; + if (_tenantid != "-1") { - Tenant tenant = tenants.Where(item => item.TenantId == int.Parse(tenantid)).FirstOrDefault(); + Tenant tenant = _tenants.Where(item => item.TenantId == int.Parse(_tenantid)).FirstOrDefault(); if (tenant != null) { - isinitialized = tenant.IsInitialized; + _isinitialized = tenant.IsInitialized; StateHasChanged(); } } } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", tenantid, ex.Message); + await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", _tenantid, ex.Message); AddModuleMessage("Error Loading Tenant", MessageType.Error); } } @@ -165,31 +165,31 @@ else { try { - themetype = (string)e.Value; - if (themetype != "") + _themetype = (string)e.Value; + if (_themetype != "") { - panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); + _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); } else { - panelayouts = new Dictionary(); + _panelayouts = new Dictionary(); } StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", themetype, ex.Message); + await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); AddModuleMessage("Error Loading Pane Layouts For Theme", MessageType.Error); } } private async Task SaveSite() { - if (tenantid != "-1" && name != "" && urls != "" && !string.IsNullOrEmpty(themetype) && (panelayouts.Count == 0 || !string.IsNullOrEmpty(layouttype)) && !string.IsNullOrEmpty(containertype)) + if (_tenantid != "-1" && _name != "" && _urls != "" && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)) && !string.IsNullOrEmpty(_containertype)) { bool unique = true; List aliases = await AliasService.GetAliasesAsync(); - foreach (string name in urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { if (aliases.Exists(item => item.Name == name)) { @@ -200,12 +200,12 @@ else { bool isvalid = true; - if (!isinitialized) + if (!_isinitialized) { User user = new User(); user.SiteId = PageState.Site.SiteId; - user.Username = username; - user.Password = password; + user.Username = _username; + user.Password = _password; user = await UserService.LoginUserAsync(user, false, false); isvalid = user.IsAuthenticated; } @@ -215,24 +215,24 @@ else ShowProgressIndicator(); aliases = new List(); - urls = urls.Replace("\n", ","); - foreach (string name in urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + _urls = _urls.Replace("\n", ","); + foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { Alias alias = new Alias(); alias.Name = name; - alias.TenantId = int.Parse(tenantid); + alias.TenantId = int.Parse(_tenantid); alias.SiteId = -1; alias = await AliasService.AddAliasAsync(alias); aliases.Add(alias); } Site site = new Site(); - site.TenantId = int.Parse(tenantid); - site.Name = name; + site.TenantId = int.Parse(_tenantid); + site.Name = _name; site.LogoFileId = null; - site.DefaultThemeType = themetype; - site.DefaultLayoutType = (layouttype == null ? "" : layouttype); - site.DefaultContainerType = containertype; + site.DefaultThemeType = _themetype; + site.DefaultLayoutType = (_layouttype == null ? "" : _layouttype); + site.DefaultContainerType = _containertype; site = await SiteService.AddSiteAsync(site, aliases[0]); foreach (Alias alias in aliases) @@ -241,19 +241,19 @@ else await AliasService.UpdateAliasAsync(alias); } - if (!isinitialized) + if (!_isinitialized) { User user = new User(); user.SiteId = site.SiteId; - user.Username = username; - user.Password = password; + user.Username = _username; + user.Password = _password; user.Email = PageState.User.Email; user.DisplayName = PageState.User.DisplayName; user = await UserService.AddUserAsync(user, aliases[0]); if (user != null) { - Tenant tenant = tenants.Where(item => item.TenantId == int.Parse(tenantid)).FirstOrDefault(); + Tenant tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid)); tenant.IsInitialized = true; await TenantService.UpdateTenantAsync(tenant); } @@ -265,7 +265,7 @@ else } else { - await logger.LogError("Invalid Password Entered For Host {Username}", username); + await logger.LogError("Invalid Password Entered For Host {Username}", _username); AddModuleMessage("Invalid Host Password", MessageType.Error); } } diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index c9b63239..e8e0c675 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -11,6 +11,7 @@ @inject IModuleService ModuleService @inject IModuleDefinitionService ModuleDefinitionService @inject ILogService LogService +@using System.Diagnostics.CodeAnalysis @implements IHandleAfterRender @DynamicComponent @@ -65,6 +66,7 @@ } } + [SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")] private async Task Refresh() { Alias alias = null; diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index dd12b058..2be44081 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -1,5 +1,5 @@ @namespace Oqtane.UI -@inject IJSRuntime jsRuntime +@inject IJSRuntime JsRuntime @DynamicComponent diff --git a/Oqtane.Server/Controllers/AliasController.cs b/Oqtane.Server/Controllers/AliasController.cs index 20eeba79..dc4bf37b 100644 --- a/Oqtane.Server/Controllers/AliasController.cs +++ b/Oqtane.Server/Controllers/AliasController.cs @@ -49,11 +49,11 @@ namespace Oqtane.Controllers name = WebUtility.UrlDecode(name); List aliases = _aliases.GetAliases().ToList(); Alias alias = null; - alias = aliases.Where(item => item.Name == name).FirstOrDefault(); - if (alias == null && name.Contains("/")) + alias = aliases.FirstOrDefault(item => item.Name == name); + if (name != null && (alias == null && name.Contains("/"))) { // lookup alias without folder name - alias = aliases.Find(item => item.Name == name.Substring(0, name.IndexOf("/"))); + alias = aliases.Find(item => item.Name == name.Substring(0, name.IndexOf("/", StringComparison.Ordinal))); } if (alias == null && aliases.Count > 0) { @@ -71,27 +71,27 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] [Authorize(Roles = Constants.AdminRole)] - public Alias Post([FromBody] Alias Alias) + public Alias Post([FromBody] Alias alias) { if (ModelState.IsValid) { - Alias = _aliases.AddAlias(Alias); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Alias Added {Alias}", Alias); + alias = _aliases.AddAlias(alias); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Alias Added {Alias}", alias); } - return Alias; + return alias; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.AdminRole)] - public Alias Put(int id, [FromBody] Alias Alias) + public Alias Put(int id, [FromBody] Alias alias) { if (ModelState.IsValid) { - Alias = _aliases.UpdateAlias(Alias); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Alias Updated {Alias}", Alias); + alias = _aliases.UpdateAlias(alias); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Alias Updated {Alias}", alias); } - return Alias; + return alias; } // DELETE api//5 diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index ea02c5ba..4cc7ac9a 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -61,14 +61,15 @@ namespace Oqtane.Controllers { foreach (string file in Directory.GetFiles(folder)) { - files.Add(new Models.File { Name = Path.GetFileName(file), Extension = Path.GetExtension(file).Replace(".","") }); + files.Add(new Models.File {Name = Path.GetFileName(file), Extension = Path.GetExtension(file)?.Replace(".", "")}); } } } } + return files; } - + // GET: api//siteId/folderPath [HttpGet("{siteId}/{path}")] public IEnumerable Get(int siteId, string path) @@ -95,6 +96,7 @@ namespace Oqtane.Controllers HttpContext.Response.StatusCode = 401; return null; } + return files; } @@ -103,7 +105,7 @@ namespace Oqtane.Controllers public Models.File Get(int id) { Models.File file = _files.GetFile(id); - if (_userPermissions.IsAuthorized(User,PermissionNames.View, file.Folder.Permissions)) + if (_userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) { return file; } @@ -118,20 +120,21 @@ namespace Oqtane.Controllers // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.RegisteredRole)] - public Models.File Put(int id, [FromBody] Models.File File) + public Models.File Put(int id, [FromBody] Models.File file) { - if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, File.Folder.FolderId, PermissionNames.Edit)) + if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, file.Folder.FolderId, PermissionNames.Edit)) { - File = _files.UpdateFile(File); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", File); + file = _files.UpdateFile(file); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", file); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update File {File}", File); + _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update File {File}", file); HttpContext.Response.StatusCode = 401; - File = null; + file = null; } - return File; + + return file; } // DELETE api//5 @@ -149,6 +152,7 @@ namespace Oqtane.Controllers { System.IO.File.Delete(filepath); } + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file); } else @@ -164,11 +168,11 @@ namespace Oqtane.Controllers { Models.File file = null; Folder folder = _folders.GetFolder(int.Parse(folderid)); - if (folder != null && _userPermissions.IsAuthorized(User,PermissionNames.Edit, folder.Permissions)) + if (folder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions)) { string folderpath = GetFolderPath(folder); CreateDirectory(folderpath); - string filename = url.Substring(url.LastIndexOf("/") + 1); + string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); // check for allowable file extensions if (Constants.UploadableFiles.Contains(Path.GetExtension(filename).Replace(".", ""))) { @@ -180,6 +184,7 @@ namespace Oqtane.Controllers { System.IO.File.Delete(folderpath + filename); } + client.DownloadFile(url, folderpath + filename); _files.AddFile(CreateFile(filename, folder.FolderId, folderpath + filename)); } @@ -199,9 +204,10 @@ namespace Oqtane.Controllers HttpContext.Response.StatusCode = 401; file = null; } + return file; } - + // POST api//upload [HttpPost("upload")] public async Task UploadFile(string folder, IFormFile file) @@ -213,9 +219,9 @@ namespace Oqtane.Controllers if (int.TryParse(folder, out folderid)) { Folder Folder = _folders.GetFolder(folderid); - if (Folder != null && _userPermissions.IsAuthorized(User,PermissionNames.Edit, Folder.Permissions)) + if (Folder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, Folder.Permissions)) { - folderpath = GetFolderPath(Folder); + folderpath = GetFolderPath(Folder); } } else @@ -225,6 +231,7 @@ namespace Oqtane.Controllers folderpath = GetFolderPath(folder); } } + if (folderpath != "") { CreateDirectory(folderpath); @@ -232,6 +239,7 @@ namespace Oqtane.Controllers { await file.CopyToAsync(stream); } + string upload = await MergeFile(folderpath, file.FileName); if (upload != "" && folderid != -1) { @@ -252,19 +260,19 @@ namespace Oqtane.Controllers // parse the filename which is in the format of filename.ext.part_x_y string token = ".part_"; - string parts = Path.GetExtension(filename).Replace(token, ""); // returns "x_y" - int totalparts = int.Parse(parts.Substring(parts.IndexOf("_") + 1)); - filename = filename.Substring(0, filename.IndexOf(token)); // base filename - string[] fileparts = Directory.GetFiles(folder, filename + token + "*"); // list of all file parts + string parts = Path.GetExtension(filename)?.Replace(token, ""); // returns "x_y" + int totalparts = int.Parse(parts?.Substring(parts.IndexOf("_") + 1)); + filename = filename?.Substring(0, filename.IndexOf(token)); // base filename + string[] fileParts = Directory.GetFiles(folder, filename + token + "*"); // list of all file parts // if all of the file parts exist ( note that file parts can arrive out of order ) - if (fileparts.Length == totalparts && CanAccessFiles(fileparts)) + if (fileParts.Length == totalparts && CanAccessFiles(fileParts)) { // merge file parts bool success = true; using (var stream = new FileStream(Path.Combine(folder, filename + ".tmp"), FileMode.Create)) { - foreach (string filepart in fileparts) + foreach (string filepart in fileParts) { try { @@ -283,13 +291,13 @@ namespace Oqtane.Controllers // delete file parts and rename file if (success) { - foreach (string filepart in fileparts) + foreach (string filepart in fileParts) { System.IO.File.Delete(filepart); } // check for allowable file extensions - if (!Constants.UploadableFiles.Contains(Path.GetExtension(filename).Replace(".", ""))) + if (!Constants.UploadableFiles.Contains(Path.GetExtension(filename)?.Replace(".", ""))) { System.IO.File.Delete(Path.Combine(folder, filename + ".tmp")); } @@ -300,17 +308,19 @@ namespace Oqtane.Controllers { System.IO.File.Delete(Path.Combine(folder, filename)); } + // rename file now that the entire process is completed System.IO.File.Move(Path.Combine(folder, filename + ".tmp"), Path.Combine(folder, filename)); _logger.Log(LogLevel.Information, this, LogFunction.Create, "File Uploaded {File}", Path.Combine(folder, filename)); } + merged = filename; } } // clean up file parts which are more than 2 hours old ( which can happen if a prior file upload failed ) - fileparts = Directory.GetFiles(folder, "*" + token + "*"); - foreach (string filepart in fileparts) + fileParts = Directory.GetFiles(folder, "*" + token + "*"); + foreach (string filepart in fileParts) { DateTime createddate = System.IO.File.GetCreationTime(filepart).ToUniversalTime(); if (createddate < DateTime.UtcNow.AddHours(-2)) @@ -339,7 +349,7 @@ namespace Oqtane.Controllers locked = false; } catch // file is locked by another process - { + { Thread.Sleep(1000); // wait 1 second } finally @@ -349,13 +359,16 @@ namespace Oqtane.Controllers stream.Close(); } } + attempts += 1; } + if (locked && canaccess) { canaccess = false; } } + return canaccess; } @@ -364,7 +377,7 @@ namespace Oqtane.Controllers public IActionResult Download(int id) { Models.File file = _files.GetFile(id); - if (file != null && _userPermissions.IsAuthorized(User,PermissionNames.View, file.Folder.Permissions)) + if (file != null && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) { string filepath = GetFolderPath(file.Folder) + file.Name; if (System.IO.File.Exists(filepath)) @@ -402,7 +415,7 @@ namespace Oqtane.Controllers if (!Directory.Exists(folderpath)) { string path = ""; - string[] folders = folderpath.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + string[] folders = folderpath.Split(new char[] {'\\'}, StringSplitOptions.RemoveEmptyEntries); foreach (string folder in folders) { path += folder + "\\"; @@ -422,7 +435,7 @@ namespace Oqtane.Controllers FileInfo fileinfo = new FileInfo(filepath); file.Extension = fileinfo.Extension.ToLower().Replace(".", ""); - file.Size = (int)fileinfo.Length; + file.Size = (int) fileinfo.Length; file.ImageHeight = 0; file.ImageWidth = 0; @@ -434,6 +447,7 @@ namespace Oqtane.Controllers file.ImageHeight = image.Height; file.ImageWidth = image.Width; } + stream.Close(); } From 5b3feaf26fbd0bf9ccbf6e49b9efa7ea5e9f3627 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sun, 15 Mar 2020 09:38:37 +0100 Subject: [PATCH 071/265] Server naming fixes and cleanup Server is now completely cleaned up and without warnings --- Oqtane.Server/Controllers/AliasController.cs | 4 +- Oqtane.Server/Controllers/FileController.cs | 51 ++--- Oqtane.Server/Controllers/FolderController.cs | 46 ++--- .../Controllers/InstallationController.cs | 27 +-- Oqtane.Server/Controllers/JobController.cs | 20 +- Oqtane.Server/Controllers/JobLogController.cs | 20 +- Oqtane.Server/Controllers/LogController.cs | 8 +- Oqtane.Server/Controllers/ModuleController.cs | 47 +++-- .../Controllers/ModuleDefinitionController.cs | 11 +- .../Controllers/NotificationController.cs | 37 ++-- .../Controllers/PackageController.cs | 1 + Oqtane.Server/Controllers/PageController.cs | 38 ++-- .../Controllers/PageModuleController.cs | 40 ++-- .../Controllers/ProfileController.cs | 20 +- Oqtane.Server/Controllers/RoleController.cs | 20 +- .../Controllers/SettingController.cs | 49 +++-- Oqtane.Server/Controllers/SiteController.cs | 30 ++- Oqtane.Server/Controllers/TenantController.cs | 20 +- Oqtane.Server/Controllers/ThemeController.cs | 6 +- Oqtane.Server/Controllers/UserController.cs | 187 +++++++++--------- .../Controllers/UserRoleController.cs | 24 +-- .../Extensions/AssemblyExtensions.cs | 4 +- .../Extensions/OqtaneMvcBuilderExtensions.cs | 3 +- .../OqtaneServiceCollectionExtensions.cs | 23 ++- .../Infrastructure/InstallationManager.cs | 60 +++--- .../Interfaces/IInstallationManager.cs | 4 +- .../Infrastructure/Interfaces/ILogManager.cs | 16 +- .../Infrastructure/Interfaces/ISyncManager.cs | 11 +- .../Infrastructure/Jobs/HostedServiceBase.cs | 88 ++++----- .../Infrastructure/Jobs/NotificationJob.cs | 34 ++-- Oqtane.Server/Infrastructure/LogManager.cs | 81 ++++---- Oqtane.Server/Infrastructure/SyncManager.cs | 19 +- .../Controllers/HtmlTextController.cs | 28 +-- .../HtmlText/Manager/HtmlTextManager.cs | 16 +- .../HtmlText/Repository/HtmlTextContext.cs | 2 +- .../HtmlText/Repository/HtmlTextRepository.cs | 56 ++---- .../Repository/IHtmlTextRepository.cs | 11 +- Oqtane.Server/Modules/IPortable.cs | 4 +- Oqtane.Server/Pages/_Host.cshtml | 1 - Oqtane.Server/Program.cs | 8 +- Oqtane.Server/Repository/AliasRepository.cs | 26 +-- .../Repository/Context/DBContextBase.cs | 12 +- .../Repository/Context/MasterDBContext.cs | 10 +- .../Repository/Context/TenantDBContext.cs | 2 +- Oqtane.Server/Repository/FileRepository.cs | 34 ++-- Oqtane.Server/Repository/FolderRepository.cs | 42 ++-- .../Repository/Interfaces/IAliasRepository.cs | 8 +- .../Repository/Interfaces/IFileRepository.cs | 10 +- .../Interfaces/IFolderRepository.cs | 12 +- .../Interfaces/IJobLogRepository.cs | 8 +- .../Repository/Interfaces/IJobRepository.cs | 8 +- .../Repository/Interfaces/ILogRepository.cs | 10 +- .../Interfaces/IModuleDefinitionRepository.cs | 8 +- .../Interfaces/IModuleRepository.cs | 14 +- .../Interfaces/INotificationRepository.cs | 10 +- .../Interfaces/IPageModuleRepository.cs | 14 +- .../Repository/Interfaces/IPageRepository.cs | 14 +- .../Interfaces/IPermissionRepository.cs | 22 +-- .../Interfaces/IProfileRepository.cs | 10 +- .../Repository/Interfaces/IRoleRepository.cs | 12 +- .../Interfaces/ISettingRepository.cs | 10 +- .../Repository/Interfaces/ISiteRepository.cs | 8 +- .../Interfaces/ITenantRepository.cs | 12 +- .../Repository/Interfaces/IUserRepository.cs | 10 +- .../Interfaces/IUserRoleRepository.cs | 12 +- Oqtane.Server/Repository/JobLogRepository.cs | 24 +-- Oqtane.Server/Repository/JobRepository.cs | 28 +-- Oqtane.Server/Repository/LogRepository.cs | 47 ++--- .../Repository/ModuleDefinitionRepository.cs | 99 +++++----- Oqtane.Server/Repository/ModuleRepository.cs | 53 ++--- .../Repository/NotificationRepository.cs | 50 +++-- .../Repository/PageModuleRepository.cs | 42 ++-- Oqtane.Server/Repository/PageRepository.cs | 50 ++--- .../Repository/PermissionRepository.cs | 89 +++++---- Oqtane.Server/Repository/ProfileRepository.cs | 30 +-- Oqtane.Server/Repository/RoleRepository.cs | 34 ++-- Oqtane.Server/Repository/SettingRepository.cs | 32 +-- Oqtane.Server/Repository/SiteRepository.cs | 27 +-- Oqtane.Server/Repository/TenantRepository.cs | 25 ++- Oqtane.Server/Repository/TenantResolver.cs | 52 ++--- Oqtane.Server/Repository/ThemeRepository.cs | 39 ++-- Oqtane.Server/Repository/UserRepository.cs | 8 +- .../Repository/UserRoleRepository.cs | 36 ++-- .../Security/ClaimsPrincipalFactory.cs | 2 +- Oqtane.Server/Security/IUserPermissions.cs | 6 +- Oqtane.Server/Security/PermissionHandler.cs | 8 +- Oqtane.Server/Security/UserPermissions.cs | 47 +++-- Oqtane.Server/Startup.cs | 67 +++---- Oqtane.Shared/Models/Log.cs | 1 - Oqtane.Shared/Models/Module.cs | 4 +- Oqtane.Shared/Security/UserSecurity.cs | 62 +++--- Oqtane.Upgrade/Program.cs | 21 +- 92 files changed, 1223 insertions(+), 1273 deletions(-) diff --git a/Oqtane.Server/Controllers/AliasController.cs b/Oqtane.Server/Controllers/AliasController.cs index dc4bf37b..94633fd4 100644 --- a/Oqtane.Server/Controllers/AliasController.cs +++ b/Oqtane.Server/Controllers/AliasController.cs @@ -1,14 +1,14 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure; using System.Linq; using System; using System.Net; using System.Globalization; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 4cc7ac9a..35026847 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -2,8 +2,6 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Oqtane.Infrastructure; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using System; @@ -15,6 +13,10 @@ using Oqtane.Security; using System.Linq; using System.Drawing; using System.Net; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; + +// ReSharper disable StringIndexOfIsCultureSpecific.1 namespace Oqtane.Controllers { @@ -170,23 +172,23 @@ namespace Oqtane.Controllers Folder folder = _folders.GetFolder(int.Parse(folderid)); if (folder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions)) { - string folderpath = GetFolderPath(folder); - CreateDirectory(folderpath); + string folderPath = GetFolderPath(folder); + CreateDirectory(folderPath); string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); // check for allowable file extensions if (Constants.UploadableFiles.Contains(Path.GetExtension(filename).Replace(".", ""))) { try { - var client = new System.Net.WebClient(); + var client = new WebClient(); // remove file if it already exists - if (System.IO.File.Exists(folderpath + filename)) + if (System.IO.File.Exists(folderPath + filename)) { - System.IO.File.Delete(folderpath + filename); + System.IO.File.Delete(folderPath + filename); } - client.DownloadFile(url, folderpath + filename); - _files.AddFile(CreateFile(filename, folder.FolderId, folderpath + filename)); + client.DownloadFile(url, folderPath + filename); + _files.AddFile(CreateFile(filename, folder.FolderId, folderPath + filename)); } catch { @@ -202,7 +204,6 @@ namespace Oqtane.Controllers { _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Download File {Url} {FolderId}", url, folderid); HttpContext.Response.StatusCode = 401; - file = null; } return file; @@ -214,36 +215,36 @@ namespace Oqtane.Controllers { if (file.Length > 0) { - string folderpath = ""; - int folderid = -1; - if (int.TryParse(folder, out folderid)) + string folderPath = ""; + + if (int.TryParse(folder, out int folderId)) { - Folder Folder = _folders.GetFolder(folderid); - if (Folder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, Folder.Permissions)) + Folder virtualFolder = _folders.GetFolder(folderId); + if (virtualFolder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions)) { - folderpath = GetFolderPath(Folder); + folderPath = GetFolderPath(virtualFolder); } } else { if (User.IsInRole(Constants.HostRole)) { - folderpath = GetFolderPath(folder); + folderPath = GetFolderPath(folder); } } - if (folderpath != "") + if (folderPath != "") { - CreateDirectory(folderpath); - using (var stream = new FileStream(Path.Combine(folderpath, file.FileName), FileMode.Create)) + CreateDirectory(folderPath); + using (var stream = new FileStream(Path.Combine(folderPath, file.FileName), FileMode.Create)) { await file.CopyToAsync(stream); } - string upload = await MergeFile(folderpath, file.FileName); - if (upload != "" && folderid != -1) + string upload = await MergeFile(folderPath, file.FileName); + if (upload != "" && folderId != -1) { - _files.AddFile(CreateFile(upload, folderid, folderpath + upload)); + _files.AddFile(CreateFile(upload, folderId, folderPath + upload)); } } else @@ -341,7 +342,7 @@ namespace Oqtane.Controllers { int attempts = 0; bool locked = true; - while (attempts < 5 && locked == true) + while (attempts < 5 && locked) { try { @@ -415,7 +416,7 @@ namespace Oqtane.Controllers if (!Directory.Exists(folderpath)) { string path = ""; - string[] folders = folderpath.Split(new char[] {'\\'}, StringSplitOptions.RemoveEmptyEntries); + string[] folders = folderpath.Split(new[] {'\\'}, StringSplitOptions.RemoveEmptyEntries); foreach (string folder in folders) { path += folder + "\\"; diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 24da6e17..0a7c5c13 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using System.Linq; using System.Net; -using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; using Oqtane.Security; namespace Oqtane.Controllers @@ -86,14 +86,14 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] [Authorize(Roles = Constants.RegisteredRole)] - public Folder Post([FromBody] Folder Folder) + public Folder Post([FromBody] Folder folder) { if (ModelState.IsValid) { string permissions; - if (Folder.ParentId != null) + if (folder.ParentId != null) { - permissions = _folders.GetFolder(Folder.ParentId.Value).Permissions; + permissions = _folders.GetFolder(folder.ParentId.Value).Permissions; } else { @@ -101,46 +101,46 @@ namespace Oqtane.Controllers } if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions)) { - if (string.IsNullOrEmpty(Folder.Path) && Folder.ParentId != null) + if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { - Folder parent = _folders.GetFolder(Folder.ParentId.Value); - Folder.Path = parent.Path + Folder.Name + "\\"; + Folder parent = _folders.GetFolder(folder.ParentId.Value); + folder.Path = parent.Path + folder.Name + "\\"; } - Folder = _folders.AddFolder(Folder); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", Folder); + folder = _folders.AddFolder(folder); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Folder {Folder}", Folder); + _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Folder {Folder}", folder); HttpContext.Response.StatusCode = 401; - Folder = null; + folder = null; } } - return Folder; + return folder; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.RegisteredRole)] - public Folder Put(int id, [FromBody] Folder Folder) + public Folder Put(int id, [FromBody] Folder folder) { - if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, Folder.FolderId, PermissionNames.Edit)) + if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit)) { - if (string.IsNullOrEmpty(Folder.Path) && Folder.ParentId != null) + if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { - Folder parent = _folders.GetFolder(Folder.ParentId.Value); - Folder.Path = parent.Path + Folder.Name + "\\"; + Folder parent = _folders.GetFolder(folder.ParentId.Value); + folder.Path = parent.Path + folder.Name + "\\"; } - Folder = _folders.UpdateFolder(Folder); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", Folder); + folder = _folders.UpdateFolder(folder); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Folder {Folder}", Folder); + _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Folder {Folder}", folder); HttpContext.Response.StatusCode = 401; - Folder = null; + folder = null; } - return Folder; + return folder; } // PUT api//?siteid=x&folderid=y&parentid=z diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index 7f8b358f..cde7a4be 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -1,10 +1,8 @@ using DbUp; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; -using Oqtane.Infrastructure; using Oqtane.Models; using Oqtane.Shared; using System; @@ -13,6 +11,9 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading; +using Oqtane.Infrastructure.Interfaces; + +// ReSharper disable StringIndexOfIsCultureSpecific.1 namespace Oqtane.Controllers { @@ -30,7 +31,7 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] - public GenericResponse Post([FromBody] string connectionstring) + public GenericResponse Post([FromBody] string connectionString) { var response = new GenericResponse { Success = false, Message = "" }; @@ -38,7 +39,7 @@ namespace Oqtane.Controllers { bool master = false; string defaultconnectionstring = _config.GetConnectionString("DefaultConnection"); - if (string.IsNullOrEmpty(defaultconnectionstring) || connectionstring == defaultconnectionstring) + if (string.IsNullOrEmpty(defaultconnectionstring) || connectionString == defaultconnectionstring) { master = true; } @@ -52,9 +53,9 @@ namespace Oqtane.Controllers if (!exists) { string datadirectory = AppDomain.CurrentDomain.GetData("DataDirectory").ToString(); - connectionstring = connectionstring.Replace("|DataDirectory|", datadirectory); + connectionString = connectionString.Replace("|DataDirectory|", datadirectory); - SqlConnection connection = new SqlConnection(connectionstring); + SqlConnection connection = new SqlConnection(connectionString); try { using (connection) @@ -73,7 +74,7 @@ namespace Oqtane.Controllers { string masterConnectionString = ""; string databaseName = ""; - string[] fragments = connectionstring.Split(';', StringSplitOptions.RemoveEmptyEntries); + string[] fragments = connectionString.Split(';', StringSplitOptions.RemoveEmptyEntries); foreach (string fragment in fragments) { if (fragment.ToLower().Contains("initial catalog=") || fragment.ToLower().Contains("database=")) @@ -95,7 +96,7 @@ namespace Oqtane.Controllers { connection.Open(); SqlCommand command; - if (connectionstring.ToLower().Contains("attachdbfilename=")) // LocalDB + if (connectionString.ToLower().Contains("attachdbfilename=")) // LocalDB { command = new SqlCommand("CREATE DATABASE [" + databaseName + "] ON ( NAME = '" + databaseName + "', FILENAME = '" + datadirectory + "\\" + databaseName + ".mdf')", connection); } @@ -126,11 +127,11 @@ namespace Oqtane.Controllers { initializationScript = reader.ReadToEnd(); } - initializationScript = initializationScript.Replace("{ConnectionString}", connectionstring.Replace(datadirectory, "|DataDirectory|")); + initializationScript = initializationScript.Replace("{ConnectionString}", connectionString.Replace(datadirectory, "|DataDirectory|")); initializationScript = initializationScript.Replace("{Alias}", HttpContext.Request.Host.Value); } - var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionstring) + var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) .WithScript(new DbUp.Engine.SqlScript("Master.sql", initializationScript)) .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly()); // tenant scripts should be added to /Scripts folder as Embedded Resources var dbUpgrade = dbUpgradeConfig.Build(); @@ -151,9 +152,9 @@ namespace Oqtane.Controllers { config = reader.ReadToEnd(); } - connectionstring = connectionstring.Replace(datadirectory, "|DataDirectory|"); - connectionstring = connectionstring.Replace(@"\", @"\\"); - config = config.Replace("DefaultConnection\": \"", "DefaultConnection\": \"" + connectionstring); + connectionString = connectionString.Replace(datadirectory, "|DataDirectory|"); + connectionString = connectionString.Replace(@"\", @"\\"); + config = config.Replace("DefaultConnection\": \"", "DefaultConnection\": \"" + connectionString); using (StreamWriter writer = new StreamWriter(Directory.GetCurrentDirectory() + "\\appsettings.json")) { writer.WriteLine(config); diff --git a/Oqtane.Server/Controllers/JobController.cs b/Oqtane.Server/Controllers/JobController.cs index 0f5bdea3..997bad67 100644 --- a/Oqtane.Server/Controllers/JobController.cs +++ b/Oqtane.Server/Controllers/JobController.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure; using System; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Controllers { @@ -44,27 +44,27 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] [Authorize(Roles = Constants.HostRole)] - public Job Post([FromBody] Job Job) + public Job Post([FromBody] Job job) { if (ModelState.IsValid) { - Job = _jobs.AddJob(Job); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Job Added {Job}", Job); + job = _jobs.AddJob(job); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Job Added {Job}", job); } - return Job; + return job; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.HostRole)] - public Job Put(int id, [FromBody] Job Job) + public Job Put(int id, [FromBody] Job job) { if (ModelState.IsValid) { - Job = _jobs.UpdateJob(Job); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Job Updated {Job}", Job); + job = _jobs.UpdateJob(job); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Job Updated {Job}", job); } - return Job; + return job; } // DELETE api//5 diff --git a/Oqtane.Server/Controllers/JobLogController.cs b/Oqtane.Server/Controllers/JobLogController.cs index 2d9d4b7e..769e5e27 100644 --- a/Oqtane.Server/Controllers/JobLogController.cs +++ b/Oqtane.Server/Controllers/JobLogController.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Controllers { @@ -39,27 +39,27 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] [Authorize(Roles = Constants.HostRole)] - public JobLog Post([FromBody] JobLog JobLog) + public JobLog Post([FromBody] JobLog jobLog) { if (ModelState.IsValid) { - JobLog = _jobLogs.AddJobLog(JobLog); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Job Log Added {JobLog}", JobLog); + jobLog = _jobLogs.AddJobLog(jobLog); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Job Log Added {JobLog}", jobLog); } - return JobLog; + return jobLog; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.HostRole)] - public JobLog Put(int id, [FromBody] JobLog JobLog) + public JobLog Put(int id, [FromBody] JobLog jobLog) { if (ModelState.IsValid) { - JobLog = _jobLogs.UpdateJobLog(JobLog); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Job Log Updated {JobLog}", JobLog); + jobLog = _jobLogs.UpdateJobLog(jobLog); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Job Log Updated {JobLog}", jobLog); } - return JobLog; + return jobLog; } // DELETE api//5 diff --git a/Oqtane.Server/Controllers/LogController.cs b/Oqtane.Server/Controllers/LogController.cs index 9942cf8b..d59b9411 100644 --- a/Oqtane.Server/Controllers/LogController.cs +++ b/Oqtane.Server/Controllers/LogController.cs @@ -1,9 +1,9 @@ using Microsoft.AspNetCore.Mvc; using Oqtane.Models; using System.Collections.Generic; -using Oqtane.Repository; -using Oqtane.Infrastructure; using Microsoft.AspNetCore.Authorization; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; using Oqtane.Shared; namespace Oqtane.Controllers @@ -39,11 +39,11 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] - public void Post([FromBody] Log Log) + public void Post([FromBody] Log log) { if (ModelState.IsValid) { - _logger.Log(Log); + _logger.Log(log); } } } diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index 40df2585..6443e9a8 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using System.Linq; -using System.Reflection; -using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; using Oqtane.Security; namespace Oqtane.Controllers @@ -31,15 +30,15 @@ namespace Oqtane.Controllers // GET: api/?siteid=x [HttpGet] - public IEnumerable Get(string siteid) + public IEnumerable Get(string siteid) { List moduledefinitions = _moduleDefinitions.GetModuleDefinitions(int.Parse(siteid)).ToList(); - List modules = new List(); + List modules = new List(); foreach (PageModule pagemodule in _pageModules.GetPageModules(int.Parse(siteid))) { if (_userPermissions.IsAuthorized(User,PermissionNames.View, pagemodule.Module.Permissions)) { - Models.Module module = new Models.Module(); + Module module = new Module(); module.SiteId = pagemodule.Module.SiteId; module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName; module.Permissions = pagemodule.Module.Permissions; @@ -67,9 +66,9 @@ namespace Oqtane.Controllers // GET api//5 [HttpGet("{id}")] - public Models.Module Get(int id) + public Module Get(int id) { - Models.Module module = _modules.GetModule(id); + Module module = _modules.GetModule(id); if (_userPermissions.IsAuthorized(User,PermissionNames.View, module.Permissions)) { List moduledefinitions = _moduleDefinitions.GetModuleDefinitions(module.SiteId).ToList(); @@ -87,39 +86,39 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] [Authorize(Roles = Constants.RegisteredRole)] - public Models.Module Post([FromBody] Models.Module Module) + public Module Post([FromBody] Module module) { - if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, Module.PageId, PermissionNames.Edit)) + if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, module.PageId, PermissionNames.Edit)) { - Module = _modules.AddModule(Module); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Added {Module}", Module); + module = _modules.AddModule(module); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Added {Module}", module); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Module {Module}", Module); + _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Module {Module}", module); HttpContext.Response.StatusCode = 401; - Module = null; + module = null; } - return Module; + return module; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.RegisteredRole)] - public Models.Module Put(int id, [FromBody] Models.Module Module) + public Module Put(int id, [FromBody] Module module) { - if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, Module.ModuleId, PermissionNames.Edit)) + if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit)) { - Module = _modules.UpdateModule(Module); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", Module); + module = _modules.UpdateModule(module); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Module {Module}", Module); + _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Module {Module}", module); HttpContext.Response.StatusCode = 401; - Module = null; + module = null; } - return Module; + return module; } // DELETE api//5 @@ -160,12 +159,12 @@ namespace Oqtane.Controllers // POST api//import?moduleid=x [HttpPost("import")] [Authorize(Roles = Constants.RegisteredRole)] - public bool Import(int moduleid, [FromBody] string Content) + public bool Import(int moduleid, [FromBody] string content) { bool success = false; if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, moduleid, PermissionNames.Edit)) { - success = _modules.ImportModule(moduleid, Content); + success = _modules.ImportModule(moduleid, content); } else { diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 10bd92e7..77a50d8c 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -1,15 +1,16 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using Microsoft.AspNetCore.Authorization; -using Oqtane.Infrastructure; using System.IO; using System.Reflection; using System.Linq; using Microsoft.AspNetCore.Hosting; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; using Oqtane.Security; +// ReSharper disable StringIndexOfIsCultureSpecific.1 namespace Oqtane.Controllers { @@ -66,12 +67,12 @@ namespace Oqtane.Controllers // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.AdminRole)] - public void Put(int id, [FromBody] ModuleDefinition ModuleDefinition) + public void Put(int id, [FromBody] ModuleDefinition moduleDefinition) { if (ModelState.IsValid) { - _moduleDefinitions.UpdateModuleDefinition(ModuleDefinition); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Definition Updated {ModuleDefinition}", ModuleDefinition); + _moduleDefinitions.UpdateModuleDefinition(moduleDefinition); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Definition Updated {ModuleDefinition}", moduleDefinition); } } diff --git a/Oqtane.Server/Controllers/NotificationController.cs b/Oqtane.Server/Controllers/NotificationController.cs index cecc9216..cb4a1afa 100644 --- a/Oqtane.Server/Controllers/NotificationController.cs +++ b/Oqtane.Server/Controllers/NotificationController.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure; -using Microsoft.AspNetCore.Http; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; using Oqtane.Security; namespace Oqtane.Controllers @@ -49,38 +48,38 @@ namespace Oqtane.Controllers [Authorize(Roles = Constants.RegisteredRole)] public Notification Get(int id) { - Notification Notification = _notifications.GetNotification(id); - if (!(IsAuthorized(Notification.FromUserId) || IsAuthorized(Notification.ToUserId))) + Notification notification = _notifications.GetNotification(id); + if (!(IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId))) { - Notification = null; + notification = null; } - return Notification; + return notification; } // POST api/ [HttpPost] [Authorize(Roles = Constants.RegisteredRole)] - public Notification Post([FromBody] Notification Notification) + public Notification Post([FromBody] Notification notification) { - if (IsAuthorized(Notification.FromUserId)) + if (IsAuthorized(notification.FromUserId)) { - Notification = _notifications.AddNotification(Notification); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Notification Added {Notification}", Notification); + notification = _notifications.AddNotification(notification); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Notification Added {Notification}", notification); } - return Notification; + return notification; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.RegisteredRole)] - public Notification Put(int id, [FromBody] Notification Notification) + public Notification Put(int id, [FromBody] Notification notification) { - if (IsAuthorized(Notification.FromUserId)) + if (IsAuthorized(notification.FromUserId)) { - Notification = _notifications.UpdateNotification(Notification); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Notification Updated {Folder}", Notification); + notification = _notifications.UpdateNotification(notification); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Notification Updated {Folder}", notification); } - return Notification; + return notification; } // DELETE api//5 @@ -88,8 +87,8 @@ namespace Oqtane.Controllers [Authorize(Roles = Constants.RegisteredRole)] public void Delete(int id) { - Notification Notification = _notifications.GetNotification(id); - if (IsAuthorized(Notification.FromUserId) || IsAuthorized(Notification.ToUserId)) + Notification notification = _notifications.GetNotification(id); + if (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)) { _notifications.DeleteNotification(id); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Notification Deleted {NotificationId}", id); diff --git a/Oqtane.Server/Controllers/PackageController.cs b/Oqtane.Server/Controllers/PackageController.cs index 5b69d426..a6ea13ef 100644 --- a/Oqtane.Server/Controllers/PackageController.cs +++ b/Oqtane.Server/Controllers/PackageController.cs @@ -11,6 +11,7 @@ using System.Linq; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Authorization; using Oqtane.Shared; +// ReSharper disable PartialTypeWithSinglePart namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index f7167a38..394146dc 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using System.Linq; -using Oqtane.Infrastructure; using Oqtane.Security; using System.Net; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Controllers { @@ -99,14 +99,14 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] [Authorize(Roles = Constants.RegisteredRole)] - public Page Post([FromBody] Page Page) + public Page Post([FromBody] Page page) { if (ModelState.IsValid) { string permissions; - if (Page.ParentId != null) + if (page.ParentId != null) { - permissions = _pages.GetPage(Page.ParentId.Value).Permissions; + permissions = _pages.GetPage(page.ParentId.Value).Permissions; } else { @@ -115,18 +115,18 @@ namespace Oqtane.Controllers if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions)) { - Page = _pages.AddPage(Page); - _syncManager.AddSyncEvent(EntityNames.Site, Page.SiteId); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", Page); + page = _pages.AddPage(page); + _syncManager.AddSyncEvent(EntityNames.Site, page.SiteId); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Page {Page}", Page); + _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Page {Page}", page); HttpContext.Response.StatusCode = 401; - Page = null; + page = null; } } - return Page; + return page; } // POST api//5?userid=x @@ -195,21 +195,21 @@ namespace Oqtane.Controllers // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.RegisteredRole)] - public Page Put(int id, [FromBody] Page Page) + public Page Put(int id, [FromBody] Page page) { - if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, Page.PageId, PermissionNames.Edit)) + if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit)) { - Page = _pages.UpdatePage(Page); - _syncManager.AddSyncEvent(EntityNames.Site, Page.SiteId); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", Page); + page = _pages.UpdatePage(page); + _syncManager.AddSyncEvent(EntityNames.Site, page.SiteId); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", page); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Page {Page}", Page); + _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Page {Page}", page); HttpContext.Response.StatusCode = 401; - Page = null; + page = null; } - return Page; + return page; } // PUT api//?siteid=x&pageid=y&parentid=z diff --git a/Oqtane.Server/Controllers/PageModuleController.cs b/Oqtane.Server/Controllers/PageModuleController.cs index 265a3f00..6c30a364 100644 --- a/Oqtane.Server/Controllers/PageModuleController.cs +++ b/Oqtane.Server/Controllers/PageModuleController.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using System.Linq; -using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; using Oqtane.Security; namespace Oqtane.Controllers @@ -14,15 +14,13 @@ namespace Oqtane.Controllers public class PageModuleController : Controller { private readonly IPageModuleRepository _pageModules; - private readonly IModuleRepository _modules; private readonly IUserPermissions _userPermissions; private readonly ISyncManager _syncManager; private readonly ILogManager _logger; - public PageModuleController(IPageModuleRepository pageModules, IModuleRepository modules, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger) + public PageModuleController(IPageModuleRepository pageModules, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger) { _pageModules = pageModules; - _modules = modules; _userPermissions = userPermissions; _syncManager = syncManager; _logger = logger; @@ -65,41 +63,41 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] [Authorize(Roles = Constants.RegisteredRole)] - public PageModule Post([FromBody] PageModule PageModule) + public PageModule Post([FromBody] PageModule pageModule) { - if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, PageModule.PageId, PermissionNames.Edit)) + if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, pageModule.PageId, PermissionNames.Edit)) { - PageModule = _pageModules.AddPageModule(PageModule); - _syncManager.AddSyncEvent(EntityNames.Page, PageModule.PageId); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Module Added {PageModule}", PageModule); + pageModule = _pageModules.AddPageModule(pageModule); + _syncManager.AddSyncEvent(EntityNames.Page, pageModule.PageId); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Module Added {PageModule}", pageModule); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add PageModule {PageModule}", PageModule); + _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add PageModule {PageModule}", pageModule); HttpContext.Response.StatusCode = 401; - PageModule = null; + pageModule = null; } - return PageModule; + return pageModule; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.RegisteredRole)] - public PageModule Put(int id, [FromBody] PageModule PageModule) + public PageModule Put(int id, [FromBody] PageModule pageModule) { - if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, PageModule.ModuleId, PermissionNames.Edit)) + if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, pageModule.ModuleId, PermissionNames.Edit)) { - PageModule = _pageModules.UpdatePageModule(PageModule); - _syncManager.AddSyncEvent(EntityNames.Page, PageModule.PageId); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Updated {PageModule}", PageModule); + pageModule = _pageModules.UpdatePageModule(pageModule); + _syncManager.AddSyncEvent(EntityNames.Page, pageModule.PageId); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Updated {PageModule}", pageModule); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update PageModule {PageModule}", PageModule); + _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update PageModule {PageModule}", pageModule); HttpContext.Response.StatusCode = 401; - PageModule = null; + pageModule = null; } - return PageModule; + return pageModule; } // PUT api//?pageid=x&pane=y diff --git a/Oqtane.Server/Controllers/ProfileController.cs b/Oqtane.Server/Controllers/ProfileController.cs index b325fccf..0244515b 100644 --- a/Oqtane.Server/Controllers/ProfileController.cs +++ b/Oqtane.Server/Controllers/ProfileController.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Controllers { @@ -37,27 +37,27 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] [Authorize(Roles = Constants.AdminRole)] - public Profile Post([FromBody] Profile Profile) + public Profile Post([FromBody] Profile profile) { if (ModelState.IsValid) { - Profile = _profiles.AddProfile(Profile); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Profile Added {Profile}", Profile); + profile = _profiles.AddProfile(profile); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Profile Added {Profile}", profile); } - return Profile; + return profile; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.AdminRole)] - public Profile Put(int id, [FromBody] Profile Profile) + public Profile Put(int id, [FromBody] Profile profile) { if (ModelState.IsValid) { - Profile = _profiles.UpdateProfile(Profile); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Profile Updated {Profile}", Profile); + profile = _profiles.UpdateProfile(profile); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Profile Updated {Profile}", profile); } - return Profile; + return profile; } // DELETE api//5 diff --git a/Oqtane.Server/Controllers/RoleController.cs b/Oqtane.Server/Controllers/RoleController.cs index 7de3ae22..d6767d57 100644 --- a/Oqtane.Server/Controllers/RoleController.cs +++ b/Oqtane.Server/Controllers/RoleController.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Controllers { @@ -39,27 +39,27 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] [Authorize(Roles = Constants.AdminRole)] - public Role Post([FromBody] Role Role) + public Role Post([FromBody] Role role) { if (ModelState.IsValid) { - Role = _roles.AddRole(Role); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Role Added {Role}", Role); + role = _roles.AddRole(role); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Role Added {Role}", role); } - return Role; + return role; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.AdminRole)] - public Role Put(int id, [FromBody] Role Role) + public Role Put(int id, [FromBody] Role role) { if (ModelState.IsValid) { - Role = _roles.UpdateRole(Role); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Role Updated {Role}", Role); + role = _roles.UpdateRole(role); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Role Updated {Role}", role); } - return Role; + return role; } // DELETE api//5 diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index c0a8571d..326aa276 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using Oqtane.Security; -using Oqtane.Infrastructure; using System.Linq; -using Microsoft.AspNetCore.Http; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Controllers { @@ -62,38 +61,38 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] - public Setting Post([FromBody] Setting Setting) + public Setting Post([FromBody] Setting setting) { - if (ModelState.IsValid && IsAuthorized(Setting.EntityName, Setting.EntityId, PermissionNames.Edit)) + if (ModelState.IsValid && IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit)) { - Setting = _settings.AddSetting(Setting); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Setting Added {Setting}", Setting); + setting = _settings.AddSetting(setting); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Setting Added {Setting}", setting); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Setting {Setting}", Setting); + _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Setting {Setting}", setting); HttpContext.Response.StatusCode = 401; - Setting = null; + setting = null; } - return Setting; + return setting; } // PUT api//5 [HttpPut("{id}")] - public Setting Put(int id, [FromBody] Setting Setting) + public Setting Put(int id, [FromBody] Setting setting) { - if (ModelState.IsValid && IsAuthorized(Setting.EntityName, Setting.EntityId, PermissionNames.Edit)) + if (ModelState.IsValid && IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit)) { - Setting = _settings.UpdateSetting(Setting); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Setting Updated {Setting}", Setting); + setting = _settings.UpdateSetting(setting); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Setting Updated {Setting}", setting); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Setting {Setting}", Setting); + _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Setting {Setting}", setting); HttpContext.Response.StatusCode = 401; - Setting = null; + setting = null; } - return Setting; + return setting; } // DELETE api//5 @@ -113,15 +112,15 @@ namespace Oqtane.Controllers } } - private bool IsAuthorized(string EntityName, int EntityId, string PermissionName) + private bool IsAuthorized(string entityName, int entityId, string permissionName) { bool authorized = false; - if (EntityName == EntityNames.PageModule) + if (entityName == EntityNames.PageModule) { - EntityName = EntityNames.Module; - EntityId = _pageModules.GetPageModule(EntityId).ModuleId; + entityName = EntityNames.Module; + entityId = _pageModules.GetPageModule(entityId).ModuleId; } - switch (EntityName) + switch (entityName) { case EntityNames.Host: authorized = User.IsInRole(Constants.HostRole); @@ -132,13 +131,13 @@ namespace Oqtane.Controllers case EntityNames.Page: case EntityNames.Module: case EntityNames.Folder: - authorized = _userPermissions.IsAuthorized(User, EntityName, EntityId, PermissionName); + authorized = _userPermissions.IsAuthorized(User, entityName, entityId, permissionName); break; case EntityNames.User: authorized = true; - if (PermissionName == PermissionNames.Edit) + if (permissionName == PermissionNames.Edit) { - authorized = User.IsInRole(Constants.AdminRole) || (_userPermissions.GetUser(User).UserId == EntityId); + authorized = User.IsInRole(Constants.AdminRole) || (_userPermissions.GetUser(User).UserId == entityId); } break; } diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index 47939bf4..ad482652 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -1,13 +1,11 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using System.Linq; -using System.IO; -using Microsoft.AspNetCore.Hosting; -using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Controllers { @@ -16,15 +14,13 @@ namespace Oqtane.Controllers { private readonly ISiteRepository _sites; private readonly ITenantResolver _tenants; - private readonly IWebHostEnvironment _environment; private readonly ISyncManager _syncManager; private readonly ILogManager _logger; - public SiteController(ISiteRepository sites, ITenantResolver tenants, IWebHostEnvironment environment, ISyncManager syncManager, ILogManager logger) + public SiteController(ISiteRepository sites, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger) { _sites = sites; _tenants = tenants; - _environment = environment; _syncManager = syncManager; _logger = logger; } @@ -46,7 +42,7 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] - public Site Post([FromBody] Site Site) + public Site Post([FromBody] Site site) { if (ModelState.IsValid) { @@ -56,7 +52,7 @@ namespace Oqtane.Controllers // provision initial site during installation authorized = true; Tenant tenant = _tenants.GetTenant(); - Site.TenantId = tenant.TenantId; + site.TenantId = tenant.TenantId; } else { @@ -64,25 +60,25 @@ namespace Oqtane.Controllers } if (authorized) { - Site = _sites.AddSite(Site); - _logger.Log(Site.SiteId, LogLevel.Information, this, LogFunction.Create, "Site Added {Site}", Site); + site = _sites.AddSite(site); + _logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Create, "Site Added {Site}", site); } } - return Site; + return site; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.HostRole)] - public Site Put(int id, [FromBody] Site Site) + public Site Put(int id, [FromBody] Site site) { if (ModelState.IsValid) { - Site = _sites.UpdateSite(Site); - _syncManager.AddSyncEvent(EntityNames.Site, Site.SiteId); - _logger.Log(Site.SiteId, LogLevel.Information, this, LogFunction.Update, "Site Updated {Site}", Site); + site = _sites.UpdateSite(site); + _syncManager.AddSyncEvent(EntityNames.Site, site.SiteId); + _logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Update, "Site Updated {Site}", site); } - return Site; + return site; } // DELETE api//5 diff --git a/Oqtane.Server/Controllers/TenantController.cs b/Oqtane.Server/Controllers/TenantController.cs index 61ecae8e..aadb2404 100644 --- a/Oqtane.Server/Controllers/TenantController.cs +++ b/Oqtane.Server/Controllers/TenantController.cs @@ -1,10 +1,10 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using System.Collections.Generic; using Oqtane.Shared; -using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Controllers { @@ -39,27 +39,27 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] [Authorize(Roles = Constants.HostRole)] - public Tenant Post([FromBody] Tenant Tenant) + public Tenant Post([FromBody] Tenant tenant) { if (ModelState.IsValid) { - Tenant = _tenants.AddTenant(Tenant); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Tenant Added {TenantId}", Tenant.TenantId); + tenant = _tenants.AddTenant(tenant); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Tenant Added {TenantId}", tenant.TenantId); } - return Tenant; + return tenant; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.HostRole)] - public Tenant Put(int id, [FromBody] Tenant Tenant) + public Tenant Put(int id, [FromBody] Tenant tenant) { if (ModelState.IsValid) { - Tenant = _tenants.UpdateTenant(Tenant); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Tenant Updated {TenantId}", Tenant.TenantId); + tenant = _tenants.UpdateTenant(tenant); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Tenant Updated {TenantId}", tenant.TenantId); } - return Tenant; + return tenant; } // DELETE api//5 diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index 6ae226a7..e51275e0 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -1,14 +1,16 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; -using Oqtane.Repository; using Oqtane.Models; using Microsoft.AspNetCore.Authorization; using Oqtane.Shared; -using Oqtane.Infrastructure; using System.IO; using System.Reflection; using System.Linq; using Microsoft.AspNetCore.Hosting; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; + +// ReSharper disable StringIndexOfIsCultureSpecific.1 namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index c338a0cc..e278f8ae 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -2,17 +2,16 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Microsoft.AspNetCore.Identity; using System.Threading.Tasks; using System.Linq; using System.Security.Claims; using Oqtane.Shared; -using Oqtane.Infrastructure; using System; -using Microsoft.AspNetCore.Http; using System.Net; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Controllers { @@ -73,43 +72,39 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] - public async Task Post([FromBody] User User) + public async Task Post([FromBody] User user) { - User user = null; + User newUser = null; if (ModelState.IsValid) { - bool verified = true; - // users created by non-administrators must be verified - if (!base.User.IsInRole(Constants.AdminRole) && User.Username != Constants.HostUser) - { - verified = false; - } + // users created by non-administrators must be verified + bool verified = !(!User.IsInRole(Constants.AdminRole) && user.Username != Constants.HostUser); - IdentityUser identityuser = await _identityUserManager.FindByNameAsync(User.Username); + IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); if (identityuser == null) { identityuser = new IdentityUser(); - identityuser.UserName = User.Username; - identityuser.Email = User.Email; + identityuser.UserName = user.Username; + identityuser.Email = user.Email; identityuser.EmailConfirmed = verified; - var result = await _identityUserManager.CreateAsync(identityuser, User.Password); + var result = await _identityUserManager.CreateAsync(identityuser, user.Password); if (result.Succeeded) { - User.LastLoginOn = null; - User.LastIPAddress = ""; - user = _users.AddUser(User); + user.LastLoginOn = null; + user.LastIPAddress = ""; + newUser = _users.AddUser(user); if (!verified) { Notification notification = new Notification(); - notification.SiteId = User.SiteId; + notification.SiteId = user.SiteId; notification.FromUserId = null; - notification.ToUserId = user.UserId; + notification.ToUserId = newUser.UserId; notification.ToEmail = ""; notification.Subject = "User Account Verification"; string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser); - string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + User.Username + "&token=" + WebUtility.UrlEncode(token); - notification.Body = "Dear " + User.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!"; + string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token); + notification.Body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!"; notification.ParentId = null; notification.CreatedOn = DateTime.UtcNow; notification.IsDelivered = false; @@ -118,11 +113,11 @@ namespace Oqtane.Controllers } // assign to host role if this is the host user ( initial installation ) - if (User.Username == Constants.HostUser) + if (user.Username == Constants.HostUser) { - int hostroleid = _roles.GetRoles(User.SiteId, true).Where(item => item.Name == Constants.HostRole).FirstOrDefault().RoleId; + int hostroleid = _roles.GetRoles(user.SiteId, true).Where(item => item.Name == Constants.HostRole).FirstOrDefault().RoleId; UserRole userrole = new UserRole(); - userrole.UserId = user.UserId; + userrole.UserId = newUser.UserId; userrole.RoleId = hostroleid; userrole.EffectiveDate = null; userrole.ExpiryDate = null; @@ -130,75 +125,79 @@ namespace Oqtane.Controllers } // add folder for user - Folder folder = _folders.GetFolder(User.SiteId, "Users\\"); + Folder folder = _folders.GetFolder(user.SiteId, "Users\\"); if (folder != null) { - _folders.AddFolder(new Folder { SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", Path = folder.Path + user.UserId.ToString() + "\\", Order = 1, IsSystem = true, - Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + user.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + user.UserId.ToString() + "]\"}]" }); + _folders.AddFolder(new Folder { SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", Path = folder.Path + newUser.UserId.ToString() + "\\", Order = 1, IsSystem = true, + Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"}]" }); } } } else { - var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, User.Password, false); + var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false); if (result.Succeeded) { - user = _users.GetUser(User.Username); + newUser = _users.GetUser(user.Username); } } - if (user != null && User.Username != Constants.HostUser) + if (newUser != null && user.Username != Constants.HostUser) { // add auto assigned roles to user for site - List roles = _roles.GetRoles(User.SiteId).Where(item => item.IsAutoAssigned == true).ToList(); + List roles = _roles.GetRoles(user.SiteId).Where(item => item.IsAutoAssigned).ToList(); foreach (Role role in roles) { UserRole userrole = new UserRole(); - userrole.UserId = user.UserId; + userrole.UserId = newUser.UserId; userrole.RoleId = role.RoleId; userrole.EffectiveDate = null; userrole.ExpiryDate = null; _userRoles.AddUserRole(userrole); } } - user.Password = ""; // remove sensitive information - _logger.Log(User.SiteId, LogLevel.Information, this, LogFunction.Create, "User Added {User}", user); + + if (newUser != null) + { + newUser.Password = ""; // remove sensitive information + _logger.Log(user.SiteId, LogLevel.Information, this, LogFunction.Create, "User Added {User}", newUser); + } } - return user; + return newUser; } // PUT api//5 [HttpPut("{id}")] [Authorize] - public async Task Put(int id, [FromBody] User User) + public async Task Put(int id, [FromBody] User user) { if (ModelState.IsValid) { - if (base.User.IsInRole(Constants.AdminRole) || base.User.Identity.Name == User.Username) + if (User.IsInRole(Constants.AdminRole) || User.Identity.Name == user.Username) { - if (User.Password != "") + if (user.Password != "") { - IdentityUser identityuser = await _identityUserManager.FindByNameAsync(User.Username); + IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); if (identityuser != null) { - identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, User.Password); + identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, user.Password); await _identityUserManager.UpdateAsync(identityuser); } } - User = _users.UpdateUser(User); - _syncManager.AddSyncEvent(EntityNames.User, User.UserId); - User.Password = ""; // remove sensitive information - _logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", User); + user = _users.UpdateUser(user); + _syncManager.AddSyncEvent(EntityNames.User, user.UserId); + user.Password = ""; // remove sensitive information + _logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update User {User}", User); + _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update User {User}", user); HttpContext.Response.StatusCode = 401; - User = null; + user = null; } } - return User; + return user; } // DELETE api//5?siteid=x @@ -222,147 +221,147 @@ namespace Oqtane.Controllers // POST api//login [HttpPost("login")] - public async Task Login([FromBody] User User, bool SetCookie, bool IsPersistent) + public async Task Login([FromBody] User user, bool setCookie, bool isPersistent) { - User user = new Models.User { Username = User.Username, IsAuthenticated = false }; + User loginUser = new User { Username = user.Username, IsAuthenticated = false }; if (ModelState.IsValid) { - IdentityUser identityuser = await _identityUserManager.FindByNameAsync(User.Username); + IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); if (identityuser != null) { - var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, User.Password, false); + var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false); if (result.Succeeded) { - user = _users.GetUser(identityuser.UserName); - if (user != null) + loginUser = _users.GetUser(identityuser.UserName); + if (loginUser != null) { if (identityuser.EmailConfirmed) { - user.IsAuthenticated = true; - user.LastLoginOn = DateTime.UtcNow; - user.LastIPAddress = HttpContext.Connection.RemoteIpAddress.ToString(); - _users.UpdateUser(user); - _logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", User.Username); - if (SetCookie) + loginUser.IsAuthenticated = true; + loginUser.LastLoginOn = DateTime.UtcNow; + loginUser.LastIPAddress = HttpContext.Connection.RemoteIpAddress.ToString(); + _users.UpdateUser(loginUser); + _logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", user.Username); + if (setCookie) { - await _identitySignInManager.SignInAsync(identityuser, IsPersistent); + await _identitySignInManager.SignInAsync(identityuser, isPersistent); } } else { - _logger.Log(LogLevel.Information, this, LogFunction.Security, "User Not Verified {Username}", User.Username); + _logger.Log(LogLevel.Information, this, LogFunction.Security, "User Not Verified {Username}", user.Username); } } } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "User Login Failed {Username}", User.Username); + _logger.Log(LogLevel.Error, this, LogFunction.Security, "User Login Failed {Username}", user.Username); } } } - return user; + return loginUser; } // POST api//logout [HttpPost("logout")] [Authorize] - public async Task Logout([FromBody] User User) + public async Task Logout([FromBody] User user) { await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme); - _logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout {Username}", User.Username); + _logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout {Username}", user.Username); } // POST api//verify [HttpPost("verify")] - public async Task Verify([FromBody] User User, string token) + public async Task Verify([FromBody] User user, string token) { if (ModelState.IsValid) { - IdentityUser identityuser = await _identityUserManager.FindByNameAsync(User.Username); + IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); if (identityuser != null) { var result = await _identityUserManager.ConfirmEmailAsync(identityuser, token); if (result.Succeeded) { - _logger.Log(LogLevel.Information, this, LogFunction.Security, "Email Verified For {Username}", User.Username); + _logger.Log(LogLevel.Information, this, LogFunction.Security, "Email Verified For {Username}", user.Username); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}", User.Username); - User = null; + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}", user.Username); + user = null; } } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}", User.Username); - User = null; + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}", user.Username); + user = null; } } - return User; + return user; } // POST api//forgot [HttpPost("forgot")] - public async Task Forgot([FromBody] User User) + public async Task Forgot([FromBody] User user) { if (ModelState.IsValid) { - IdentityUser identityuser = await _identityUserManager.FindByNameAsync(User.Username); + IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); if (identityuser != null) { Notification notification = new Notification(); - notification.SiteId = User.SiteId; + notification.SiteId = user.SiteId; notification.FromUserId = null; - notification.ToUserId = User.UserId; + notification.ToUserId = user.UserId; notification.ToEmail = ""; notification.Subject = "User Password Reset"; string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser); - string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/reset?name=" + User.Username + "&token=" + WebUtility.UrlEncode(token); - notification.Body = "Dear " + User.DisplayName + ",\n\nPlease Click The Link Displayed Below To Reset Your Password:\n\n" + url + "\n\nThank You!"; + string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token); + notification.Body = "Dear " + user.DisplayName + ",\n\nPlease Click The Link Displayed Below To Reset Your Password:\n\n" + url + "\n\nThank You!"; notification.ParentId = null; notification.CreatedOn = DateTime.UtcNow; notification.IsDelivered = false; notification.DeliveredOn = null; _notifications.AddNotification(notification); - _logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", User.Username); + _logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Notification Failed For {Username}", User.Username); + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Notification Failed For {Username}", user.Username); } } } // POST api//reset [HttpPost("reset")] - public async Task Reset([FromBody] User User, string token) + public async Task Reset([FromBody] User user, string token) { if (ModelState.IsValid) { - IdentityUser identityuser = await _identityUserManager.FindByNameAsync(User.Username); + IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); if (identityuser != null && !string.IsNullOrEmpty(token)) { - var result = await _identityUserManager.ResetPasswordAsync(identityuser, token, User.Password); + var result = await _identityUserManager.ResetPasswordAsync(identityuser, token, user.Password); if (result.Succeeded) { - _logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset For {Username}", User.Username); - User.Password = ""; + _logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset For {Username}", user.Username); + user.Password = ""; } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username}", User.Username); - User = null; + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username}", user.Username); + user = null; } } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username}", User.Username); - User = null; + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username}", user.Username); + user = null; } } - return User; + return user; } // GET api//current @@ -382,10 +381,10 @@ namespace Oqtane.Controllers return user; } - private string GetUserRoles(int UserId, int SiteId) + private string GetUserRoles(int userId, int siteId) { string roles = ""; - List userroles = _userRoles.GetUserRoles(UserId, SiteId).ToList(); + List userroles = _userRoles.GetUserRoles(userId, siteId).ToList(); foreach (UserRole userrole in userroles) { roles += userrole.Role.Name + ";"; diff --git a/Oqtane.Server/Controllers/UserRoleController.cs b/Oqtane.Server/Controllers/UserRoleController.cs index 7345beb9..74eaace9 100644 --- a/Oqtane.Server/Controllers/UserRoleController.cs +++ b/Oqtane.Server/Controllers/UserRoleController.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Controllers { @@ -41,29 +41,29 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] [Authorize(Roles = Constants.AdminRole)] - public UserRole Post([FromBody] UserRole UserRole) + public UserRole Post([FromBody] UserRole userRole) { if (ModelState.IsValid) { - UserRole = _userRoles.AddUserRole(UserRole); - _syncManager.AddSyncEvent(EntityNames.User, UserRole.UserId); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", UserRole); + userRole = _userRoles.AddUserRole(userRole); + _syncManager.AddSyncEvent(EntityNames.User, userRole.UserId); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole); } - return UserRole; + return userRole; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = Constants.AdminRole)] - public UserRole Put(int id, [FromBody] UserRole UserRole) + public UserRole Put(int id, [FromBody] UserRole userRole) { if (ModelState.IsValid) { - UserRole = _userRoles.UpdateUserRole(UserRole); - _syncManager.AddSyncEvent(EntityNames.User, UserRole.UserId); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "User Role Updated {UserRole}", UserRole); + userRole = _userRoles.UpdateUserRole(userRole); + _syncManager.AddSyncEvent(EntityNames.User, userRole.UserId); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "User Role Updated {UserRole}", userRole); } - return UserRole; + return userRole; } // DELETE api//5 diff --git a/Oqtane.Server/Extensions/AssemblyExtensions.cs b/Oqtane.Server/Extensions/AssemblyExtensions.cs index 7c3bb924..8cb7c39d 100644 --- a/Oqtane.Server/Extensions/AssemblyExtensions.cs +++ b/Oqtane.Server/Extensions/AssemblyExtensions.cs @@ -1,7 +1,7 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +// ReSharper disable once CheckNamespace namespace System.Reflection { public static class AssemblyExtensions diff --git a/Oqtane.Server/Extensions/OqtaneMvcBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneMvcBuilderExtensions.cs index 61a3103c..9464e767 100644 --- a/Oqtane.Server/Extensions/OqtaneMvcBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneMvcBuilderExtensions.cs @@ -3,6 +3,7 @@ using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationParts; +// ReSharper disable once CheckNamespace namespace Microsoft.Extensions.DependencyInjection { public static class OqtaneMvcBuilderExtensions @@ -30,4 +31,4 @@ namespace Microsoft.Extensions.DependencyInjection return mvcBuilder; } } -} \ No newline at end of file +} diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index c32f7194..3911d03b 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -8,15 +8,16 @@ using Microsoft.Extensions.Hosting; using Oqtane.Infrastructure; using Oqtane.Modules; +// ReSharper disable once CheckNamespace namespace Microsoft.Extensions.DependencyInjection { public static class OqtaneServiceCollectionExtensions { - private static readonly IList _oqtaneModuleAssemblies = new List(); + private static readonly IList OqtaneModuleAssemblies = new List(); private static Assembly[] Assemblies => AppDomain.CurrentDomain.GetAssemblies(); - internal static IEnumerable GetOqtaneModuleAssemblies() => _oqtaneModuleAssemblies; + internal static IEnumerable GetOqtaneModuleAssemblies() => OqtaneModuleAssemblies; public static IServiceCollection AddOqtaneModules(this IServiceCollection services) { @@ -50,15 +51,17 @@ namespace Microsoft.Extensions.DependencyInjection } // dynamically register module services, contexts, and repository classes - var assemblies = Assemblies. - Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray(); + var assemblies = Assemblies.Where(item => item.FullName != null && (item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module."))).ToArray(); foreach (var assembly in assemblies) { var implementationTypes = assembly.GetInterfaces(); foreach (var implementationType in implementationTypes) { - var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}")); - services.AddScoped(serviceType ?? implementationType, implementationType); + if (implementationType.AssemblyQualifiedName != null) + { + var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}")); + services.AddScoped(serviceType ?? implementationType, implementationType); + } } } @@ -91,14 +94,16 @@ namespace Microsoft.Extensions.DependencyInjection private static void LoadAssemblies(string pattern) { - var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); + if (assemblyPath == null) return; + var assembliesFolder = new DirectoryInfo(assemblyPath); // iterate through Oqtane theme assemblies in /bin ( filter is narrow to optimize loading process ) foreach (var file in assembliesFolder.EnumerateFiles($"*.{pattern}.*.dll")) { // check if assembly is already loaded - var assembly = Assemblies.Where(a => a.Location == file.FullName).FirstOrDefault(); + var assembly = Assemblies.FirstOrDefault(a => a.Location == file.FullName); if (assembly == null) { // load assembly from stream to prevent locking file ( as long as dependencies are in /bin they will load as well ) @@ -106,7 +111,7 @@ namespace Microsoft.Extensions.DependencyInjection if (pattern == "Module") { // build a list of module assemblies - _oqtaneModuleAssemblies.Add(assembly); + OqtaneModuleAssemblies.Add(assembly); } } } diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index b375d21d..26457c53 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -7,6 +7,7 @@ using System.Xml; using Oqtane.Shared; using System; using System.Diagnostics; +using Oqtane.Infrastructure.Interfaces; namespace Oqtane.Infrastructure { @@ -21,27 +22,27 @@ namespace Oqtane.Infrastructure _environment = environment; } - public void InstallPackages(string Folders, bool Restart) + public void InstallPackages(string folders, bool restart) { bool install = false; - string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + string binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); - foreach (string Folder in Folders.Split(',')) + foreach (string folder in folders.Split(',')) { - string folder = Path.Combine(_environment.WebRootPath, Folder); + string sourceFolder = Path.Combine(_environment.WebRootPath, folder); // create folder if it does not exist - if (!Directory.Exists(folder)) + if (!Directory.Exists(sourceFolder)) { - Directory.CreateDirectory(folder); + Directory.CreateDirectory(sourceFolder); } // iterate through packages - foreach (string packagename in Directory.GetFiles(folder, "*.nupkg")) + foreach (string packagename in Directory.GetFiles(sourceFolder, "*.nupkg")) { string name = Path.GetFileNameWithoutExtension(packagename); - string[] segments = name.Split('.'); - name = string.Join('.', segments, 0, segments.Length - 3); + string[] segments = name?.Split('.'); + if (segments != null) name = string.Join('.', segments, 0, segments.Length - 3); // iterate through files using (ZipArchive archive = ZipFile.OpenRead(packagename)) @@ -78,7 +79,7 @@ namespace Oqtane.Infrastructure { case ".pdb": case ".dll": - entry.ExtractToFile(Path.Combine(binfolder, filename), true); + if (binFolder != null) entry.ExtractToFile(Path.Combine(binFolder, filename), true); break; case ".png": case ".jpg": @@ -87,7 +88,7 @@ namespace Oqtane.Infrastructure case ".svg": case ".js": case ".css": - filename = folder + "\\" + entry.FullName.Replace("wwwroot", name).Replace("/", "\\"); + filename = sourceFolder + "\\" + entry.FullName.Replace("wwwroot", name).Replace("/", "\\"); if (!Directory.Exists(Path.GetDirectoryName(filename))) { Directory.CreateDirectory(Path.GetDirectoryName(filename)); @@ -104,7 +105,7 @@ namespace Oqtane.Infrastructure } } - if (install && Restart) + if (install && restart) { // restart application RestartApplication(); @@ -165,26 +166,29 @@ namespace Oqtane.Infrastructure private void FinishUpgrade() { - string folder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + string folder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); // check if upgrade application exists - if (File.Exists(Path.Combine(folder, "Oqtane.Upgrade.exe"))) + if (folder == null || !File.Exists(Path.Combine(folder, "Oqtane.Upgrade.exe"))) return; + // run upgrade application + var process = new Process { - // run upgrade application - var process = new Process(); - process.StartInfo.FileName = Path.Combine(folder, "Oqtane.Upgrade.exe"); - process.StartInfo.Arguments = ""; - process.StartInfo.ErrorDialog = false; - process.StartInfo.UseShellExecute = false; - process.StartInfo.CreateNoWindow = true; - process.StartInfo.RedirectStandardOutput = false; - process.StartInfo.RedirectStandardError = false; - process.Start(); - process.Dispose(); + StartInfo = + { + FileName = Path.Combine(folder, "Oqtane.Upgrade.exe"), + Arguments = "", + ErrorDialog = false, + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = false, + RedirectStandardError = false + } + }; + process.Start(); + process.Dispose(); - // stop application so upgrade application can proceed - RestartApplication(); - } + // stop application so upgrade application can proceed + RestartApplication(); } public void RestartApplication() diff --git a/Oqtane.Server/Infrastructure/Interfaces/IInstallationManager.cs b/Oqtane.Server/Infrastructure/Interfaces/IInstallationManager.cs index 52afc3f8..a6d0fa90 100644 --- a/Oqtane.Server/Infrastructure/Interfaces/IInstallationManager.cs +++ b/Oqtane.Server/Infrastructure/Interfaces/IInstallationManager.cs @@ -1,8 +1,8 @@ -namespace Oqtane.Infrastructure +namespace Oqtane.Infrastructure.Interfaces { public interface IInstallationManager { - void InstallPackages(string Folders, bool Restart); + void InstallPackages(string folders, bool restart); void UpgradeFramework(); void RestartApplication(); } diff --git a/Oqtane.Server/Infrastructure/Interfaces/ILogManager.cs b/Oqtane.Server/Infrastructure/Interfaces/ILogManager.cs index 0798f86c..920ec59c 100644 --- a/Oqtane.Server/Infrastructure/Interfaces/ILogManager.cs +++ b/Oqtane.Server/Infrastructure/Interfaces/ILogManager.cs @@ -1,15 +1,15 @@ -using Oqtane.Models; +using System; +using Oqtane.Models; using Oqtane.Shared; -using System; -namespace Oqtane.Infrastructure +namespace Oqtane.Infrastructure.Interfaces { public interface ILogManager { - void Log(LogLevel Level, object Class, LogFunction Function, string Message, params object[] Args); - void Log(LogLevel Level, object Class, LogFunction Function, Exception Exception, string Message, params object[] Args); - void Log(int SiteId, LogLevel Level, object Class, LogFunction Function, string Message, params object[] Args); - void Log(int SiteId, LogLevel Level, object Class, LogFunction Function, Exception Exception, string Message, params object[] Args); - void Log(Log Log); + void Log(LogLevel level, object @class, LogFunction function, string message, params object[] args); + void Log(LogLevel level, object @class, LogFunction function, Exception exception, string message, params object[] args); + void Log(int siteId, LogLevel level, object @class, LogFunction function, string message, params object[] args); + void Log(int siteId, LogLevel level, object @class, LogFunction function, Exception exception, string message, params object[] args); + void Log(Log log); } } diff --git a/Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs b/Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs index f5bee6df..3b821f38 100644 --- a/Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs +++ b/Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs @@ -1,13 +1,12 @@ -using Oqtane.Models; -using Oqtane.Shared; -using System; +using System; using System.Collections.Generic; +using Oqtane.Models; -namespace Oqtane.Infrastructure +namespace Oqtane.Infrastructure.Interfaces { public interface ISyncManager { - List GetSyncEvents(DateTime LastSyncDate); - void AddSyncEvent(string EntityName, int EntityId); + List GetSyncEvents(DateTime lastSyncDate); + void AddSyncEvent(string entityName, int entityId); } } diff --git a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs index 37421b87..35b8b0a9 100644 --- a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs +++ b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs @@ -13,7 +13,7 @@ namespace Oqtane.Infrastructure { public abstract class HostedServiceBase : IHostedService, IDisposable { - private Task ExecutingTask; + private Task _executingTask; private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); private readonly IServiceScopeFactory _serviceScopeFactory; @@ -34,43 +34,43 @@ namespace Oqtane.Infrastructure using (var scope = _serviceScopeFactory.CreateScope()) { // get name of job - string JobType = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName); + string jobType = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName); // load jobs and find current job - IJobRepository Jobs = scope.ServiceProvider.GetRequiredService(); - Job Job = Jobs.GetJobs().Where(item => item.JobType == JobType).FirstOrDefault(); - if (Job != null && Job.IsEnabled && !Job.IsExecuting) + IJobRepository jobs = scope.ServiceProvider.GetRequiredService(); + Job job = jobs.GetJobs().Where(item => item.JobType == jobType).FirstOrDefault(); + if (job != null && job.IsEnabled && !job.IsExecuting) { // set next execution date - if (Job.NextExecution == null) + if (job.NextExecution == null) { - if (Job.StartDate != null) + if (job.StartDate != null) { - Job.NextExecution = Job.StartDate; + job.NextExecution = job.StartDate; } else { - Job.NextExecution = DateTime.UtcNow; + job.NextExecution = DateTime.UtcNow; } } // determine if the job should be run - if (Job.NextExecution <= DateTime.UtcNow && (Job.EndDate == null || Job.EndDate >= DateTime.UtcNow)) + if (job.NextExecution <= DateTime.UtcNow && (job.EndDate == null || job.EndDate >= DateTime.UtcNow)) { - IJobLogRepository JobLogs = scope.ServiceProvider.GetRequiredService(); + IJobLogRepository jobLogs = scope.ServiceProvider.GetRequiredService(); // create a job log entry JobLog log = new JobLog(); - log.JobId = Job.JobId; + log.JobId = job.JobId; log.StartDate = DateTime.UtcNow; log.FinishDate = null; log.Succeeded = false; log.Notes = ""; - log = JobLogs.AddJobLog(log); + log = jobLogs.AddJobLog(log); // update the job to indicate it is running - Job.IsExecuting = true; - Jobs.UpdateJob(Job); + job.IsExecuting = true; + jobs.UpdateJob(job); // execute the job try @@ -86,19 +86,19 @@ namespace Oqtane.Infrastructure // update the job log log.FinishDate = DateTime.UtcNow; - JobLogs.UpdateJobLog(log); + jobLogs.UpdateJobLog(log); // update the job - Job.NextExecution = CalculateNextExecution(Job.NextExecution.Value, Job.Frequency, Job.Interval); - Job.IsExecuting = false; - Jobs.UpdateJob(Job); + job.NextExecution = CalculateNextExecution(job.NextExecution.Value, job.Frequency, job.Interval); + job.IsExecuting = false; + jobs.UpdateJob(job); // trim the job log - List logs = JobLogs.GetJobLogs().Where(item => item.JobId == Job.JobId) + List logs = jobLogs.GetJobLogs().Where(item => item.JobId == job.JobId) .OrderByDescending(item => item.JobLogId).ToList(); - for (int i = logs.Count; i > Job.RetentionHistory; i--) + for (int i = logs.Count; i > job.RetentionHistory; i--) { - JobLogs.DeleteJobLog(logs[i - 1].JobLogId); + jobLogs.DeleteJobLog(logs[i - 1].JobLogId); } } } @@ -115,28 +115,28 @@ namespace Oqtane.Infrastructure } - private DateTime CalculateNextExecution(DateTime NextExecution, string Frequency, int Interval) + private DateTime CalculateNextExecution(DateTime nextExecution, string frequency, int interval) { - switch (Frequency) + switch (frequency) { case "m": // minutes - NextExecution = NextExecution.AddMinutes(Interval); + nextExecution = nextExecution.AddMinutes(interval); break; case "H": // hours - NextExecution = NextExecution.AddHours(Interval); + nextExecution = nextExecution.AddHours(interval); break; case "d": // days - NextExecution = NextExecution.AddDays(Interval); + nextExecution = nextExecution.AddDays(interval); break; case "M": // months - NextExecution = NextExecution.AddMonths(Interval); + nextExecution = nextExecution.AddMonths(interval); break; } - if (NextExecution < DateTime.UtcNow) + if (nextExecution < DateTime.UtcNow) { - NextExecution = DateTime.UtcNow; + nextExecution = DateTime.UtcNow; } - return NextExecution; + return nextExecution; } public Task StartAsync(CancellationToken cancellationToken) @@ -146,14 +146,14 @@ namespace Oqtane.Infrastructure // set IsExecuting to false in case this job was forcefully terminated previously using (var scope = _serviceScopeFactory.CreateScope()) { - string JobType = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName); - IJobRepository Jobs = scope.ServiceProvider.GetRequiredService(); - Job Job = Jobs.GetJobs().Where(item => item.JobType == JobType).FirstOrDefault(); - if (Job != null) + string jobType = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName); + IJobRepository jobs = scope.ServiceProvider.GetRequiredService(); + Job job = jobs.GetJobs().Where(item => item.JobType == jobType).FirstOrDefault(); + if (job != null) { - Job.IsStarted = true; - Job.IsExecuting = false; - Jobs.UpdateJob(Job); + job.IsStarted = true; + job.IsExecuting = false; + jobs.UpdateJob(job); } } } @@ -162,19 +162,19 @@ namespace Oqtane.Infrastructure // can occur during the initial installation as there is no DBContext } - ExecutingTask = ExecuteAsync(_cancellationTokenSource.Token); + _executingTask = ExecuteAsync(_cancellationTokenSource.Token); - if (ExecutingTask.IsCompleted) + if (_executingTask.IsCompleted) { - return ExecutingTask; + return _executingTask; } return Task.CompletedTask; } - public async Task StopAsync(CancellationToken CancellationToken) + public async Task StopAsync(CancellationToken cancellationToken) { - if (ExecutingTask == null) + if (_executingTask == null) { return; } @@ -185,7 +185,7 @@ namespace Oqtane.Infrastructure } finally { - await Task.WhenAny(ExecutingTask, Task.Delay(Timeout.Infinite, CancellationToken)); + await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken)); } } diff --git a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs index e693924d..eea37e47 100644 --- a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs +++ b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs @@ -14,34 +14,34 @@ namespace Oqtane.Infrastructure { // JobType = "Oqtane.Infrastructure.NotificationJob, Oqtane.Server" - public NotificationJob(IServiceScopeFactory ServiceScopeFactory) : base(ServiceScopeFactory) {} + public NotificationJob(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory) {} public override string ExecuteJob(IServiceProvider provider) { string log = ""; // iterate through aliases in this installation - var Aliases = provider.GetRequiredService(); - List aliases = Aliases.GetAliases().ToList(); + var aliasRepository = provider.GetRequiredService(); + List aliases = aliasRepository.GetAliases().ToList(); foreach (Alias alias in aliases) { // use the SiteState to set the Alias explicitly so the tenant can be resolved - var sitestate = provider.GetRequiredService(); - sitestate.Alias = alias; + var siteState = provider.GetRequiredService(); + siteState.Alias = alias; // get services which require tenant resolution - var Sites = provider.GetRequiredService(); - var Settings = provider.GetRequiredService(); - var Notifications = provider.GetRequiredService(); + var siteRepository = provider.GetRequiredService(); + var settingRepository = provider.GetRequiredService(); + var notificationRepository = provider.GetRequiredService(); // iterate through sites - List sites = Sites.GetSites().ToList(); + List sites = siteRepository.GetSites().ToList(); foreach (Site site in sites) { log += "Processing Notifications For Site: " + site.Name + "\n\n"; // get site settings - List sitesettings = Settings.GetSettings("Site", site.SiteId).ToList(); + List sitesettings = settingRepository.GetSettings("Site", site.SiteId).ToList(); Dictionary settings = GetSettings(sitesettings); if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "") { @@ -61,7 +61,7 @@ namespace Oqtane.Infrastructure // iterate through notifications int sent = 0; - List notifications = Notifications.GetNotifications(site.SiteId, -1, -1).ToList(); + List notifications = notificationRepository.GetNotifications(site.SiteId, -1, -1).ToList(); foreach (Notification notification in notifications) { MailMessage mailMessage = new MailMessage(); @@ -75,7 +75,7 @@ namespace Oqtane.Infrastructure { mailMessage.Body = "From: " + site.Name + "\n"; } - mailMessage.Body += "Sent: " + notification.CreatedOn.ToString() + "\n"; + mailMessage.Body += "Sent: " + notification.CreatedOn + "\n"; if (notification.ToUserId != null) { mailMessage.To.Add(new MailAddress(notification.ToUser.Email, notification.ToUser.DisplayName)); @@ -96,15 +96,15 @@ namespace Oqtane.Infrastructure sent = sent++; notification.IsDelivered = true; notification.DeliveredOn = DateTime.UtcNow; - Notifications.UpdateNotification(notification); + notificationRepository.UpdateNotification(notification); } catch (Exception ex) { // error - log += ex.Message.ToString() + "\n\n"; + log += ex.Message + "\n\n"; } } - log += "Notifications Delivered: " + sent.ToString() + "\n\n"; + log += "Notifications Delivered: " + sent + "\n\n"; } else { @@ -117,10 +117,10 @@ namespace Oqtane.Infrastructure } - private Dictionary GetSettings(List Settings) + private Dictionary GetSettings(List settings) { Dictionary dictionary = new Dictionary(); - foreach (Setting setting in Settings.OrderBy(item => item.SettingName).ToList()) + foreach (Setting setting in settings.OrderBy(item => item.SettingName).ToList()) { dictionary.Add(setting.SettingName, setting.SettingValue); } diff --git a/Oqtane.Server/Infrastructure/LogManager.cs b/Oqtane.Server/Infrastructure/LogManager.cs index 195e9c5b..11b17eb5 100644 --- a/Oqtane.Server/Infrastructure/LogManager.cs +++ b/Oqtane.Server/Infrastructure/LogManager.cs @@ -2,11 +2,14 @@ using System; using Oqtane.Models; using System.Text.Json; -using Oqtane.Repository; using Microsoft.Extensions.Configuration; using Microsoft.AspNetCore.Http; using System.Collections.Generic; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; using Oqtane.Security; +// ReSharper disable StringIndexOfIsCultureSpecific.2 +// ReSharper disable StringIndexOfIsCultureSpecific.1 namespace Oqtane.Infrastructure { @@ -27,25 +30,25 @@ namespace Oqtane.Infrastructure _accessor = accessor; } - public void Log(LogLevel Level, object Class, LogFunction Function, string Message, params object[] Args) + public void Log(LogLevel level, object @class, LogFunction function, string message, params object[] args) { - Log(-1, Level, Class.GetType().AssemblyQualifiedName, Function, null, Message, Args); + Log(-1, level, @class.GetType().AssemblyQualifiedName, function, null, message, args); } - public void Log(LogLevel Level, object Class, LogFunction Function, Exception Exception, string Message, params object[] Args) + public void Log(LogLevel level, object @class, LogFunction function, Exception exception, string message, params object[] args) { - Log(-1, Level, Class.GetType().AssemblyQualifiedName, Function, Exception, Message, Args); + Log(-1, level, @class.GetType().AssemblyQualifiedName, function, exception, message, args); } - public void Log(int SiteId, LogLevel Level, object Class, LogFunction Function, string Message, params object[] Args) + public void Log(int siteId, LogLevel level, object @class, LogFunction function, string message, params object[] args) { - Log(SiteId, Level, Class.GetType().AssemblyQualifiedName, Function, null, Message, Args); + Log(siteId, level, @class.GetType().AssemblyQualifiedName, function, null, message, args); } - public void Log(int SiteId, LogLevel Level, object Class, LogFunction Function, Exception Exception, string Message, params object[] Args) + public void Log(int siteId, LogLevel level, object @class, LogFunction function, Exception exception, string message, params object[] args) { Log log = new Log(); - if (SiteId == -1) + if (siteId == -1) { log.SiteId = null; Alias alias = _tenantResolver.GetAlias(); @@ -56,7 +59,7 @@ namespace Oqtane.Infrastructure } else { - log.SiteId = SiteId; + log.SiteId = siteId; } log.PageId = null; log.ModuleId = null; @@ -69,10 +72,10 @@ namespace Oqtane.Infrastructure HttpRequest request = _accessor.HttpContext.Request; if (request != null) { - log.Url = request.Scheme.ToString() + "://" + request.Host.ToString() + request.Path.ToString() + request.QueryString.ToString(); + log.Url = $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}"; } - Type type = Type.GetType(Class.ToString()); + Type type = @class.GetType(); if (type != null) { log.Category = type.AssemblyQualifiedName; @@ -80,20 +83,20 @@ namespace Oqtane.Infrastructure } else { - log.Category = Class.ToString(); + log.Category = @class.ToString(); log.Feature = log.Category; } - log.Function = Enum.GetName(typeof(LogFunction), Function); - log.Level = Enum.GetName(typeof(LogLevel), Level); - if (Exception != null) + log.Function = Enum.GetName(typeof(LogFunction), function); + log.Level = Enum.GetName(typeof(LogLevel), level); + if (exception != null) { - log.Exception = Exception.ToString(); + log.Exception = exception.ToString(); } - log.Message = Message; + log.Message = message; log.MessageTemplate = ""; try { - log.Properties = JsonSerializer.Serialize(Args); + log.Properties = JsonSerializer.Serialize(args); } catch // serialization error occurred { @@ -102,7 +105,7 @@ namespace Oqtane.Infrastructure Log(log); } - public void Log(Log Log) + public void Log(Log log) { LogLevel minlevel = LogLevel.Information; var section = _config.GetSection("Logging:LogLevel:Default"); @@ -111,15 +114,15 @@ namespace Oqtane.Infrastructure minlevel = Enum.Parse(_config.GetSection("Logging:LogLevel:Default").ToString()); } - if (Enum.Parse(Log.Level) >= minlevel) + if (Enum.Parse(log.Level) >= minlevel) { - Log.LogDate = DateTime.UtcNow; - Log.Server = Environment.MachineName; - Log.MessageTemplate = Log.Message; - Log = ProcessStructuredLog(Log); + log.LogDate = DateTime.UtcNow; + log.Server = Environment.MachineName; + log.MessageTemplate = log.Message; + log = ProcessStructuredLog(log); try { - _logs.AddLog(Log); + _logs.AddLog(log); } catch { @@ -128,16 +131,16 @@ namespace Oqtane.Infrastructure } } - private Log ProcessStructuredLog(Log Log) + private Log ProcessStructuredLog(Log log) { try { - string message = Log.Message; + string message = log.Message; string properties = ""; - if (!string.IsNullOrEmpty(message) && message.Contains("{") && message.Contains("}") && !string.IsNullOrEmpty(Log.Properties)) + if (!string.IsNullOrEmpty(message) && message.Contains("{") && message.Contains("}") && !string.IsNullOrEmpty(log.Properties)) { // get the named holes in the message and replace values - object[] values = JsonSerializer.Deserialize(Log.Properties); + object[] values = JsonSerializer.Deserialize(log.Properties); List names = new List(); int index = message.IndexOf("{"); while (index != -1) @@ -160,28 +163,28 @@ namespace Oqtane.Infrastructure index = message.IndexOf("{", index + 1); } // rebuild properties into dictionary - Dictionary propertydictionary = new Dictionary(); + Dictionary propertyDictionary = new Dictionary(); for (int i = 0; i < values.Length; i++) { if (i < names.Count) { - propertydictionary.Add(names[i], values[i]); + propertyDictionary.Add(names[i], values[i]); } else { - propertydictionary.Add("Property" + i.ToString(), values[i]); + propertyDictionary.Add("Property" + i.ToString(), values[i]); } } - properties = JsonSerializer.Serialize(propertydictionary); + properties = JsonSerializer.Serialize(propertyDictionary); } - Log.Message = message; - Log.Properties = properties; + log.Message = message; + log.Properties = properties; } catch { - Log.Properties = ""; + log.Properties = ""; } - return Log; + return log; } } -} \ No newline at end of file +} diff --git a/Oqtane.Server/Infrastructure/SyncManager.cs b/Oqtane.Server/Infrastructure/SyncManager.cs index b9495e22..7828bbc3 100644 --- a/Oqtane.Server/Infrastructure/SyncManager.cs +++ b/Oqtane.Server/Infrastructure/SyncManager.cs @@ -1,20 +1,21 @@ using Microsoft.Extensions.DependencyInjection; using Oqtane.Models; -using Oqtane.Repository; using System; using System.Collections.Generic; using System.Linq; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; namespace Oqtane.Infrastructure { public class SyncManager : ISyncManager { - private readonly IServiceScopeFactory ServiceScopeFactory; + private readonly IServiceScopeFactory _serviceScopeFactory; private List SyncEvents { get; set; } - public SyncManager(IServiceScopeFactory ServiceScopeFactory) + public SyncManager(IServiceScopeFactory serviceScopeFactory) { - this.ServiceScopeFactory = ServiceScopeFactory; + this._serviceScopeFactory = serviceScopeFactory; SyncEvents = new List(); } @@ -22,21 +23,21 @@ namespace Oqtane.Infrastructure { get { - using (var scope = ServiceScopeFactory.CreateScope()) + using (var scope = _serviceScopeFactory.CreateScope()) { return scope.ServiceProvider.GetRequiredService().GetTenant().TenantId; } } } - public List GetSyncEvents(DateTime LastSyncDate) + public List GetSyncEvents(DateTime lastSyncDate) { - return SyncEvents.Where(item => item.TenantId == TenantId && item.ModifiedOn >= LastSyncDate).ToList(); + return SyncEvents.Where(item => item.TenantId == TenantId && item.ModifiedOn >= lastSyncDate).ToList(); } - public void AddSyncEvent(string EntityName, int EntityId) + public void AddSyncEvent(string entityName, int entityId) { - SyncEvents.Add(new SyncEvent { TenantId = TenantId, EntityName = EntityName, EntityId = EntityId, ModifiedOn = DateTime.UtcNow }); + SyncEvents.Add(new SyncEvent { TenantId = TenantId, EntityName = entityName, EntityId = entityId, ModifiedOn = DateTime.UtcNow }); // trim sync events SyncEvents.RemoveAll(item => item.ModifiedOn < DateTime.UtcNow.AddHours(-1)); } diff --git a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs index bb8db89c..39885c81 100644 --- a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs +++ b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs @@ -3,10 +3,10 @@ using Microsoft.AspNetCore.Authorization; using Oqtane.Modules.HtmlText.Models; using Oqtane.Modules.HtmlText.Repository; using Microsoft.AspNetCore.Http; -using Oqtane.Infrastructure; using Oqtane.Shared; using System; using System.Collections.Generic; +using Oqtane.Infrastructure.Interfaces; namespace Oqtane.Modules.HtmlText.Controllers { @@ -35,11 +35,11 @@ namespace Oqtane.Modules.HtmlText.Controllers var list = new List(); try { - HtmlTextInfo HtmlText = null; + HtmlTextInfo htmlText = null; if (_entityId == id) { - HtmlText = _htmlText.GetHtmlText(id); - list.Add(HtmlText); + htmlText = _htmlText.GetHtmlText(id); + list.Add(htmlText); } } catch (Exception ex) @@ -53,16 +53,16 @@ namespace Oqtane.Modules.HtmlText.Controllers // POST api/ [HttpPost] [Authorize(Policy = "EditModule")] - public HtmlTextInfo Post([FromBody] HtmlTextInfo HtmlText) + public HtmlTextInfo Post([FromBody] HtmlTextInfo htmlText) { try { - if (ModelState.IsValid && HtmlText.ModuleId == _entityId) + if (ModelState.IsValid && htmlText.ModuleId == _entityId) { - HtmlText = _htmlText.AddHtmlText(HtmlText); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Html/Text Added {HtmlText}", HtmlText); + htmlText = _htmlText.AddHtmlText(htmlText); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Html/Text Added {HtmlText}", htmlText); } - return HtmlText; + return htmlText; } catch (Exception ex) { @@ -74,16 +74,16 @@ namespace Oqtane.Modules.HtmlText.Controllers // PUT api//5 [HttpPut("{id}")] [Authorize(Policy = "EditModule")] - public HtmlTextInfo Put(int id, [FromBody] HtmlTextInfo HtmlText) + public HtmlTextInfo Put(int id, [FromBody] HtmlTextInfo htmlText) { try { - if (ModelState.IsValid && HtmlText.ModuleId == _entityId) + if (ModelState.IsValid && htmlText.ModuleId == _entityId) { - HtmlText = _htmlText.UpdateHtmlText(HtmlText); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Html/Text Updated {HtmlText}", HtmlText); + htmlText = _htmlText.UpdateHtmlText(htmlText); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Html/Text Updated {HtmlText}", htmlText); } - return HtmlText; + return htmlText; } catch (Exception ex) { diff --git a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs index 0d87934d..f37ac523 100644 --- a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs +++ b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs @@ -14,10 +14,10 @@ namespace Oqtane.Modules.HtmlText.Manager _htmlTexts = htmltexts; } - public string ExportModule(Module Module) + public string ExportModule(Module module) { string content = ""; - HtmlTextInfo htmltext = _htmlTexts.GetHtmlText(Module.ModuleId); + HtmlTextInfo htmltext = _htmlTexts.GetHtmlText(module.ModuleId); if (htmltext != null) { content = WebUtility.HtmlEncode(htmltext.Content); @@ -25,20 +25,20 @@ namespace Oqtane.Modules.HtmlText.Manager return content; } - public void ImportModule(Module Module, string Content, string Version) + public void ImportModule(Module module, string content, string version) { - Content = WebUtility.HtmlDecode(Content); - HtmlTextInfo htmltext = _htmlTexts.GetHtmlText(Module.ModuleId); + content = WebUtility.HtmlDecode(content); + HtmlTextInfo htmltext = _htmlTexts.GetHtmlText(module.ModuleId); if (htmltext != null) { - htmltext.Content = Content; + htmltext.Content = content; _htmlTexts.UpdateHtmlText(htmltext); } else { htmltext = new HtmlTextInfo(); - htmltext.ModuleId = Module.ModuleId; - htmltext.Content = Content; + htmltext.ModuleId = module.ModuleId; + htmltext.Content = content; _htmlTexts.AddHtmlText(htmltext); } } diff --git a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs index 2ff3e6d2..70cd2065 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs @@ -9,7 +9,7 @@ namespace Oqtane.Modules.HtmlText.Repository { public virtual DbSet HtmlText { get; set; } - public HtmlTextContext(ITenantResolver TenantResolver, IHttpContextAccessor accessor) : base(TenantResolver, accessor) + public HtmlTextContext(ITenantResolver tenantResolver, IHttpContextAccessor accessor) : base(tenantResolver, accessor) { // ContextBase handles multi-tenant database connections } diff --git a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs index 144ea1e5..c97b0de7 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs @@ -13,59 +13,31 @@ namespace Oqtane.Modules.HtmlText.Repository _db = context; } - public HtmlTextInfo GetHtmlText(int ModuleId) + public HtmlTextInfo GetHtmlText(int moduleId) { - try - { - return _db.HtmlText.Where(item => item.ModuleId == ModuleId).FirstOrDefault(); - } - catch - { - throw; - } + return _db.HtmlText.FirstOrDefault(item => item.ModuleId == moduleId); } - public HtmlTextInfo AddHtmlText(HtmlTextInfo HtmlText) + public HtmlTextInfo AddHtmlText(HtmlTextInfo htmlText) { - try - { - _db.HtmlText.Add(HtmlText); - _db.SaveChanges(); - return HtmlText; - } - catch - { - throw; - } + _db.HtmlText.Add(htmlText); + _db.SaveChanges(); + return htmlText; } - public HtmlTextInfo UpdateHtmlText(HtmlTextInfo HtmlText) + public HtmlTextInfo UpdateHtmlText(HtmlTextInfo htmlText) { - try - { - _db.Entry(HtmlText).State = EntityState.Modified; - _db.SaveChanges(); - return HtmlText; - } - catch - { - throw; - } + _db.Entry(htmlText).State = EntityState.Modified; + _db.SaveChanges(); + return htmlText; } - public void DeleteHtmlText(int ModuleId) + public void DeleteHtmlText(int moduleId) { - try - { - HtmlTextInfo HtmlText = _db.HtmlText.Where(item => item.ModuleId == ModuleId).FirstOrDefault(); - _db.HtmlText.Remove(HtmlText); - _db.SaveChanges(); - } - catch - { - throw; - } + HtmlTextInfo htmlText = _db.HtmlText.FirstOrDefault(item => item.ModuleId == moduleId); + 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 33e368c4..cbe5d0e4 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs @@ -1,13 +1,12 @@ -using System.Collections.Generic; -using Oqtane.Modules.HtmlText.Models; +using Oqtane.Modules.HtmlText.Models; namespace Oqtane.Modules.HtmlText.Repository { public interface IHtmlTextRepository { - HtmlTextInfo GetHtmlText(int ModuleId); - HtmlTextInfo AddHtmlText(HtmlTextInfo HtmlText); - HtmlTextInfo UpdateHtmlText(HtmlTextInfo HtmlText); - void DeleteHtmlText(int ModuleId); + HtmlTextInfo GetHtmlText(int moduleId); + HtmlTextInfo AddHtmlText(HtmlTextInfo htmlText); + HtmlTextInfo UpdateHtmlText(HtmlTextInfo htmlText); + void DeleteHtmlText(int moduleId); } } diff --git a/Oqtane.Server/Modules/IPortable.cs b/Oqtane.Server/Modules/IPortable.cs index 871c3045..b4c89735 100644 --- a/Oqtane.Server/Modules/IPortable.cs +++ b/Oqtane.Server/Modules/IPortable.cs @@ -6,8 +6,8 @@ namespace Oqtane.Modules { // You Must Set The "ServerAssemblyName" In Your IModule Interface - string ExportModule(Module Module); + string ExportModule(Module module); - void ImportModule(Module Module, string Content, string Version); + void ImportModule(Module module, string content, string version); } } diff --git a/Oqtane.Server/Pages/_Host.cshtml b/Oqtane.Server/Pages/_Host.cshtml index c1c7782f..c5a90e79 100644 --- a/Oqtane.Server/Pages/_Host.cshtml +++ b/Oqtane.Server/Pages/_Host.cshtml @@ -1,7 +1,6 @@ @page "/" @namespace Oqtane.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -@using Oqtane.Client diff --git a/Oqtane.Server/Program.cs b/Oqtane.Server/Program.cs index ca26b836..89bb47b3 100644 --- a/Oqtane.Server/Program.cs +++ b/Oqtane.Server/Program.cs @@ -1,11 +1,9 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; -using Microsoft.AspNetCore.Blazor.Hosting; -// used by client-side Blazor -using Microsoft.Extensions.Configuration; -using Microsoft.AspNetCore; -namespace Oqtane.Server +// used by client-side Blazor + +namespace Oqtane { public class Program { diff --git a/Oqtane.Server/Repository/AliasRepository.cs b/Oqtane.Server/Repository/AliasRepository.cs index fe16d4d9..ccb26681 100644 --- a/Oqtane.Server/Repository/AliasRepository.cs +++ b/Oqtane.Server/Repository/AliasRepository.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; -using Oqtane.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; -using System; +using Oqtane.Models; namespace Oqtane.Repository { @@ -27,30 +27,30 @@ namespace Oqtane.Repository }); } - public Alias AddAlias(Alias Alias) + public Alias AddAlias(Alias alias) { - _db.Alias.Add(Alias); + _db.Alias.Add(alias); _db.SaveChanges(); _cache.Remove("aliases"); - return Alias; + return alias; } - public Alias UpdateAlias(Alias Alias) + public Alias UpdateAlias(Alias alias) { - _db.Entry(Alias).State = EntityState.Modified; + _db.Entry(alias).State = EntityState.Modified; _db.SaveChanges(); _cache.Remove("aliases"); - return Alias; + return alias; } - public Alias GetAlias(int AliasId) + public Alias GetAlias(int aliasId) { - return _db.Alias.Find(AliasId); + return _db.Alias.Find(aliasId); } - public void DeleteAlias(int AliasId) + public void DeleteAlias(int aliasId) { - Alias alias = _db.Alias.Find(AliasId); + Alias alias = _db.Alias.Find(aliasId); _db.Alias.Remove(alias); _cache.Remove("aliases"); _db.SaveChanges(); diff --git a/Oqtane.Server/Repository/Context/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs index 405d43ed..e6aceed9 100644 --- a/Oqtane.Server/Repository/Context/DBContextBase.cs +++ b/Oqtane.Server/Repository/Context/DBContextBase.cs @@ -1,10 +1,10 @@ -using Microsoft.AspNetCore.Http; +using System; +using System.Linq; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using System; -using System.Linq; namespace Oqtane.Repository { @@ -53,7 +53,7 @@ namespace Oqtane.Repository foreach(var item in created) { - if (item.Entity is IAuditable entity) + if (item.Entity is IAuditable) { item.CurrentValues[nameof(IAuditable.CreatedBy)] = username; item.CurrentValues[nameof(IAuditable.CreatedOn)] = date; @@ -65,13 +65,13 @@ namespace Oqtane.Repository foreach (var item in modified) { - if (item.Entity is IAuditable entity) + if (item.Entity is IAuditable) { item.CurrentValues[nameof(IAuditable.ModifiedBy)] = username; item.CurrentValues[nameof(IAuditable.ModifiedOn)] = date; } - if (item.Entity is IDeletable deleted && item.State != EntityState.Added) + if (item.Entity is IDeletable && item.State != EntityState.Added) { if ((bool)item.CurrentValues[nameof(IDeletable.IsDeleted)] && !item.GetDatabaseValues().GetValue(nameof(IDeletable.IsDeleted))) diff --git a/Oqtane.Server/Repository/Context/MasterDBContext.cs b/Oqtane.Server/Repository/Context/MasterDBContext.cs index e120ff7d..65312819 100644 --- a/Oqtane.Server/Repository/Context/MasterDBContext.cs +++ b/Oqtane.Server/Repository/Context/MasterDBContext.cs @@ -1,8 +1,8 @@ -using Microsoft.AspNetCore.Http; +using System; +using System.Linq; +using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using System; -using System.Linq; namespace Oqtane.Repository { @@ -37,7 +37,7 @@ namespace Oqtane.Repository foreach (var item in created) { - if (item.Entity is IAuditable entity) + if (item.Entity is IAuditable) { item.CurrentValues[nameof(IAuditable.CreatedBy)] = username; item.CurrentValues[nameof(IAuditable.CreatedOn)] = date; @@ -49,7 +49,7 @@ namespace Oqtane.Repository foreach (var item in modified) { - if (item.Entity is IAuditable entity) + if (item.Entity is IAuditable) { item.CurrentValues[nameof(IAuditable.ModifiedBy)] = username; item.CurrentValues[nameof(IAuditable.ModifiedOn)] = date; diff --git a/Oqtane.Server/Repository/Context/TenantDBContext.cs b/Oqtane.Server/Repository/Context/TenantDBContext.cs index c90f8538..be085b20 100644 --- a/Oqtane.Server/Repository/Context/TenantDBContext.cs +++ b/Oqtane.Server/Repository/Context/TenantDBContext.cs @@ -21,7 +21,7 @@ namespace Oqtane.Repository public virtual DbSet Folder { get; set; } public virtual DbSet File { get; set; } - public TenantDBContext(ITenantResolver TenantResolver, IHttpContextAccessor accessor) : base(TenantResolver, accessor) + public TenantDBContext(ITenantResolver tenantResolver, IHttpContextAccessor accessor) : base(tenantResolver, accessor) { // DBContextBase handles multi-tenant database connections } diff --git a/Oqtane.Server/Repository/FileRepository.cs b/Oqtane.Server/Repository/FileRepository.cs index 5b67bb4a..fe5d3380 100644 --- a/Oqtane.Server/Repository/FileRepository.cs +++ b/Oqtane.Server/Repository/FileRepository.cs @@ -1,6 +1,6 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; namespace Oqtane.Repository @@ -16,34 +16,34 @@ namespace Oqtane.Repository _permissions = permissions; } - public IEnumerable GetFiles(int FolderId) + public IEnumerable GetFiles(int folderId) { - IEnumerable permissions = _permissions.GetPermissions("Folder", FolderId).ToList(); - IEnumerable files = _db.File.Where(item => item.FolderId == FolderId).Include(item => item.Folder); + IEnumerable permissions = _permissions.GetPermissions("Folder", folderId).ToList(); + IEnumerable files = _db.File.Where(item => item.FolderId == folderId).Include(item => item.Folder); foreach (File file in files) { - file.Folder.Permissions = _permissions.EncodePermissions(FolderId, permissions); + file.Folder.Permissions = _permissions.EncodePermissions(folderId, permissions); } return files; } - public File AddFile(File File) + public File AddFile(File file) { - _db.File.Add(File); + _db.File.Add(file); _db.SaveChanges(); - return File; + return file; } - public File UpdateFile(File File) + public File UpdateFile(File file) { - _db.Entry(File).State = EntityState.Modified; + _db.Entry(file).State = EntityState.Modified; _db.SaveChanges(); - return File; + return file; } - public File GetFile(int FileId) + public File GetFile(int fileId) { - File file = _db.File.Where(item => item.FileId == FileId).Include(item => item.Folder).FirstOrDefault(); + File file = _db.File.Where(item => item.FileId == fileId).Include(item => item.Folder).FirstOrDefault(); if (file != null) { IEnumerable permissions = _permissions.GetPermissions("Folder", file.FolderId).ToList(); @@ -52,10 +52,10 @@ namespace Oqtane.Repository return file; } - public void DeleteFile(int FileId) + public void DeleteFile(int fileId) { - File File = _db.File.Find(FileId); - _db.File.Remove(File); + File file = _db.File.Find(fileId); + _db.File.Remove(file); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Repository/FolderRepository.cs b/Oqtane.Server/Repository/FolderRepository.cs index f4a67d5a..b3d6f4e3 100644 --- a/Oqtane.Server/Repository/FolderRepository.cs +++ b/Oqtane.Server/Repository/FolderRepository.cs @@ -1,6 +1,6 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; namespace Oqtane.Repository @@ -16,10 +16,10 @@ namespace Oqtane.Repository _permissions = permissions; } - public IEnumerable GetFolders(int SiteId) + public IEnumerable GetFolders(int siteId) { - IEnumerable permissions = _permissions.GetPermissions(SiteId, "Folder").ToList(); - IEnumerable folders = _db.Folder.Where(item => item.SiteId == SiteId); + IEnumerable permissions = _permissions.GetPermissions(siteId, "Folder").ToList(); + IEnumerable folders = _db.Folder.Where(item => item.SiteId == siteId); foreach(Folder folder in folders) { folder.Permissions = _permissions.EncodePermissions(folder.FolderId, permissions); @@ -27,25 +27,25 @@ namespace Oqtane.Repository return folders; } - public Folder AddFolder(Folder Folder) + public Folder AddFolder(Folder folder) { - _db.Folder.Add(Folder); + _db.Folder.Add(folder); _db.SaveChanges(); - _permissions.UpdatePermissions(Folder.SiteId, "Folder", Folder.FolderId, Folder.Permissions); - return Folder; + _permissions.UpdatePermissions(folder.SiteId, "Folder", folder.FolderId, folder.Permissions); + return folder; } - public Folder UpdateFolder(Folder Folder) + public Folder UpdateFolder(Folder folder) { - _db.Entry(Folder).State = EntityState.Modified; + _db.Entry(folder).State = EntityState.Modified; _db.SaveChanges(); - _permissions.UpdatePermissions(Folder.SiteId, "Folder", Folder.FolderId, Folder.Permissions); - return Folder; + _permissions.UpdatePermissions(folder.SiteId, "Folder", folder.FolderId, folder.Permissions); + return folder; } - public Folder GetFolder(int FolderId) + public Folder GetFolder(int folderId) { - Folder folder = _db.Folder.Find(FolderId); + Folder folder = _db.Folder.Find(folderId); if (folder != null) { IEnumerable permissions = _permissions.GetPermissions("Folder", folder.FolderId).ToList(); @@ -54,9 +54,9 @@ namespace Oqtane.Repository return folder; } - public Folder GetFolder(int SiteId, string Path) + public Folder GetFolder(int siteId, string path) { - Folder folder = _db.Folder.Where(item => item.SiteId == SiteId && item.Path == Path).FirstOrDefault(); + Folder folder = _db.Folder.Where(item => item.SiteId == siteId && item.Path == path).FirstOrDefault(); if (folder != null) { IEnumerable permissions = _permissions.GetPermissions("Folder", folder.FolderId).ToList(); @@ -65,11 +65,11 @@ namespace Oqtane.Repository return folder; } - public void DeleteFolder(int FolderId) + public void DeleteFolder(int folderId) { - Folder Folder = _db.Folder.Find(FolderId); - _permissions.DeletePermissions(Folder.SiteId, "Folder", FolderId); - _db.Folder.Remove(Folder); + Folder folder = _db.Folder.Find(folderId); + _permissions.DeletePermissions(folder.SiteId, "Folder", folderId); + _db.Folder.Remove(folder); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs b/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs index 3ab61f13..4f258307 100644 --- a/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs @@ -6,9 +6,9 @@ namespace Oqtane.Repository public interface IAliasRepository { IEnumerable GetAliases(); - Alias AddAlias(Alias Alias); - Alias UpdateAlias(Alias Alias); - Alias GetAlias(int AliasId); - void DeleteAlias(int AliasId); + Alias AddAlias(Alias alias); + Alias UpdateAlias(Alias alias); + Alias GetAlias(int aliasId); + void DeleteAlias(int aliasId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IFileRepository.cs b/Oqtane.Server/Repository/Interfaces/IFileRepository.cs index 3122898b..19715187 100644 --- a/Oqtane.Server/Repository/Interfaces/IFileRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IFileRepository.cs @@ -5,10 +5,10 @@ namespace Oqtane.Repository { public interface IFileRepository { - IEnumerable GetFiles(int FolderId); - File AddFile(File File); - File UpdateFile(File File); - File GetFile(int FileId); - void DeleteFile(int FileId); + IEnumerable GetFiles(int folderId); + File AddFile(File file); + File UpdateFile(File file); + File GetFile(int fileId); + void DeleteFile(int fileId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs index 86ac797f..65256184 100644 --- a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs @@ -5,11 +5,11 @@ namespace Oqtane.Repository { public interface IFolderRepository { - IEnumerable GetFolders(int SiteId); - Folder AddFolder(Folder Folder); - Folder UpdateFolder(Folder Folder); - Folder GetFolder(int FolderId); - Folder GetFolder(int SiteId, string Path); - void DeleteFolder(int FolderId); + IEnumerable GetFolders(int siteId); + Folder AddFolder(Folder folder); + Folder UpdateFolder(Folder folder); + Folder GetFolder(int folderId); + Folder GetFolder(int siteId, string path); + void DeleteFolder(int folderId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs b/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs index f99de0d2..d5858da5 100644 --- a/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs @@ -6,9 +6,9 @@ namespace Oqtane.Repository public interface IJobLogRepository { IEnumerable GetJobLogs(); - JobLog AddJobLog(JobLog JobLog); - JobLog UpdateJobLog(JobLog JobLog); - JobLog GetJobLog(int JobLogId); - void DeleteJobLog(int JobLogId); + JobLog AddJobLog(JobLog jobLog); + JobLog UpdateJobLog(JobLog jobLog); + JobLog GetJobLog(int jobLogId); + void DeleteJobLog(int jobLogId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IJobRepository.cs b/Oqtane.Server/Repository/Interfaces/IJobRepository.cs index 8683009f..12be85c1 100644 --- a/Oqtane.Server/Repository/Interfaces/IJobRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IJobRepository.cs @@ -6,9 +6,9 @@ namespace Oqtane.Repository public interface IJobRepository { IEnumerable GetJobs(); - Job AddJob(Job Job); - Job UpdateJob(Job Job); - Job GetJob(int JobId); - void DeleteJob(int JobId); + Job AddJob(Job job); + Job UpdateJob(Job job); + Job GetJob(int jobId); + void DeleteJob(int jobId); } } diff --git a/Oqtane.Server/Repository/Interfaces/ILogRepository.cs b/Oqtane.Server/Repository/Interfaces/ILogRepository.cs index e4a82e09..27752a03 100644 --- a/Oqtane.Server/Repository/Interfaces/ILogRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ILogRepository.cs @@ -1,12 +1,12 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; +using Oqtane.Models; namespace Oqtane.Repository { public interface ILogRepository { - IEnumerable GetLogs(int SiteId, string Level, string Function, int Rows); - Log GetLog(int LogId); - void AddLog(Log Log); + IEnumerable GetLogs(int siteId, string level, string function, int rows); + Log GetLog(int logId); + void AddLog(Log log); } } diff --git a/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs index c629d76f..32c59549 100644 --- a/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs @@ -5,9 +5,9 @@ namespace Oqtane.Repository { public interface IModuleDefinitionRepository { - IEnumerable GetModuleDefinitions(int SideId); - ModuleDefinition GetModuleDefinition(int ModuleDefinitionId, int SideId); - void UpdateModuleDefinition(ModuleDefinition ModuleDefinition); - void DeleteModuleDefinition(int ModuleDefinitionId, int SiteId); + IEnumerable GetModuleDefinitions(int sideId); + ModuleDefinition GetModuleDefinition(int moduleDefinitionId, int sideId); + void UpdateModuleDefinition(ModuleDefinition moduleDefinition); + void DeleteModuleDefinition(int moduleDefinitionId, int siteId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs index 5dd23858..2ef4185d 100644 --- a/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs @@ -5,12 +5,12 @@ namespace Oqtane.Repository { public interface IModuleRepository { - IEnumerable GetModules(int SiteId); - Module AddModule(Module Module); - Module UpdateModule(Module Module); - Module GetModule(int ModuleId); - void DeleteModule(int ModuleId); - string ExportModule(int ModuleId); - bool ImportModule(int ModuleId, string Content); + IEnumerable GetModules(int siteId); + Module AddModule(Module module); + Module UpdateModule(Module module); + Module GetModule(int moduleId); + void DeleteModule(int moduleId); + string ExportModule(int moduleId); + bool ImportModule(int moduleId, string content); } } diff --git a/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs b/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs index 9b1a6a56..ef53bc3a 100644 --- a/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs @@ -5,10 +5,10 @@ namespace Oqtane.Repository { public interface INotificationRepository { - IEnumerable GetNotifications(int SiteId, int FromUserId, int ToUserId); - Notification AddNotification(Notification Notification); - Notification UpdateNotification(Notification Notification); - Notification GetNotification(int NotificationId); - void DeleteNotification(int NotificationId); + IEnumerable GetNotifications(int siteId, int fromUserId, int toUserId); + Notification AddNotification(Notification notification); + Notification UpdateNotification(Notification notification); + Notification GetNotification(int notificationId); + void DeleteNotification(int notificationId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs b/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs index 8e275bcb..181fb629 100644 --- a/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs @@ -5,12 +5,12 @@ namespace Oqtane.Repository { public interface IPageModuleRepository { - IEnumerable GetPageModules(int SiteId); - IEnumerable GetPageModules(int PageId, string Pane); - PageModule AddPageModule(PageModule PageModule); - PageModule UpdatePageModule(PageModule PageModule); - PageModule GetPageModule(int PageModuleId); - PageModule GetPageModule(int PageId, int ModuleId); - void DeletePageModule(int PageModuleId); + IEnumerable GetPageModules(int siteId); + IEnumerable GetPageModules(int pageId, string pane); + PageModule AddPageModule(PageModule pageModule); + PageModule UpdatePageModule(PageModule pageModule); + PageModule GetPageModule(int pageModuleId); + PageModule GetPageModule(int pageId, int moduleId); + void DeletePageModule(int pageModuleId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IPageRepository.cs b/Oqtane.Server/Repository/Interfaces/IPageRepository.cs index 9f98c5ac..6db92dba 100644 --- a/Oqtane.Server/Repository/Interfaces/IPageRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPageRepository.cs @@ -5,12 +5,12 @@ namespace Oqtane.Repository { public interface IPageRepository { - IEnumerable GetPages(int SiteId); - Page AddPage(Page Page); - Page UpdatePage(Page Page); - Page GetPage(int PageId); - Page GetPage(int PageId, int UserId); - Page GetPage(string Path, int SiteId); - void DeletePage(int PageId); + IEnumerable GetPages(int siteId); + Page AddPage(Page page); + Page UpdatePage(Page page); + Page GetPage(int pageId); + Page GetPage(int pageId, int userId); + Page GetPage(string path, int siteId); + void DeletePage(int pageId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs index 50d53cb0..2fe96254 100644 --- a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs @@ -5,16 +5,16 @@ namespace Oqtane.Repository { public interface IPermissionRepository { - IEnumerable GetPermissions(int SiteId, string EntityName); - IEnumerable GetPermissions(string EntityName, int EntityId); - IEnumerable GetPermissions(string EntityName, int EntityId, string PermissionName); - Permission AddPermission(Permission Permission); - Permission UpdatePermission(Permission Permission); - void UpdatePermissions(int SiteId, string EntityName, int EntityId, string Permissions); - Permission GetPermission(int PermissionId); - void DeletePermission(int PermissionId); - void DeletePermissions(int SiteId, string EntityName, int EntityId); - string EncodePermissions(int EntityId, IEnumerable Permissions); - IEnumerable DecodePermissions(string Permissions, int SiteId, string EntityName, int EntityId); + IEnumerable GetPermissions(int siteId, string entityName); + IEnumerable GetPermissions(string entityName, int entityId); + IEnumerable GetPermissions(string entityName, int entityId, string permissionName); + Permission AddPermission(Permission permission); + Permission UpdatePermission(Permission permission); + void UpdatePermissions(int siteId, string entityName, int entityId, string permissionStrings); + Permission GetPermission(int permissionId); + void DeletePermission(int permissionId); + void DeletePermissions(int siteId, string entityName, int entityId); + string EncodePermissions(int entityId, IEnumerable permissionList); + IEnumerable DecodePermissions(string permissions, int siteId, string entityName, int entityId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs b/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs index e5300b59..c9b4f34b 100644 --- a/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs @@ -5,10 +5,10 @@ namespace Oqtane.Repository { public interface IProfileRepository { - IEnumerable GetProfiles(int SiteId); - Profile AddProfile(Profile Profile); - Profile UpdateProfile(Profile Profile); - Profile GetProfile(int ProfileId); - void DeleteProfile(int ProfileId); + IEnumerable GetProfiles(int siteId); + Profile AddProfile(Profile profile); + Profile UpdateProfile(Profile profile); + Profile GetProfile(int profileId); + void DeleteProfile(int profileId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs b/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs index 40541100..35f9315f 100644 --- a/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs @@ -5,11 +5,11 @@ namespace Oqtane.Repository { public interface IRoleRepository { - IEnumerable GetRoles(int SiteId); - IEnumerable GetRoles(int SiteId, bool IncludeGlobalRoles); - Role AddRole(Role Role); - Role UpdateRole(Role Role); - Role GetRole(int RoleId); - void DeleteRole(int RoleId); + IEnumerable GetRoles(int siteId); + IEnumerable GetRoles(int siteId, bool includeGlobalRoles); + Role AddRole(Role role); + Role UpdateRole(Role role); + Role GetRole(int roleId); + void DeleteRole(int roleId); } } diff --git a/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs b/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs index 0ffad860..976519fb 100644 --- a/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs @@ -5,10 +5,10 @@ namespace Oqtane.Repository { public interface ISettingRepository { - IEnumerable GetSettings(string EntityName, int EntityId); - Setting AddSetting(Setting Setting); - Setting UpdateSetting(Setting Setting); - Setting GetSetting(int SettingId); - void DeleteSetting(int SettingId); + IEnumerable GetSettings(string entityName, int entityId); + Setting AddSetting(Setting setting); + Setting UpdateSetting(Setting setting); + Setting GetSetting(int settingId); + void DeleteSetting(int settingId); } } diff --git a/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs b/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs index 65cf9aff..5e05dccf 100644 --- a/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs @@ -6,9 +6,9 @@ namespace Oqtane.Repository public interface ISiteRepository { IEnumerable GetSites(); - Site AddSite(Site Site); - Site UpdateSite(Site Site); - Site GetSite(int SiteId); - void DeleteSite(int SiteId); + Site AddSite(Site site); + Site UpdateSite(Site site); + Site GetSite(int siteId); + void DeleteSite(int siteId); } } diff --git a/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs b/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs index 17a8b1d0..5993eb63 100644 --- a/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs @@ -1,14 +1,14 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; +using Oqtane.Models; namespace Oqtane.Repository { public interface ITenantRepository { IEnumerable GetTenants(); - Tenant AddTenant(Tenant Tenant); - Tenant UpdateTenant(Tenant Tenant); - Tenant GetTenant(int TenantId); - void DeleteTenant(int TenantId); + Tenant AddTenant(Tenant tenant); + Tenant UpdateTenant(Tenant tenant); + Tenant GetTenant(int tenantId); + void DeleteTenant(int tenantId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IUserRepository.cs b/Oqtane.Server/Repository/Interfaces/IUserRepository.cs index 63757b49..be646e56 100644 --- a/Oqtane.Server/Repository/Interfaces/IUserRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IUserRepository.cs @@ -6,10 +6,10 @@ namespace Oqtane.Repository public interface IUserRepository { IEnumerable GetUsers(); - User AddUser(User User); - User UpdateUser(User User); - User GetUser(int UserId); - User GetUser(string Username); - void DeleteUser(int UserId); + User AddUser(User user); + User UpdateUser(User user); + User GetUser(int userId); + User GetUser(string username); + void DeleteUser(int userId); } } diff --git a/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs b/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs index 14cb81a4..c46a0e50 100644 --- a/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs @@ -5,11 +5,11 @@ namespace Oqtane.Repository { public interface IUserRoleRepository { - IEnumerable GetUserRoles(int SiteId); - IEnumerable GetUserRoles(int UserId, int SiteId); - UserRole AddUserRole(UserRole UserRole); - UserRole UpdateUserRole(UserRole UserRole); - UserRole GetUserRole(int UserRoleId); - void DeleteUserRole(int UserRoleId); + IEnumerable GetUserRoles(int siteId); + IEnumerable GetUserRoles(int userId, int siteId); + UserRole AddUserRole(UserRole userRole); + UserRole UpdateUserRole(UserRole userRole); + UserRole GetUserRole(int userRoleId); + void DeleteUserRole(int userRoleId); } } diff --git a/Oqtane.Server/Repository/JobLogRepository.cs b/Oqtane.Server/Repository/JobLogRepository.cs index cdfca1c0..440a483d 100644 --- a/Oqtane.Server/Repository/JobLogRepository.cs +++ b/Oqtane.Server/Repository/JobLogRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -using Oqtane.Models; using Microsoft.EntityFrameworkCore; +using Oqtane.Models; namespace Oqtane.Repository { @@ -21,30 +21,30 @@ namespace Oqtane.Repository .ToList(); } - public JobLog AddJobLog(JobLog JobLog) + public JobLog AddJobLog(JobLog jobLog) { - _db.JobLog.Add(JobLog); + _db.JobLog.Add(jobLog); _db.SaveChanges(); - return JobLog; + return jobLog; } - public JobLog UpdateJobLog(JobLog JobLog) + public JobLog UpdateJobLog(JobLog jobLog) { - _db.Entry(JobLog).State = EntityState.Modified; + _db.Entry(jobLog).State = EntityState.Modified; _db.SaveChanges(); - return JobLog; + return jobLog; } - public JobLog GetJobLog(int JobLogId) + public JobLog GetJobLog(int jobLogId) { return _db.JobLog.Include(item => item.Job) // eager load job - .SingleOrDefault(item => item.JobLogId == JobLogId); + .SingleOrDefault(item => item.JobLogId == jobLogId); } - public void DeleteJobLog(int JobLogId) + public void DeleteJobLog(int jobLogId) { - JobLog Joblog = _db.JobLog.Find(JobLogId); - _db.JobLog.Remove(Joblog); + JobLog joblog = _db.JobLog.Find(jobLogId); + _db.JobLog.Remove(joblog); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Repository/JobRepository.cs b/Oqtane.Server/Repository/JobRepository.cs index 2d146142..2ea8b7d1 100644 --- a/Oqtane.Server/Repository/JobRepository.cs +++ b/Oqtane.Server/Repository/JobRepository.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; -using Oqtane.Models; using Microsoft.EntityFrameworkCore; -using System; using Microsoft.Extensions.Caching.Memory; +using Oqtane.Models; namespace Oqtane.Repository { @@ -27,29 +27,29 @@ namespace Oqtane.Repository }); } - public Job AddJob(Job Job) + public Job AddJob(Job job) { - _db.Job.Add(Job); + _db.Job.Add(job); _db.SaveChanges(); - return Job; + return job; } - public Job UpdateJob(Job Job) + public Job UpdateJob(Job job) { - _db.Entry(Job).State = EntityState.Modified; + _db.Entry(job).State = EntityState.Modified; _db.SaveChanges(); - return Job; + return job; } - public Job GetJob(int JobId) + public Job GetJob(int jobId) { - return _db.Job.Find(JobId); + return _db.Job.Find(jobId); } - public void DeleteJob(int JobId) + public void DeleteJob(int jobId) { - Job Job = _db.Job.Find(JobId); - _db.Job.Remove(Job); + Job job = _db.Job.Find(jobId); + _db.Job.Remove(job); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Repository/LogRepository.cs b/Oqtane.Server/Repository/LogRepository.cs index b01bc069..1bcbc2be 100644 --- a/Oqtane.Server/Repository/LogRepository.cs +++ b/Oqtane.Server/Repository/LogRepository.cs @@ -1,5 +1,4 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Oqtane.Models; @@ -14,44 +13,38 @@ namespace Oqtane.Repository _db = context; } - public IEnumerable GetLogs(int SiteId, string Level, string Function, int Rows) + public IEnumerable GetLogs(int siteId, string level, string function, int rows) { - if (Level == null) + if (level == null) { - if (Function == null) + if (function == null) { - return _db.Log.Where(item => item.SiteId == SiteId). - OrderByDescending(item => item.LogDate).Take(Rows); - } - else - { - return _db.Log.Where(item => item.SiteId == SiteId && item.Function == Function). - OrderByDescending(item => item.LogDate).Take(Rows); + return _db.Log.Where(item => item.SiteId == siteId). + OrderByDescending(item => item.LogDate).Take(rows); } + + return _db.Log.Where(item => item.SiteId == siteId && item.Function == function). + OrderByDescending(item => item.LogDate).Take(rows); } - else + + if (function == null) { - if (Function == null) - { - return _db.Log.Where(item => item.SiteId == SiteId && item.Level == Level) - .OrderByDescending(item => item.LogDate).Take(Rows); - } - else - { - return _db.Log.Where(item => item.SiteId == SiteId && item.Level == Level && item.Function == Function) - .OrderByDescending(item => item.LogDate).Take(Rows); - } + return _db.Log.Where(item => item.SiteId == siteId && item.Level == level) + .OrderByDescending(item => item.LogDate).Take(rows); } + + return _db.Log.Where(item => item.SiteId == siteId && item.Level == level && item.Function == function) + .OrderByDescending(item => item.LogDate).Take(rows); } - public Log GetLog(int LogId) + public Log GetLog(int logId) { - return _db.Log.Find(LogId); + return _db.Log.Find(logId); } - public void AddLog(Log Log) + public void AddLog(Log log) { - _db.Log.Add(Log); + _db.Log.Add(log); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index c13b86d2..509a7bcf 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -1,11 +1,10 @@ -using Microsoft.EntityFrameworkCore; +using System; using System.Collections.Generic; using System.Linq; -using Oqtane.Models; using System.Reflection; -using System; -using Oqtane.Modules; using Microsoft.Extensions.Caching.Memory; +using Oqtane.Models; +using Oqtane.Modules; using Oqtane.Shared; namespace Oqtane.Repository @@ -23,51 +22,51 @@ namespace Oqtane.Repository _permissions = permissions; } - public IEnumerable GetModuleDefinitions(int SiteId) + public IEnumerable GetModuleDefinitions(int siteId) { - return LoadModuleDefinitions(SiteId); + return LoadModuleDefinitions(siteId); } - public ModuleDefinition GetModuleDefinition(int ModuleDefinitionId, int SiteId) + public ModuleDefinition GetModuleDefinition(int moduleDefinitionId, int siteId) { - List moduledefinitions = LoadModuleDefinitions(SiteId); - return moduledefinitions.Find(item => item.ModuleDefinitionId == ModuleDefinitionId); + List moduledefinitions = LoadModuleDefinitions(siteId); + return moduledefinitions.Find(item => item.ModuleDefinitionId == moduleDefinitionId); } - public void UpdateModuleDefinition(ModuleDefinition ModuleDefinition) + public void UpdateModuleDefinition(ModuleDefinition moduleDefinition) { - _permissions.UpdatePermissions(ModuleDefinition.SiteId, "ModuleDefinition", ModuleDefinition.ModuleDefinitionId, ModuleDefinition.Permissions); + _permissions.UpdatePermissions(moduleDefinition.SiteId, "ModuleDefinition", moduleDefinition.ModuleDefinitionId, moduleDefinition.Permissions); _cache.Remove("moduledefinitions"); } - public void DeleteModuleDefinition(int ModuleDefinitionId, int SiteId) + public void DeleteModuleDefinition(int moduleDefinitionId, int siteId) { - ModuleDefinition ModuleDefinition = _db.ModuleDefinition.Find(ModuleDefinitionId); - _permissions.DeletePermissions(SiteId, "ModuleDefinition", ModuleDefinitionId); - _db.ModuleDefinition.Remove(ModuleDefinition); + ModuleDefinition moduleDefinition = _db.ModuleDefinition.Find(moduleDefinitionId); + _permissions.DeletePermissions(siteId, "ModuleDefinition", moduleDefinitionId); + _db.ModuleDefinition.Remove(moduleDefinition); _db.SaveChanges(); _cache.Remove("moduledefinitions"); } - public List LoadModuleDefinitions(int SiteId) + public List LoadModuleDefinitions(int siteId) { - List ModuleDefinitions; + List moduleDefinitions; // get run-time module definitions - ModuleDefinitions = _cache.GetOrCreate("moduledefinitions", entry => + moduleDefinitions = _cache.GetOrCreate("moduledefinitions", entry => { entry.SlidingExpiration = TimeSpan.FromMinutes(30); return LoadModuleDefinitionsFromAssemblies(); }); // get module defintion permissions for site - List permissions = _permissions.GetPermissions(SiteId, "ModuleDefinition").ToList(); + List permissions = _permissions.GetPermissions(siteId, "ModuleDefinition").ToList(); // get module definitions in database List moduledefs = _db.ModuleDefinition.ToList(); // sync run-time module definitions with database - foreach (ModuleDefinition moduledefinition in ModuleDefinitions) + foreach (ModuleDefinition moduledefinition in moduleDefinitions) { ModuleDefinition moduledef = moduledefs.Where(item => item.ModuleDefinitionName == moduledefinition.ModuleDefinitionName).FirstOrDefault(); if (moduledef == null) @@ -76,14 +75,14 @@ namespace Oqtane.Repository moduledef = new ModuleDefinition { ModuleDefinitionName = moduledefinition.ModuleDefinitionName }; _db.ModuleDefinition.Add(moduledef); _db.SaveChanges(); - _permissions.UpdatePermissions(SiteId, "ModuleDefinition", moduledef.ModuleDefinitionId, moduledefinition.Permissions); + _permissions.UpdatePermissions(siteId, "ModuleDefinition", moduledef.ModuleDefinitionId, moduledefinition.Permissions); } else { // existing module definition if (permissions.Count == 0) { - _permissions.UpdatePermissions(SiteId, "ModuleDefinition", moduledef.ModuleDefinitionId, moduledefinition.Permissions); + _permissions.UpdatePermissions(siteId, "ModuleDefinition", moduledef.ModuleDefinitionId, moduledefinition.Permissions); } else { @@ -93,7 +92,7 @@ namespace Oqtane.Repository moduledefs.Remove(moduledef); } moduledefinition.ModuleDefinitionId = moduledef.ModuleDefinitionId; - moduledefinition.SiteId = SiteId; + moduledefinition.SiteId = siteId; moduledefinition.CreatedBy = moduledef.CreatedBy; moduledefinition.CreatedOn = moduledef.CreatedOn; moduledefinition.ModifiedBy = moduledef.ModifiedBy; @@ -103,24 +102,24 @@ namespace Oqtane.Repository // any remaining module definitions are orphans foreach (ModuleDefinition moduledefinition in moduledefs) { - _permissions.DeletePermissions(SiteId, "ModuleDefinition", moduledefinition.ModuleDefinitionId); + _permissions.DeletePermissions(siteId, "ModuleDefinition", moduledefinition.ModuleDefinitionId); _db.ModuleDefinition.Remove(moduledefinition); // delete } - return ModuleDefinitions; + return moduleDefinitions; } private List LoadModuleDefinitionsFromAssemblies() { - List ModuleDefinitions = new List(); + List moduleDefinitions = new List(); // iterate through Oqtane module assemblies Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray(); foreach (Assembly assembly in assemblies) { - ModuleDefinitions = LoadModuleDefinitionsFromAssembly(ModuleDefinitions, assembly); + moduleDefinitions = LoadModuleDefinitionsFromAssembly(moduleDefinitions, assembly); } - return ModuleDefinitions; + return moduleDefinitions; } private List LoadModuleDefinitionsFromAssembly(List moduledefinitions, Assembly assembly) @@ -134,25 +133,25 @@ namespace Oqtane.Repository string[] typename = modulecontroltype.AssemblyQualifiedName.Split(',').Select(item => item.Trim()).ToList().ToArray(); string[] segments = typename[0].Split('.'); Array.Resize(ref segments, segments.Length - 1); - string ModuleType = string.Join(".", segments); - string QualifiedModuleType = ModuleType + ", " + typename[1]; + string moduleType = string.Join(".", segments); + string qualifiedModuleType = moduleType + ", " + typename[1]; - int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == QualifiedModuleType); + int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType); if (index == -1) { - /// determine if this module implements IModule - Type moduletype = assembly.GetTypes() - .Where(item => item.Namespace != null) - .Where(item => item.Namespace.StartsWith(ModuleType)) - .Where(item => item.GetInterfaces().Contains(typeof(IModule))) - .FirstOrDefault(); + // determine if this module implements IModule + Type moduletype = assembly + .GetTypes() + .Where(item => item.Namespace != null) + .Where(item => item.Namespace.StartsWith(moduleType)) + .FirstOrDefault(item => item.GetInterfaces().Contains(typeof(IModule))); if (moduletype != null) { var moduleobject = Activator.CreateInstance(moduletype); Dictionary properties = (Dictionary)moduletype.GetProperty("Properties").GetValue(moduleobject); moduledefinition = new ModuleDefinition { - ModuleDefinitionName = QualifiedModuleType, + ModuleDefinitionName = qualifiedModuleType, Name = GetProperty(properties, "Name"), Description = GetProperty(properties, "Description"), Categories = GetProperty(properties, "Categories"), @@ -164,7 +163,7 @@ namespace Oqtane.Repository Dependencies = GetProperty(properties, "Dependencies"), PermissionNames = GetProperty(properties, "PermissionNames"), ServerAssemblyName = GetProperty(properties, "ServerAssemblyName"), - ControlTypeTemplate = ModuleType + "." + Constants.ActionToken + ", " + typename[1], + ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1], ControlTypeRoutes = "", AssemblyName = assembly.FullName.Split(",")[0], Permissions = "" @@ -174,10 +173,10 @@ namespace Oqtane.Repository { moduledefinition = new ModuleDefinition { - ModuleDefinitionName = QualifiedModuleType, - Name = ModuleType.Substring(ModuleType.LastIndexOf(".") + 1), - Description = ModuleType.Substring(ModuleType.LastIndexOf(".") + 1), - Categories = ((QualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : ""), + ModuleDefinitionName = qualifiedModuleType, + Name = moduleType.Substring(moduleType.LastIndexOf(".") + 1), + Description = moduleType.Substring(moduleType.LastIndexOf(".") + 1), + Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : ""), Version = new Version(1, 0, 0).ToString(), Owner = "", Url = "", @@ -186,7 +185,7 @@ namespace Oqtane.Repository Dependencies = "", PermissionNames = "", ServerAssemblyName = "", - ControlTypeTemplate = ModuleType + "." + Constants.ActionToken + ", " + typename[1], + ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1], ControlTypeRoutes = "", AssemblyName = assembly.FullName.Split(",")[0], Permissions = "" @@ -202,7 +201,7 @@ namespace Oqtane.Repository moduledefinition.Permissions = "[{\"PermissionName\":\"Utilize\",\"Permissions\":\"" + Constants.AdminRole + ";" + Constants.RegisteredRole + "\"}]"; } moduledefinitions.Add(moduledefinition); - index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == QualifiedModuleType); + index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType); } moduledefinition = moduledefinitions[index]; // actions @@ -222,14 +221,14 @@ namespace Oqtane.Repository return moduledefinitions; } - private string GetProperty(Dictionary Properties, string Key) + private string GetProperty(Dictionary properties, string key) { - string Value = ""; - if (Properties.ContainsKey(Key)) + string value = ""; + if (properties.ContainsKey(key)) { - Value = Properties[Key]; + value = properties[key]; } - return Value; + return value; } } } diff --git a/Oqtane.Server/Repository/ModuleRepository.cs b/Oqtane.Server/Repository/ModuleRepository.cs index 7abd0c34..76644286 100644 --- a/Oqtane.Server/Repository/ModuleRepository.cs +++ b/Oqtane.Server/Repository/ModuleRepository.cs @@ -1,12 +1,13 @@ -using Microsoft.EntityFrameworkCore; +using System; using System.Collections.Generic; using System.Linq; -using Oqtane.Models; using System.Reflection; -using System; -using Oqtane.Modules; -using Microsoft.Extensions.DependencyInjection; using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Oqtane.Models; +using Oqtane.Modules; +using Module = Oqtane.Models.Module; namespace Oqtane.Repository { @@ -25,30 +26,30 @@ namespace Oqtane.Repository _serviceProvider = serviceProvider; } - public IEnumerable GetModules(int SiteId) + public IEnumerable GetModules(int siteId) { - return _db.Module.Where(item => item.SiteId == SiteId).ToList(); + return _db.Module.Where(item => item.SiteId == siteId).ToList(); } - public Models.Module AddModule(Models.Module Module) + public Module AddModule(Module module) { - _db.Module.Add(Module); + _db.Module.Add(module); _db.SaveChanges(); - _permissions.UpdatePermissions(Module.SiteId, "Module", Module.ModuleId, Module.Permissions); - return Module; + _permissions.UpdatePermissions(module.SiteId, "Module", module.ModuleId, module.Permissions); + return module; } - public Models.Module UpdateModule(Models.Module Module) + public Module UpdateModule(Module module) { - _db.Entry(Module).State = EntityState.Modified; + _db.Entry(module).State = EntityState.Modified; _db.SaveChanges(); - _permissions.UpdatePermissions(Module.SiteId, "Module", Module.ModuleId, Module.Permissions); - return Module; + _permissions.UpdatePermissions(module.SiteId, "Module", module.ModuleId, module.Permissions); + return module; } - public Models.Module GetModule(int ModuleId) + public Module GetModule(int moduleId) { - Models.Module module = _db.Module.Find(ModuleId); + Module module = _db.Module.Find(moduleId); if (module != null) { List permissions = _permissions.GetPermissions("Module", module.ModuleId).ToList(); @@ -57,20 +58,20 @@ namespace Oqtane.Repository return module; } - public void DeleteModule(int ModuleId) + public void DeleteModule(int moduleId) { - Models.Module Module = _db.Module.Find(ModuleId); - _permissions.DeletePermissions(Module.SiteId, "Module", ModuleId); - _db.Module.Remove(Module); + Module module = _db.Module.Find(moduleId); + _permissions.DeletePermissions(module.SiteId, "Module", moduleId); + _db.Module.Remove(module); _db.SaveChanges(); } - public string ExportModule(int ModuleId) + public string ExportModule(int moduleId) { string content = ""; try { - Models.Module module = GetModule(ModuleId); + Module module = GetModule(moduleId); if (module != null) { List moduledefinitions = _moduleDefinitions.GetModuleDefinitions(module.SiteId).ToList(); @@ -110,19 +111,19 @@ namespace Oqtane.Repository return content; } - public bool ImportModule(int ModuleId, string Content) + public bool ImportModule(int moduleId, string content) { bool success = false; try { - Models.Module module = GetModule(ModuleId); + Module module = GetModule(moduleId); if (module != null) { List moduledefinitions = _moduleDefinitions.GetModuleDefinitions(module.SiteId).ToList(); ModuleDefinition moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault(); if (moduledefinition != null) { - ModuleContent modulecontent = JsonSerializer.Deserialize(Content); + ModuleContent modulecontent = JsonSerializer.Deserialize(content); if (modulecontent.ModuleDefinitionName == moduledefinition.ModuleDefinitionName) { if (moduledefinition.ServerAssemblyName != "") diff --git a/Oqtane.Server/Repository/NotificationRepository.cs b/Oqtane.Server/Repository/NotificationRepository.cs index dbc00564..8f34ff50 100644 --- a/Oqtane.Server/Repository/NotificationRepository.cs +++ b/Oqtane.Server/Repository/NotificationRepository.cs @@ -1,6 +1,6 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; namespace Oqtane.Repository @@ -14,52 +14,50 @@ namespace Oqtane.Repository _db = context; } - public IEnumerable GetNotifications(int SiteId, int FromUserId, int ToUserId) + public IEnumerable GetNotifications(int siteId, int fromUserId, int toUserId) { - if (ToUserId == -1 && FromUserId == -1) + if (toUserId == -1 && fromUserId == -1) { return _db.Notification - .Where(item => item.SiteId == SiteId) + .Where(item => item.SiteId == siteId) .Where(item => item.IsDelivered == false) .Include(item => item.FromUser) .Include(item => item.ToUser) .ToList(); } - else - { - return _db.Notification - .Where(item => item.SiteId == SiteId) - .Where(item => item.ToUserId == ToUserId || ToUserId == -1) - .Where(item => item.FromUserId == FromUserId || FromUserId == -1) - .Include(item => item.FromUser) - .Include(item => item.ToUser) - .ToList(); - } + + return _db.Notification + .Where(item => item.SiteId == siteId) + .Where(item => item.ToUserId == toUserId || toUserId == -1) + .Where(item => item.FromUserId == fromUserId || fromUserId == -1) + .Include(item => item.FromUser) + .Include(item => item.ToUser) + .ToList(); } - public Notification AddNotification(Notification Notification) + public Notification AddNotification(Notification notification) { - _db.Notification.Add(Notification); + _db.Notification.Add(notification); _db.SaveChanges(); - return Notification; + return notification; } - public Notification UpdateNotification(Notification Notification) + public Notification UpdateNotification(Notification notification) { - _db.Entry(Notification).State = EntityState.Modified; + _db.Entry(notification).State = EntityState.Modified; _db.SaveChanges(); - return Notification; + return notification; } - public Notification GetNotification(int NotificationId) + public Notification GetNotification(int notificationId) { - return _db.Notification.Find(NotificationId); + return _db.Notification.Find(notificationId); } - public void DeleteNotification(int NotificationId) + public void DeleteNotification(int notificationId) { - Notification Notification = _db.Notification.Find(NotificationId); - _db.Notification.Remove(Notification); + Notification notification = _db.Notification.Find(notificationId); + _db.Notification.Remove(notification); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Repository/PageModuleRepository.cs b/Oqtane.Server/Repository/PageModuleRepository.cs index 8493c7f1..70f7c201 100644 --- a/Oqtane.Server/Repository/PageModuleRepository.cs +++ b/Oqtane.Server/Repository/PageModuleRepository.cs @@ -1,6 +1,6 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; namespace Oqtane.Repository @@ -16,11 +16,11 @@ namespace Oqtane.Repository _permissions = permissions; } - public IEnumerable GetPageModules(int SiteId) + public IEnumerable GetPageModules(int siteId) { IEnumerable pagemodules = _db.PageModule .Include(item => item.Module) // eager load modules - .Where(item => item.Module.SiteId == SiteId); + .Where(item => item.Module.SiteId == siteId); if (pagemodules != null && pagemodules.Any()) { IEnumerable permissions = _permissions.GetPermissions(pagemodules.FirstOrDefault().Module.SiteId, "Module").ToList(); @@ -32,14 +32,14 @@ namespace Oqtane.Repository return pagemodules; } - public IEnumerable GetPageModules(int PageId, string Pane) + public IEnumerable GetPageModules(int pageId, string pane) { IEnumerable pagemodules = _db.PageModule .Include(item => item.Module) // eager load modules - .Where(item => item.PageId == PageId); - if (Pane != "" && pagemodules != null && pagemodules.Any()) + .Where(item => item.PageId == pageId); + if (pane != "" && pagemodules != null && pagemodules.Any()) { - pagemodules = pagemodules.Where(item => item.Pane == Pane); + pagemodules = pagemodules.Where(item => item.Pane == pane); } if (pagemodules != null && pagemodules.Any()) { @@ -52,24 +52,24 @@ namespace Oqtane.Repository return pagemodules; } - public PageModule AddPageModule(PageModule PageModule) + public PageModule AddPageModule(PageModule pageModule) { - _db.PageModule.Add(PageModule); + _db.PageModule.Add(pageModule); _db.SaveChanges(); - return PageModule; + return pageModule; } - public PageModule UpdatePageModule(PageModule PageModule) + public PageModule UpdatePageModule(PageModule pageModule) { - _db.Entry(PageModule).State = EntityState.Modified; + _db.Entry(pageModule).State = EntityState.Modified; _db.SaveChanges(); - return PageModule; + return pageModule; } - public PageModule GetPageModule(int PageModuleId) + public PageModule GetPageModule(int pageModuleId) { PageModule pagemodule = _db.PageModule.Include(item => item.Module) // eager load modules - .SingleOrDefault(item => item.PageModuleId == PageModuleId); + .SingleOrDefault(item => item.PageModuleId == pageModuleId); if (pagemodule != null) { IEnumerable permissions = _permissions.GetPermissions("Module", pagemodule.ModuleId).ToList(); @@ -78,10 +78,10 @@ namespace Oqtane.Repository return pagemodule; } - public PageModule GetPageModule(int PageId, int ModuleId) + public PageModule GetPageModule(int pageId, int moduleId) { PageModule pagemodule = _db.PageModule.Include(item => item.Module) // eager load modules - .SingleOrDefault(item => item.PageId == PageId && item.ModuleId == ModuleId); + .SingleOrDefault(item => item.PageId == pageId && item.ModuleId == moduleId); if (pagemodule != null) { IEnumerable permissions = _permissions.GetPermissions("Module", pagemodule.ModuleId).ToList(); @@ -90,10 +90,10 @@ namespace Oqtane.Repository return pagemodule; } - public void DeletePageModule(int PageModuleId) + public void DeletePageModule(int pageModuleId) { - PageModule PageModule = _db.PageModule.Find(PageModuleId); - _db.PageModule.Remove(PageModule); + PageModule pageModule = _db.PageModule.Find(pageModuleId); + _db.PageModule.Remove(pageModule); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Repository/PageRepository.cs b/Oqtane.Server/Repository/PageRepository.cs index a016a88c..f5b726f3 100644 --- a/Oqtane.Server/Repository/PageRepository.cs +++ b/Oqtane.Server/Repository/PageRepository.cs @@ -1,6 +1,6 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; namespace Oqtane.Repository @@ -18,10 +18,10 @@ namespace Oqtane.Repository _pageModules = pageModules; } - public IEnumerable GetPages(int SiteId) + public IEnumerable GetPages(int siteId) { - IEnumerable permissions = _permissions.GetPermissions(SiteId, "Page").ToList(); - IEnumerable pages = _db.Page.Where(item => item.SiteId == SiteId && item.UserId == null); + IEnumerable permissions = _permissions.GetPermissions(siteId, "Page").ToList(); + IEnumerable pages = _db.Page.Where(item => item.SiteId == siteId && item.UserId == null); foreach(Page page in pages) { page.Permissions = _permissions.EncodePermissions(page.PageId, permissions); @@ -29,25 +29,25 @@ namespace Oqtane.Repository return pages; } - public Page AddPage(Page Page) + public Page AddPage(Page page) { - _db.Page.Add(Page); + _db.Page.Add(page); _db.SaveChanges(); - _permissions.UpdatePermissions(Page.SiteId, "Page", Page.PageId, Page.Permissions); - return Page; + _permissions.UpdatePermissions(page.SiteId, "Page", page.PageId, page.Permissions); + return page; } - public Page UpdatePage(Page Page) + public Page UpdatePage(Page page) { - _db.Entry(Page).State = EntityState.Modified; + _db.Entry(page).State = EntityState.Modified; _db.SaveChanges(); - _permissions.UpdatePermissions(Page.SiteId, "Page", Page.PageId, Page.Permissions); - return Page; + _permissions.UpdatePermissions(page.SiteId, "Page", page.PageId, page.Permissions); + return page; } - public Page GetPage(int PageId) + public Page GetPage(int pageId) { - Page page = _db.Page.Find(PageId); + Page page = _db.Page.Find(pageId); if (page != null) { IEnumerable permissions = _permissions.GetPermissions("Page", page.PageId).ToList(); @@ -56,12 +56,12 @@ namespace Oqtane.Repository return page; } - public Page GetPage(int PageId, int UserId) + public Page GetPage(int pageId, int userId) { - Page page = _db.Page.Find(PageId); + Page page = _db.Page.Find(pageId); if (page != null) { - Page personalized = _db.Page.Where(item => item.SiteId == page.SiteId && item.Path == page.Path && item.UserId == UserId).FirstOrDefault(); + Page personalized = _db.Page.Where(item => item.SiteId == page.SiteId && item.Path == page.Path && item.UserId == userId).FirstOrDefault(); if (personalized != null) { page = personalized; @@ -75,9 +75,9 @@ namespace Oqtane.Repository return page; } - public Page GetPage(string Path, int SiteId) + public Page GetPage(string path, int siteId) { - Page page = _db.Page.Where(item => item.Path == Path && item.SiteId == SiteId).FirstOrDefault(); + Page page = _db.Page.Where(item => item.Path == path && item.SiteId == siteId).FirstOrDefault(); if (page != null) { IEnumerable permissions = _permissions.GetPermissions("Page", page.PageId).ToList(); @@ -86,16 +86,16 @@ namespace Oqtane.Repository return page; } - public void DeletePage(int PageId) + public void DeletePage(int pageId) { - Page Page = _db.Page.Find(PageId); - _permissions.DeletePermissions(Page.SiteId, "Page", PageId); - IEnumerable pageModules = _db.PageModule.Where(item => item.PageId == PageId).ToList(); + Page page = _db.Page.Find(pageId); + _permissions.DeletePermissions(page.SiteId, "Page", pageId); + IEnumerable pageModules = _db.PageModule.Where(item => item.PageId == pageId).ToList(); foreach (var pageModule in pageModules) { _pageModules.DeletePageModule(pageModule.PageModuleId); } - _db.Page.Remove(Page); + _db.Page.Remove(page); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Repository/PermissionRepository.cs b/Oqtane.Server/Repository/PermissionRepository.cs index f9edc3b6..991d222c 100644 --- a/Oqtane.Server/Repository/PermissionRepository.cs +++ b/Oqtane.Server/Repository/PermissionRepository.cs @@ -1,11 +1,10 @@ -using Microsoft.EntityFrameworkCore; +using System; using System.Collections.Generic; using System.Linq; -using Oqtane.Models; using System.Text; -using System; using System.Text.Json; -using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; +using Oqtane.Models; namespace Oqtane.Repository { @@ -20,55 +19,55 @@ namespace Oqtane.Repository _roles = roles; } - public IEnumerable GetPermissions(int SiteId, string EntityName) + public IEnumerable GetPermissions(int siteId, string entityName) { - return _db.Permission.Where(item => item.SiteId == SiteId) - .Where(item => item.EntityName == EntityName) + return _db.Permission.Where(item => item.SiteId == siteId) + .Where(item => item.EntityName == entityName) .Include(item => item.Role); // eager load roles } - public IEnumerable GetPermissions(string EntityName, int EntityId) + public IEnumerable GetPermissions(string entityName, int entityId) { - return _db.Permission.Where(item => item.EntityName == EntityName) - .Where(item => item.EntityId == EntityId) + return _db.Permission.Where(item => item.EntityName == entityName) + .Where(item => item.EntityId == entityId) .Include(item => item.Role); // eager load roles } - public IEnumerable GetPermissions(string EntityName, int EntityId, string PermissionName) + public IEnumerable GetPermissions(string entityName, int entityId, string permissionName) { - return _db.Permission.Where(item => item.EntityName == EntityName) - .Where(item => item.EntityId == EntityId) - .Where(item => item.PermissionName == PermissionName) + return _db.Permission.Where(item => item.EntityName == entityName) + .Where(item => item.EntityId == entityId) + .Where(item => item.PermissionName == permissionName) .Include(item => item.Role); // eager load roles } - public Permission AddPermission(Permission Permission) + public Permission AddPermission(Permission permission) { - _db.Permission.Add(Permission); + _db.Permission.Add(permission); _db.SaveChanges(); - return Permission; + return permission; } - public Permission UpdatePermission(Permission Permission) + public Permission UpdatePermission(Permission permission) { - _db.Entry(Permission).State = EntityState.Modified; + _db.Entry(permission).State = EntityState.Modified; _db.SaveChanges(); - return Permission; + return permission; } - public void UpdatePermissions(int SiteId, string EntityName, int EntityId, string Permissions) + public void UpdatePermissions(int siteId, string entityName, int entityId, string permissionStrings) { // get current permissions and delete IEnumerable permissions = _db.Permission - .Where(item => item.EntityName == EntityName) - .Where(item => item.EntityId == EntityId) - .Where(item => item.SiteId == SiteId); + .Where(item => item.EntityName == entityName) + .Where(item => item.EntityId == entityId) + .Where(item => item.SiteId == siteId); foreach (Permission permission in permissions) { _db.Permission.Remove(permission); } // add permissions - permissions = DecodePermissions(Permissions, SiteId, EntityName, EntityId); + permissions = DecodePermissions(permissionStrings, siteId, entityName, entityId); foreach (Permission permission in permissions) { _db.Permission.Add(permission); @@ -76,24 +75,24 @@ namespace Oqtane.Repository _db.SaveChanges(); } - public Permission GetPermission(int PermissionId) + public Permission GetPermission(int permissionId) { - return _db.Permission.Find(PermissionId); + return _db.Permission.Find(permissionId); } - public void DeletePermission(int PermissionId) + public void DeletePermission(int permissionId) { - Permission Permission = _db.Permission.Find(PermissionId); - _db.Permission.Remove(Permission); + Permission permission = _db.Permission.Find(permissionId); + _db.Permission.Remove(permission); _db.SaveChanges(); } - public void DeletePermissions(int SiteId, string EntityName, int EntityId) + public void DeletePermissions(int siteId, string entityName, int entityId) { IEnumerable permissions = _db.Permission - .Where(item => item.EntityName == EntityName) - .Where(item => item.EntityId == EntityId) - .Where(item => item.SiteId == SiteId); + .Where(item => item.EntityName == entityName) + .Where(item => item.EntityId == entityId) + .Where(item => item.SiteId == siteId); foreach (Permission permission in permissions) { _db.Permission.Remove(permission); @@ -102,14 +101,14 @@ namespace Oqtane.Repository } // permissions are stored in the format "{permissionname:!rolename1;![userid1];rolename2;rolename3;[userid2];[userid3]}" where "!" designates Deny permissions - public string EncodePermissions(int EntityId, IEnumerable Permissions) + public string EncodePermissions(int entityId, IEnumerable permissionList) { List permissionstrings = new List(); string permissionname = ""; string permissions = ""; StringBuilder permissionsbuilder = new StringBuilder(); string securityid = ""; - foreach (Permission permission in Permissions.Where(item => item.EntityId == EntityId).OrderBy(item => item.PermissionName)) + foreach (Permission permission in permissionList.Where(item => item.EntityId == entityId).OrderBy(item => item.PermissionName)) { // permission collections are grouped by permissionname if (permissionname != permission.PermissionName) @@ -133,7 +132,7 @@ namespace Oqtane.Repository } else { - securityid = prefix + "[" + permission.UserId.ToString() + "];"; + securityid = prefix + "[" + permission.UserId + "];"; } // insert deny permissions at the beginning and append grant permissions at the end @@ -155,20 +154,20 @@ namespace Oqtane.Repository return JsonSerializer.Serialize(permissionstrings); } - public IEnumerable DecodePermissions(string PermissionStrings, int SiteId, string EntityName, int EntityId) + public IEnumerable DecodePermissions(string permissionStrings, int siteId, string entityName, int entityId) { List permissions = new List(); - List roles = _roles.GetRoles(SiteId, true).ToList(); + List roles = _roles.GetRoles(siteId, true).ToList(); string securityid = ""; - foreach (PermissionString permissionstring in JsonSerializer.Deserialize>(PermissionStrings)) + foreach (PermissionString permissionstring in JsonSerializer.Deserialize>(permissionStrings)) { foreach (string id in permissionstring.Permissions.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { securityid = id; Permission permission = new Permission(); - permission.SiteId = SiteId; - permission.EntityName = EntityName; - permission.EntityId = EntityId; + permission.SiteId = siteId; + permission.EntityName = entityName; + permission.EntityId = entityId; permission.PermissionName = permissionstring.PermissionName; permission.RoleId = null; permission.UserId = null; @@ -177,7 +176,7 @@ namespace Oqtane.Repository if (securityid.StartsWith("!")) { // deny permission - securityid.Replace("!", ""); + securityid = securityid.Replace("!", ""); permission.IsAuthorized = false; } if (securityid.StartsWith("[") && securityid.EndsWith("]")) @@ -189,7 +188,7 @@ namespace Oqtane.Repository else { // role name - Role role = roles.Where(item => item.Name == securityid).SingleOrDefault(); + Role role = roles.SingleOrDefault(item => item.Name == securityid); if (role != null) { permission.RoleId = role.RoleId; diff --git a/Oqtane.Server/Repository/ProfileRepository.cs b/Oqtane.Server/Repository/ProfileRepository.cs index 91062cf9..8577ea78 100644 --- a/Oqtane.Server/Repository/ProfileRepository.cs +++ b/Oqtane.Server/Repository/ProfileRepository.cs @@ -1,6 +1,6 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; namespace Oqtane.Repository @@ -14,34 +14,34 @@ namespace Oqtane.Repository _db = context; } - public IEnumerable GetProfiles(int SiteId) + public IEnumerable GetProfiles(int siteId) { - return _db.Profile.Where(item => item.SiteId == SiteId || item.SiteId == null); + return _db.Profile.Where(item => item.SiteId == siteId || item.SiteId == null); } - public Profile AddProfile(Profile Profile) + public Profile AddProfile(Profile profile) { - _db.Profile.Add(Profile); + _db.Profile.Add(profile); _db.SaveChanges(); - return Profile; + return profile; } - public Profile UpdateProfile(Profile Profile) + public Profile UpdateProfile(Profile profile) { - _db.Entry(Profile).State = EntityState.Modified; + _db.Entry(profile).State = EntityState.Modified; _db.SaveChanges(); - return Profile; + return profile; } - public Profile GetProfile(int ProfileId) + public Profile GetProfile(int profileId) { - return _db.Profile.Find(ProfileId); + return _db.Profile.Find(profileId); } - public void DeleteProfile(int ProfileId) + public void DeleteProfile(int profileId) { - Profile Profile = _db.Profile.Find(ProfileId); - _db.Profile.Remove(Profile); + Profile profile = _db.Profile.Find(profileId); + _db.Profile.Remove(profile); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Repository/RoleRepository.cs b/Oqtane.Server/Repository/RoleRepository.cs index 35aa7fe3..2814d7e0 100644 --- a/Oqtane.Server/Repository/RoleRepository.cs +++ b/Oqtane.Server/Repository/RoleRepository.cs @@ -1,6 +1,6 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; namespace Oqtane.Repository @@ -14,40 +14,40 @@ namespace Oqtane.Repository _db = context; } - public IEnumerable GetRoles(int SiteId) + public IEnumerable GetRoles(int siteId) { - return _db.Role.Where(item => item.SiteId == SiteId); + return _db.Role.Where(item => item.SiteId == siteId); } - public IEnumerable GetRoles(int SiteId, bool IncludeGlobalRoles) + public IEnumerable GetRoles(int siteId, bool includeGlobalRoles) { - return _db.Role.Where(item => item.SiteId == SiteId || item.SiteId == null); + return _db.Role.Where(item => item.SiteId == siteId || item.SiteId == null); } - public Role AddRole(Role Role) + public Role AddRole(Role role) { - _db.Role.Add(Role); + _db.Role.Add(role); _db.SaveChanges(); - return Role; + return role; } - public Role UpdateRole(Role Role) + public Role UpdateRole(Role role) { - _db.Entry(Role).State = EntityState.Modified; + _db.Entry(role).State = EntityState.Modified; _db.SaveChanges(); - return Role; + return role; } - public Role GetRole(int RoleId) + public Role GetRole(int roleId) { - return _db.Role.Find(RoleId); + return _db.Role.Find(roleId); } - public void DeleteRole(int RoleId) + public void DeleteRole(int roleId) { - Role Role = _db.Role.Find(RoleId); - _db.Role.Remove(Role); + Role role = _db.Role.Find(roleId); + _db.Role.Remove(role); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Repository/SettingRepository.cs b/Oqtane.Server/Repository/SettingRepository.cs index 3d670f55..30b7f676 100644 --- a/Oqtane.Server/Repository/SettingRepository.cs +++ b/Oqtane.Server/Repository/SettingRepository.cs @@ -1,6 +1,6 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; namespace Oqtane.Repository @@ -14,35 +14,35 @@ namespace Oqtane.Repository _db = context; } - public IEnumerable GetSettings(string EntityName, int EntityId) + public IEnumerable GetSettings(string entityName, int entityId) { - return _db.Setting.Where(item => item.EntityName == EntityName) - .Where(item => item.EntityId == EntityId); + return _db.Setting.Where(item => item.EntityName == entityName) + .Where(item => item.EntityId == entityId); } - public Setting AddSetting(Setting Setting) + public Setting AddSetting(Setting setting) { - _db.Setting.Add(Setting); + _db.Setting.Add(setting); _db.SaveChanges(); - return Setting; + return setting; } - public Setting UpdateSetting(Setting Setting) + public Setting UpdateSetting(Setting setting) { - _db.Entry(Setting).State = EntityState.Modified; + _db.Entry(setting).State = EntityState.Modified; _db.SaveChanges(); - return Setting; + return setting; } - public Setting GetSetting(int SettingId) + public Setting GetSetting(int settingId) { - return _db.Setting.Find(SettingId); + return _db.Setting.Find(settingId); } - public void DeleteSetting(int SettingId) + public void DeleteSetting(int settingId) { - Setting Setting = _db.Setting.Find(SettingId); - _db.Setting.Remove(Setting); + Setting setting = _db.Setting.Find(settingId); + _db.Setting.Remove(setting); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 6b6db2c1..7dead20c 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -1,12 +1,13 @@ -using Microsoft.EntityFrameworkCore; +using System; using System.Collections.Generic; using System.Linq; -using Oqtane.Models; -using Oqtane.Shared; -using System; using System.Reflection; -using Oqtane.Modules; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using Oqtane.Models; +using Oqtane.Modules; +using Oqtane.Shared; +using Module = Oqtane.Models.Module; namespace Oqtane.Repository { @@ -130,19 +131,19 @@ namespace Oqtane.Repository return _db.Site; } - public Site AddSite(Site Site) + public Site AddSite(Site site) { - _db.Site.Add(Site); + _db.Site.Add(site); _db.SaveChanges(); - CreateSite(Site); - return Site; + CreateSite(site); + return site; } - public Site UpdateSite(Site Site) + public Site UpdateSite(Site site) { - _db.Entry(Site).State = EntityState.Modified; + _db.Entry(site).State = EntityState.Modified; _db.SaveChanges(); - return Site; + return site; } public Site GetSite(int siteId) @@ -226,7 +227,7 @@ namespace Oqtane.Repository ModuleDefinition moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == pagetemplatemodule.ModuleDefinitionName).FirstOrDefault(); if (moduledefinition != null) { - Models.Module module = new Models.Module + Module module = new Module { SiteId = site.SiteId, ModuleDefinitionName = pagetemplatemodule.ModuleDefinitionName, diff --git a/Oqtane.Server/Repository/TenantRepository.cs b/Oqtane.Server/Repository/TenantRepository.cs index 8d8c989e..19acf9fa 100644 --- a/Oqtane.Server/Repository/TenantRepository.cs +++ b/Oqtane.Server/Repository/TenantRepository.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using Oqtane.Models; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Caching.Memory; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; +using Oqtane.Models; namespace Oqtane.Repository { @@ -28,30 +27,30 @@ namespace Oqtane.Repository }); } - public Tenant AddTenant(Tenant Tenant) + public Tenant AddTenant(Tenant tenant) { - _db.Tenant.Add(Tenant); + _db.Tenant.Add(tenant); _db.SaveChanges(); _cache.Remove("tenants"); - return Tenant; + return tenant; } - public Tenant UpdateTenant(Tenant Tenant) + public Tenant UpdateTenant(Tenant tenant) { - _db.Entry(Tenant).State = EntityState.Modified; + _db.Entry(tenant).State = EntityState.Modified; _db.SaveChanges(); _cache.Remove("tenants"); - return Tenant; + return tenant; } - public Tenant GetTenant(int TenantId) + public Tenant GetTenant(int tenantId) { - return _db.Tenant.Find(TenantId); + return _db.Tenant.Find(tenantId); } - public void DeleteTenant(int TenantId) + public void DeleteTenant(int tenantId) { - Tenant tenant = _db.Tenant.Find(TenantId); + Tenant tenant = _db.Tenant.Find(tenantId); _db.Tenant.Remove(tenant); _db.SaveChanges(); _cache.Remove("tenants"); diff --git a/Oqtane.Server/Repository/TenantResolver.cs b/Oqtane.Server/Repository/TenantResolver.cs index 7cbfda35..9760c4e6 100644 --- a/Oqtane.Server/Repository/TenantResolver.cs +++ b/Oqtane.Server/Repository/TenantResolver.cs @@ -1,70 +1,70 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; -using Oqtane.Models; using Microsoft.AspNetCore.Http; -using System; +using Oqtane.Models; using Oqtane.Shared; namespace Oqtane.Repository { public class TenantResolver : ITenantResolver { - private readonly Alias _alias = null; - private readonly Tenant _tenant = null; + private readonly Alias _alias; + private readonly Tenant _tenant; - public TenantResolver(IHttpContextAccessor Accessor, IAliasRepository Aliases, ITenantRepository Tenants, SiteState SiteState) + public TenantResolver(IHttpContextAccessor accessor, IAliasRepository aliasRepository, ITenantRepository tenantRepository, SiteState siteState) { - int aliasid = -1; - string aliasname = ""; + int aliasId = -1; + string aliasName = ""; // get alias identifier based on request context - if (Accessor.HttpContext != null) + if (accessor.HttpContext != null) { // check if an alias is passed as a querystring parameter ( for cross tenant access ) - if (Accessor.HttpContext.Request.Query.ContainsKey("aliasid")) + if (accessor.HttpContext.Request.Query.ContainsKey("aliasid")) { - aliasid = int.Parse(Accessor.HttpContext.Request.Query["aliasid"]); + aliasId = int.Parse(accessor.HttpContext.Request.Query["aliasid"]); } else // get the alias from the request url { - aliasname = Accessor.HttpContext.Request.Host.Value; - string path = Accessor.HttpContext.Request.Path.Value; + aliasName = accessor.HttpContext.Request.Host.Value; + string path = accessor.HttpContext.Request.Path.Value; string[] segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); if (segments.Length > 1 && segments[1] == "api" && segments[0] != "~") { - aliasname += "/" + segments[0]; + aliasName += "/" + segments[0]; } - if (aliasname.EndsWith("/")) + if (aliasName.EndsWith("/")) { - aliasname = aliasname.Substring(0, aliasname.Length - 1); + aliasName = aliasName.Substring(0, aliasName.Length - 1); } } } else // background processes can pass in an alias using the SiteState service { - if (SiteState != null) + if (siteState != null) { - aliasid = SiteState.Alias.AliasId; + aliasId = siteState.Alias.AliasId; } } // get the alias and tenant - if (aliasid != -1 || aliasname != "") + if (aliasId != -1 || aliasName != "") { - IEnumerable aliases = Aliases.GetAliases(); // cached - IEnumerable tenants = Tenants.GetTenants(); // cached + IEnumerable aliases = aliasRepository.GetAliases(); // cached + IEnumerable tenants = tenantRepository.GetTenants(); // cached - if (aliasid != -1) + if (aliasId != -1) { - _alias = aliases.Where(item => item.AliasId == aliasid).FirstOrDefault(); + _alias = aliases.FirstOrDefault(item => item.AliasId == aliasId); } else { - _alias = aliases.Where(item => item.Name == aliasname).FirstOrDefault(); + _alias = aliases.FirstOrDefault(item => item.Name == aliasName); } if (_alias != null) { - _tenant = tenants.Where(item => item.TenantId == _alias.TenantId).FirstOrDefault(); + _tenant = tenants.FirstOrDefault(item => item.TenantId == _alias.TenantId); } } } @@ -79,4 +79,4 @@ namespace Oqtane.Repository return _tenant; } } -} \ No newline at end of file +} diff --git a/Oqtane.Server/Repository/ThemeRepository.cs b/Oqtane.Server/Repository/ThemeRepository.cs index 0de27194..3bb3f7e9 100644 --- a/Oqtane.Server/Repository/ThemeRepository.cs +++ b/Oqtane.Server/Repository/ThemeRepository.cs @@ -1,9 +1,8 @@ -using Microsoft.EntityFrameworkCore; +using System; using System.Collections.Generic; using System.Linq; -using Oqtane.Models; using System.Reflection; -using System; +using Oqtane.Models; using Oqtane.Themes; namespace Oqtane.Repository @@ -12,17 +11,17 @@ namespace Oqtane.Repository { private List LoadThemes() { - List Themes = new List(); + List themes = new List(); // iterate through Oqtane theme assemblies Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Theme.")).ToArray(); foreach (Assembly assembly in assemblies) { - Themes = LoadThemesFromAssembly(Themes, assembly); + themes = LoadThemesFromAssembly(themes, assembly); } - return Themes; + return themes; } private List LoadThemesFromAssembly(List themes, Assembly assembly) @@ -36,15 +35,15 @@ namespace Oqtane.Repository string[] typename = themeControlType.AssemblyQualifiedName.Split(',').Select(item => item.Trim()).ToList().ToArray(); string[] segments = typename[0].Split('.'); Array.Resize(ref segments, segments.Length - 1); - string Namespace = string.Join(".", segments); + string @namespace = string.Join(".", segments); - int index = themes.FindIndex(item => item.ThemeName == Namespace); + int index = themes.FindIndex(item => item.ThemeName == @namespace); if (index == -1) { - /// determine if this theme implements ITheme + // determine if this theme implements ITheme Type themetype = assembly.GetTypes() .Where(item => item.Namespace != null) - .Where(item => item.Namespace.StartsWith(Namespace)) + .Where(item => item.Namespace.StartsWith(@namespace)) .Where(item => item.GetInterfaces().Contains(typeof(ITheme))).FirstOrDefault(); if (themetype != null) { @@ -52,7 +51,7 @@ namespace Oqtane.Repository Dictionary properties = (Dictionary)themetype.GetProperty("Properties").GetValue(themeobject); theme = new Theme { - ThemeName = Namespace, + ThemeName = @namespace, Name = GetProperty(properties, "Name"), Version = GetProperty(properties, "Version"), Owner = GetProperty(properties, "Owner"), @@ -70,7 +69,7 @@ namespace Oqtane.Repository { theme = new Theme { - ThemeName = Namespace, + ThemeName = @namespace, Name = themeControlType.Name, Version = new Version(1, 0, 0).ToString(), Owner = "", @@ -85,7 +84,7 @@ namespace Oqtane.Repository }; } themes.Add(theme); - index = themes.FindIndex(item => item.ThemeName == Namespace); + index = themes.FindIndex(item => item.ThemeName == @namespace); } theme = themes[index]; theme.ThemeControls += (themeControlType.FullName + ", " + typename[1] + ";"); @@ -93,7 +92,7 @@ namespace Oqtane.Repository // layouts Type[] layouttypes = assembly.GetTypes() .Where(item => item.Namespace != null) - .Where(item => item.Namespace.StartsWith(Namespace)) + .Where(item => item.Namespace.StartsWith(@namespace)) .Where(item => item.GetInterfaces().Contains(typeof(ILayoutControl))).ToArray(); foreach (Type layouttype in layouttypes) { @@ -107,7 +106,7 @@ namespace Oqtane.Repository // containers Type[] containertypes = assembly.GetTypes() .Where(item => item.Namespace != null) - .Where(item => item.Namespace.StartsWith(Namespace)) + .Where(item => item.Namespace.StartsWith(@namespace)) .Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray(); foreach (Type containertype in containertypes) { @@ -124,14 +123,14 @@ namespace Oqtane.Repository return themes; } - private string GetProperty(Dictionary Properties, string Key) + private string GetProperty(Dictionary properties, string key) { - string Value = ""; - if (Properties.ContainsKey(Key)) + string value = ""; + if (properties.ContainsKey(key)) { - Value = Properties[Key]; + value = properties[key]; } - return Value; + return value; } public IEnumerable GetThemes() diff --git a/Oqtane.Server/Repository/UserRepository.cs b/Oqtane.Server/Repository/UserRepository.cs index 16478504..a9e51afe 100644 --- a/Oqtane.Server/Repository/UserRepository.cs +++ b/Oqtane.Server/Repository/UserRepository.cs @@ -1,6 +1,6 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; namespace Oqtane.Repository @@ -38,9 +38,9 @@ namespace Oqtane.Repository return _db.User.Find(userId); } - public User GetUser(string Username) + public User GetUser(string username) { - return _db.User.Where(item => item.Username == Username).FirstOrDefault(); + return _db.User.Where(item => item.Username == username).FirstOrDefault(); } public void DeleteUser(int userId) diff --git a/Oqtane.Server/Repository/UserRoleRepository.cs b/Oqtane.Server/Repository/UserRoleRepository.cs index bf4c9676..79f8a629 100644 --- a/Oqtane.Server/Repository/UserRoleRepository.cs +++ b/Oqtane.Server/Repository/UserRoleRepository.cs @@ -1,6 +1,6 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; namespace Oqtane.Repository @@ -14,48 +14,48 @@ namespace Oqtane.Repository _db = context; } - public IEnumerable GetUserRoles(int SiteId) + public IEnumerable GetUserRoles(int siteId) { return _db.UserRole .Include(item => item.Role) // eager load roles .Include(item => item.User) // eager load users - .Where(item => item.Role.SiteId == SiteId || item.Role.SiteId == null); + .Where(item => item.Role.SiteId == siteId || item.Role.SiteId == null); } - public IEnumerable GetUserRoles(int UserId, int SiteId) + public IEnumerable GetUserRoles(int userId, int siteId) { - return _db.UserRole.Where(item => item.UserId == UserId) + return _db.UserRole.Where(item => item.UserId == userId) .Include(item => item.Role) // eager load roles .Include(item => item.User) // eager load users - .Where(item => item.Role.SiteId == SiteId || item.Role.SiteId == null); + .Where(item => item.Role.SiteId == siteId || item.Role.SiteId == null); } - public UserRole AddUserRole(UserRole UserRole) + public UserRole AddUserRole(UserRole userRole) { - _db.UserRole.Add(UserRole); + _db.UserRole.Add(userRole); _db.SaveChanges(); - return UserRole; + return userRole; } - public UserRole UpdateUserRole(UserRole UserRole) + public UserRole UpdateUserRole(UserRole userRole) { - _db.Entry(UserRole).State = EntityState.Modified; + _db.Entry(userRole).State = EntityState.Modified; _db.SaveChanges(); - return UserRole; + return userRole; } - public UserRole GetUserRole(int UserRoleId) + public UserRole GetUserRole(int userRoleId) { return _db.UserRole .Include(item => item.Role) // eager load roles .Include(item => item.User) // eager load users - .SingleOrDefault(item => item.UserRoleId == UserRoleId); + .SingleOrDefault(item => item.UserRoleId == userRoleId); } - public void DeleteUserRole(int UserRoleId) + public void DeleteUserRole(int userRoleId) { - UserRole UserRole = _db.UserRole.Find(UserRoleId); - _db.UserRole.Remove(UserRole); + UserRole userRole = _db.UserRole.Find(userRoleId); + _db.UserRole.Remove(userRole); _db.SaveChanges(); } } diff --git a/Oqtane.Server/Security/ClaimsPrincipalFactory.cs b/Oqtane.Server/Security/ClaimsPrincipalFactory.cs index eadf7c6f..1e945938 100644 --- a/Oqtane.Server/Security/ClaimsPrincipalFactory.cs +++ b/Oqtane.Server/Security/ClaimsPrincipalFactory.cs @@ -2,11 +2,11 @@ using Microsoft.Extensions.Options; using System.Security.Claims; using System.Threading.Tasks; -using Oqtane.Repository; using Oqtane.Models; using Oqtane.Shared; using System.Collections.Generic; using System.Linq; +using Oqtane.Repository; namespace Oqtane.Security { diff --git a/Oqtane.Server/Security/IUserPermissions.cs b/Oqtane.Server/Security/IUserPermissions.cs index 92e50d1d..b7964383 100644 --- a/Oqtane.Server/Security/IUserPermissions.cs +++ b/Oqtane.Server/Security/IUserPermissions.cs @@ -5,9 +5,9 @@ namespace Oqtane.Security { public interface IUserPermissions { - bool IsAuthorized(ClaimsPrincipal User, string EntityName, int EntityId, string PermissionName); - bool IsAuthorized(ClaimsPrincipal User, string PermissionName, string Permissions); - User GetUser(ClaimsPrincipal User); + bool IsAuthorized(ClaimsPrincipal user, string entityName, int entityId, string permissionName); + bool IsAuthorized(ClaimsPrincipal user, string permissionName, string permissions); + User GetUser(ClaimsPrincipal user); User GetUser(); } } diff --git a/Oqtane.Server/Security/PermissionHandler.cs b/Oqtane.Server/Security/PermissionHandler.cs index 1f47122b..028df96c 100644 --- a/Oqtane.Server/Security/PermissionHandler.cs +++ b/Oqtane.Server/Security/PermissionHandler.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; -using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; using Oqtane.Shared; namespace Oqtane.Security @@ -25,14 +25,14 @@ namespace Oqtane.Security var ctx = _httpContextAccessor.HttpContext; if (ctx != null && ctx.Request.Query.ContainsKey("entityid")) { - int EntityId = int.Parse(ctx.Request.Query["entityid"]); - if (_userPermissions.IsAuthorized(context.User, requirement.EntityName, EntityId, requirement.PermissionName)) + int entityId = int.Parse(ctx.Request.Query["entityid"]); + if (_userPermissions.IsAuthorized(context.User, requirement.EntityName, entityId, requirement.PermissionName)) { context.Succeed(requirement); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "User {User} Does Not Have {PermissionName} Permission For {EntityName}:{EntityId}", context.User, requirement.PermissionName, requirement.EntityName, EntityId); + _logger.Log(LogLevel.Error, this, LogFunction.Security, "User {User} Does Not Have {PermissionName} Permission For {EntityName}:{EntityId}", context.User, requirement.PermissionName, requirement.EntityName, entityId); } } return Task.CompletedTask; diff --git a/Oqtane.Server/Security/UserPermissions.cs b/Oqtane.Server/Security/UserPermissions.cs index 3ec92160..2e20b062 100644 --- a/Oqtane.Server/Security/UserPermissions.cs +++ b/Oqtane.Server/Security/UserPermissions.cs @@ -1,8 +1,8 @@ using Microsoft.AspNetCore.Http; using Oqtane.Models; -using Oqtane.Repository; using System.Linq; using System.Security.Claims; +using Oqtane.Repository; namespace Oqtane.Security { @@ -17,41 +17,40 @@ namespace Oqtane.Security _accessor = accessor; } - public bool IsAuthorized(ClaimsPrincipal User, string EntityName, int EntityId, string PermissionName) + public bool IsAuthorized(ClaimsPrincipal user, string entityName, int entityId, string permissionName) { - return IsAuthorized(User, PermissionName, _permissions.EncodePermissions(EntityId, _permissions.GetPermissions(EntityName, EntityId, PermissionName).ToList())); + return IsAuthorized(user, permissionName, _permissions.EncodePermissions(entityId, _permissions.GetPermissions(entityName, entityId, permissionName).ToList())); } - public bool IsAuthorized(ClaimsPrincipal User, string PermissionName, string Permissions) + public bool IsAuthorized(ClaimsPrincipal user, string permissionName, string permissions) { - return UserSecurity.IsAuthorized(GetUser(User), PermissionName, Permissions); + return UserSecurity.IsAuthorized(GetUser(user), permissionName, permissions); } - public User GetUser(ClaimsPrincipal User) + public User GetUser(ClaimsPrincipal user) { - User user = new User(); - user.Username = ""; - user.IsAuthenticated = false; - user.UserId = -1; - user.Roles = ""; + User resultUser = new User(); + resultUser.Username = ""; + resultUser.IsAuthenticated = false; + resultUser.UserId = -1; + resultUser.Roles = ""; - if (User != null) + if (user == null) return resultUser; + + resultUser.Username = user.Identity.Name; + resultUser.IsAuthenticated = user.Identity.IsAuthenticated; + var idclaim = user.Claims.FirstOrDefault(item => item.Type == ClaimTypes.PrimarySid); + if (idclaim != null) { - user.Username = User.Identity.Name; - user.IsAuthenticated = User.Identity.IsAuthenticated; - var idclaim = User.Claims.Where(item => item.Type == ClaimTypes.PrimarySid).FirstOrDefault(); - if (idclaim != null) + resultUser.UserId = int.Parse(idclaim.Value); + foreach (var claim in user.Claims.Where(item => item.Type == ClaimTypes.Role)) { - user.UserId = int.Parse(idclaim.Value); - foreach (var claim in User.Claims.Where(item => item.Type == ClaimTypes.Role)) - { - user.Roles += claim.Value + ";"; - } - if (user.Roles != "") user.Roles = ";" + user.Roles; + resultUser.Roles += claim.Value + ";"; } - } - return user; + if (resultUser.Roles != "") resultUser.Roles = ";" + resultUser.Roles; + } + return resultUser; } public User GetUser() diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index acb3b811..79a2d31c 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -1,34 +1,27 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.ResponseCompression; // needed for WASM -using Microsoft.Extensions.DependencyInjection; -using System.Linq; -using Microsoft.Extensions.Configuration; -using Microsoft.EntityFrameworkCore; -using Microsoft.AspNetCore.Http; using System; -using System.Reflection; -using Microsoft.Extensions.Hosting; -using Oqtane.Modules; -using Oqtane.Repository; using System.IO; -using System.Runtime.Loader; -using Oqtane.Services; +using System.Linq; using System.Net.Http; -using Microsoft.AspNetCore.Components; -using Oqtane.Shared; -using Microsoft.AspNetCore.Identity; using System.Threading.Tasks; -using System.Collections.Generic; -using Microsoft.OpenApi.Models; -using Oqtane.Security; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authentication; -using System.Net; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; +using Oqtane.Security; +using Oqtane.Services; +using Oqtane.Shared; // needed for WASM -namespace Oqtane.Server +namespace Oqtane { public class Startup { @@ -59,10 +52,10 @@ namespace Oqtane.Server // setup HttpClient for server side in a client side compatible fashion ( with auth cookie ) if (!services.Any(x => x.ServiceType == typeof(HttpClient))) { - services.AddScoped(s => + services.AddScoped(s => { // creating the URI helper needs to wait until the JS Runtime is initialized, so defer it. - var NavigationManager = s.GetRequiredService(); + var navigationManager = s.GetRequiredService(); var httpContextAccessor = s.GetRequiredService(); var authToken = httpContextAccessor.HttpContext.Request.Cookies[".AspNetCore.Identity.Application"]; var client = new HttpClient(new HttpClientHandler { UseCookies = false }); @@ -70,7 +63,7 @@ namespace Oqtane.Server { client.DefaultRequestHeaders.Add("Cookie", ".AspNetCore.Identity.Application=" + authToken); } - client.BaseAddress = new Uri(NavigationManager.Uri); + client.BaseAddress = new Uri(navigationManager.Uri); return client; }); } @@ -78,13 +71,13 @@ namespace Oqtane.Server // register authorization services services.AddAuthorizationCore(options => { - options.AddPolicy("ViewPage", policy => policy.Requirements.Add(new PermissionRequirement("Page", PermissionNames.View))); - options.AddPolicy("EditPage", policy => policy.Requirements.Add(new PermissionRequirement("Page", PermissionNames.Edit))); - options.AddPolicy("ViewModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", PermissionNames.View))); - options.AddPolicy("EditModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", PermissionNames.Edit))); - options.AddPolicy("ViewFolder", policy => policy.Requirements.Add(new PermissionRequirement("Folder", PermissionNames.View))); - options.AddPolicy("EditFolder", policy => policy.Requirements.Add(new PermissionRequirement("Folder", PermissionNames.Edit))); - options.AddPolicy("ListFolder", policy => policy.Requirements.Add(new PermissionRequirement("Folder", "List"))); + options.AddPolicy("ViewPage", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.View))); + options.AddPolicy("EditPage", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.Edit))); + options.AddPolicy("ViewModule", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Module, PermissionNames.View))); + options.AddPolicy("EditModule", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Module, PermissionNames.Edit))); + options.AddPolicy("ViewFolder", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Folder, PermissionNames.View))); + options.AddPolicy("EditFolder", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Folder, PermissionNames.Edit))); + options.AddPolicy("ListFolder", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Folder, PermissionNames.Browse))); }); // register scoped core services @@ -160,7 +153,7 @@ namespace Oqtane.Server services.AddTransient, ClaimsPrincipalFactory>(); // register singleton scoped core services - services.AddSingleton(Configuration); + services.AddSingleton(Configuration); services.AddSingleton(); services.AddSingleton(); @@ -206,7 +199,7 @@ namespace Oqtane.Server } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IInstallationManager InstallationManager) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IInstallationManager installationManager) { if (env.IsDevelopment()) { @@ -219,7 +212,7 @@ namespace Oqtane.Server } // install any modules or themes - InstallationManager.InstallPackages("Modules,Themes", false); + installationManager.InstallPackages("Modules,Themes", false); app.UseHttpsRedirection(); diff --git a/Oqtane.Shared/Models/Log.cs b/Oqtane.Shared/Models/Log.cs index 1e8aa8c8..a1ff4c16 100644 --- a/Oqtane.Shared/Models/Log.cs +++ b/Oqtane.Shared/Models/Log.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel.DataAnnotations.Schema; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Module.cs b/Oqtane.Shared/Models/Module.cs index 0466c326..927377a0 100644 --- a/Oqtane.Shared/Models/Module.cs +++ b/Oqtane.Shared/Models/Module.cs @@ -1,7 +1,5 @@ -using Oqtane.Modules; -using Oqtane.Shared; +using Oqtane.Shared; using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; namespace Oqtane.Models diff --git a/Oqtane.Shared/Security/UserSecurity.cs b/Oqtane.Shared/Security/UserSecurity.cs index 01bbb7e7..a8e85304 100644 --- a/Oqtane.Shared/Security/UserSecurity.cs +++ b/Oqtane.Shared/Security/UserSecurity.cs @@ -9,21 +9,21 @@ namespace Oqtane.Security { public class UserSecurity { - public static List GetPermissionStrings(string PermissionStrings) + public static List GetPermissionStrings(string permissionStrings) { - return JsonSerializer.Deserialize>(PermissionStrings); + return JsonSerializer.Deserialize>(permissionStrings); } - public static string SetPermissionStrings(List PermissionStrings) + public static string SetPermissionStrings(List permissionStrings) { - return JsonSerializer.Serialize(PermissionStrings); + return JsonSerializer.Serialize(permissionStrings); } - public static string GetPermissions(string PermissionName, string PermissionStrings) + public static string GetPermissions(string permissionName, string permissionStrings) { string permissions = ""; - List permissionstrings = JsonSerializer.Deserialize>(PermissionStrings); - PermissionString permissionstring = permissionstrings.FirstOrDefault(item => item.PermissionName == PermissionName); + List permissionstrings = JsonSerializer.Deserialize>(permissionStrings); + PermissionString permissionstring = permissionstrings.FirstOrDefault(item => item.PermissionName == permissionName); if (permissionstring != null) { permissions = permissionstring.Permissions; @@ -31,68 +31,68 @@ namespace Oqtane.Security return permissions; } - public static bool IsAuthorized(User User, string PermissionName, string PermissionStrings) + public static bool IsAuthorized(User user, string permissionName, string permissionStrings) { - return IsAuthorized(User, GetPermissions(PermissionName, PermissionStrings)); + return IsAuthorized(user, GetPermissions(permissionName, permissionStrings)); } // permissions are stored in the format "!rolename1;![userid1];rolename2;rolename3;[userid2];[userid3]" where "!" designates Deny permissions - public static bool IsAuthorized(User User, string Permissions) + public static bool IsAuthorized(User user, string permissions) { bool authorized = false; - if (Permissions != "") + if (permissions != "") { - if (User == null) + if (user == null) { - authorized = IsAuthorized(-1, "", Permissions); // user is not authenticated but may have access to resource + authorized = IsAuthorized(-1, "", permissions); // user is not authenticated but may have access to resource } else { - authorized = IsAuthorized(User.UserId, User.Roles, Permissions); + authorized = IsAuthorized(user.UserId, user.Roles, permissions); } } return authorized; } - private static bool IsAuthorized(int UserId, string Roles, string Permissions) + private static bool IsAuthorized(int userId, string roles, string permissions) { - bool IsAuthorized = false; + bool isAuthorized = false; - if (Permissions != null) + if (permissions != null) { - foreach (string permission in Permissions.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + foreach (string permission in permissions.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { - bool? allowed = VerifyPermission(UserId, Roles, permission); + bool? allowed = VerifyPermission(userId, roles, permission); if (allowed.HasValue) { - IsAuthorized = allowed.Value; + isAuthorized = allowed.Value; break; } } } - return IsAuthorized; + return isAuthorized; } - private static bool? VerifyPermission(int UserId, string Roles, string Permission) + private static bool? VerifyPermission(int userId, string roles, string permission) { bool? allowed = null; //permissions strings are encoded with deny permissions at the beginning and grant permissions at the end for optimal performance - if (!String.IsNullOrEmpty(Permission)) + if (!String.IsNullOrEmpty(permission)) { // deny permission - if (Permission.StartsWith("!")) + if (permission.StartsWith("!")) { - string denyRole = Permission.Replace("!", ""); - if (denyRole == Constants.AllUsersRole || IsAllowed(UserId, Roles, denyRole)) + string denyRole = permission.Replace("!", ""); + if (denyRole == Constants.AllUsersRole || IsAllowed(userId, roles, denyRole)) { allowed = false; } } else // grant permission { - if (Permission == Constants.AllUsersRole || IsAllowed(UserId, Roles, Permission)) + if (permission == Constants.AllUsersRole || IsAllowed(userId, roles, permission)) { allowed = true; } @@ -101,16 +101,16 @@ namespace Oqtane.Security return allowed; } - private static bool IsAllowed(int UserId, string Roles, string Permission) + private static bool IsAllowed(int userId, string roles, string permission) { - if ("[" + UserId + "]" == Permission) + if ("[" + userId + "]" == permission) { return true; } - if (Roles != null) + if (roles != null) { - return Roles.IndexOf(";" + Permission + ";") != -1; + return roles.IndexOf(";" + permission + ";") != -1; } return false; } diff --git a/Oqtane.Upgrade/Program.cs b/Oqtane.Upgrade/Program.cs index 70ede475..8aa70d3b 100644 --- a/Oqtane.Upgrade/Program.cs +++ b/Oqtane.Upgrade/Program.cs @@ -125,27 +125,25 @@ namespace Oqtane.Upgrade } } } - - return; } private static bool CanAccessFiles(List files, string folder) { // ensure files are not locked by another process - the shutdownTimeLimit defines the duration for app shutdown - bool canaccess = true; + bool canAccess = true; FileStream stream = null; int i = 0; - while (i < (files.Count - 1) && canaccess) + while (i < (files.Count - 1) && canAccess) { string filepath = Path.Combine(folder, Path.GetFileName(files[i])); int attempts = 0; bool locked = true; // try up to 30 times - while (attempts < 30 && locked == true) + while (attempts < 30 && locked) { try { - stream = System.IO.File.Open(filepath, FileMode.Open, FileAccess.Read, FileShare.None); + stream = File.Open(filepath, FileMode.Open, FileAccess.Read, FileShare.None); locked = false; } catch // file is locked by another process @@ -154,20 +152,17 @@ namespace Oqtane.Upgrade } finally { - if (stream != null) - { - stream.Close(); - } + stream?.Close(); } attempts += 1; } - if (locked && canaccess) + if (locked) { - canaccess = false; + canAccess = false; } i += 1; } - return canaccess; + return canAccess; } } } From cf6643aef32dc2a14689c21c5eb7064b0b591d91 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sun, 15 Mar 2020 15:18:32 +0100 Subject: [PATCH 072/265] Client fixes Client is partially done. 227 warnings left out of 1500 I like Rider --- Oqtane.Client/Modules/Admin/Files/Index.razor | 2 - Oqtane.Client/Modules/Admin/Pages/Add.razor | 148 +++++------ Oqtane.Client/Modules/Admin/Pages/Edit.razor | 230 +++++++++--------- Oqtane.Client/Modules/Admin/Site/Index.razor | 186 +++++++------- Oqtane.Client/Modules/Admin/Sites/Edit.razor | 162 ++++++------ Oqtane.Client/Modules/Admin/Sites/Index.razor | 34 +-- .../Modules/Controls/ActionDialog.razor | 18 +- .../Modules/Controls/ActionLink.razor | 38 +-- .../Modules/Controls/AuditInfo.razor | 32 +-- .../Modules/Controls/FileManager.razor | 171 +++++++------ Oqtane.Client/Modules/Controls/Pager.razor | 84 +++---- .../Modules/Controls/PermissionGrid.razor | 98 ++++---- .../Modules/Controls/RichTextEditor.razor | 78 +++--- .../Modules/Controls/TabControl.razor | 8 +- .../Modules/Controls/TriStateCheckBox.razor | 34 +-- Oqtane.Client/Modules/HtmlText/Edit.razor | 4 +- Oqtane.Client/Modules/ModuleBase.cs | 1 + Oqtane.Client/Program.cs | 4 +- .../IdentityAuthenticationStateProvider.cs | 2 +- Oqtane.Client/Services/AliasService.cs | 22 +- Oqtane.Client/Services/FileService.cs | 57 +++-- Oqtane.Client/Services/FolderService.cs | 47 ++-- Oqtane.Client/Services/InstallationService.cs | 10 +- .../Services/Interfaces/IAliasService.cs | 10 +- .../Services/Interfaces/IFileService.cs | 20 +- .../Services/Interfaces/IFolderService.cs | 12 +- .../Services/Interfaces/IJobLogService.cs | 8 +- .../Services/Interfaces/IJobService.cs | 12 +- .../Services/Interfaces/ILogService.cs | 9 +- .../Interfaces/IModuleDefinitionService.cs | 10 +- .../Services/Interfaces/IModuleService.cs | 14 +- .../Interfaces/INotificationService.cs | 10 +- .../Services/Interfaces/IPackageService.cs | 4 +- .../Services/Interfaces/IPageModuleService.cs | 13 +- .../Services/Interfaces/IPageService.cs | 18 +- .../Services/Interfaces/IProfileService.cs | 10 +- .../Services/Interfaces/IRoleService.cs | 10 +- .../Services/Interfaces/ISettingService.cs | 42 ++-- .../Services/Interfaces/ISiteService.cs | 10 +- .../Services/Interfaces/ITenantService.cs | 8 +- .../Services/Interfaces/IThemeService.cs | 8 +- .../Services/Interfaces/IUserRoleService.cs | 10 +- .../Services/Interfaces/IUserService.cs | 23 +- Oqtane.Client/Services/JobLogService.cs | 22 +- Oqtane.Client/Services/JobService.cs | 30 +-- Oqtane.Client/Services/LogService.cs | 29 +-- .../Services/ModuleDefinitionService.cs | 28 +-- Oqtane.Client/Services/ModuleService.cs | 30 +-- Oqtane.Client/Services/NotificationService.cs | 26 +- Oqtane.Client/Services/PackageService.cs | 10 +- Oqtane.Client/Services/PageModuleService.cs | 28 +-- Oqtane.Client/Services/PageService.cs | 58 ++--- Oqtane.Client/Services/ProfileService.cs | 24 +- Oqtane.Client/Services/RoleService.cs | 24 +- Oqtane.Client/Services/SettingService.cs | 106 ++++---- Oqtane.Client/Services/SiteService.cs | 22 +- Oqtane.Client/Services/TenantService.cs | 20 +- Oqtane.Client/Services/ThemeService.cs | 28 +-- Oqtane.Client/Services/UserRoleService.cs | 23 +- Oqtane.Client/Services/UserService.cs | 46 ++-- .../Themes/Controls/ControlPanel.razor | 1 + Oqtane.Client/Themes/ThemeBase.cs | 2 +- Oqtane.Client/UI/ContainerBuilder.razor | 10 +- Oqtane.Client/UI/Installer.razor | 102 ++++---- Oqtane.Client/UI/Interop.cs | 2 +- Oqtane.Client/UI/ModuleInstance.razor | 24 +- Oqtane.Client/UI/Pane.razor | 48 ++-- Oqtane.Client/UI/RichTextEditorInterop.cs | 10 +- Oqtane.Client/UI/SiteRouter.razor | 37 +-- Oqtane.Server/Controllers/AliasController.cs | 1 + Oqtane.Server/Controllers/FileController.cs | 1 + Oqtane.Server/Controllers/FolderController.cs | 1 + Oqtane.Server/Controllers/JobController.cs | 1 + Oqtane.Server/Controllers/JobLogController.cs | 1 + Oqtane.Server/Controllers/ModuleController.cs | 1 + .../Controllers/ModuleDefinitionController.cs | 1 + .../Controllers/NotificationController.cs | 1 + Oqtane.Server/Controllers/PageController.cs | 1 + .../Controllers/PageModuleController.cs | 1 + .../Controllers/ProfileController.cs | 1 + Oqtane.Server/Controllers/RoleController.cs | 1 + .../Controllers/SettingController.cs | 1 + Oqtane.Server/Controllers/SiteController.cs | 1 + Oqtane.Server/Controllers/TenantController.cs | 1 + Oqtane.Server/Controllers/ThemeController.cs | 1 + Oqtane.Server/Controllers/UserController.cs | 1 + .../Controllers/UserRoleController.cs | 1 + .../Infrastructure/Interfaces/ILogManager.cs | 1 + Oqtane.Server/Infrastructure/LogManager.cs | 1 + .../Controllers/HtmlTextController.cs | 1 + Oqtane.Server/Security/PermissionHandler.cs | 1 + Oqtane.Shared/Enums/LogFunction.cs | 2 +- 92 files changed, 1283 insertions(+), 1262 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Files/Index.razor b/Oqtane.Client/Modules/Admin/Files/Index.razor index 6dd27603..efe9c33a 100644 --- a/Oqtane.Client/Modules/Admin/Files/Index.razor +++ b/Oqtane.Client/Modules/Admin/Files/Index.razor @@ -54,7 +54,6 @@ List _folders; int _folderId = -1; List _files; - Uri _uri; protected override async Task OnParametersSetAsync() { @@ -66,7 +65,6 @@ _folderId = _folders[0].FolderId; await GetFiles(); } - _uri = new Uri(NavigationManager.Uri); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 17a785dc..e912cc9d 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -4,7 +4,7 @@ @inject IPageService PageService @inject IThemeService ThemeService -@if (Themes != null) +@if (_themeList != null) {
- + @@ -14,7 +14,7 @@
- + @@ -22,7 +22,7 @@
- +
- +
- +
- +
- +
- @@ -76,7 +76,7 @@ - @@ -90,16 +90,16 @@ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } public override string Actions { get { return "Add,Edit"; } } - int profileid = -1; - string name = ""; - string title = ""; - string description = ""; - string category = ""; - string vieworder = "0"; - string maxlength = "0"; - string defaultvalue = ""; - string isrequired = "False"; - string isprivate = "False"; + int _profileid = -1; + string _name = ""; + string _title = ""; + string _description = ""; + string _category = ""; + string _vieworder = "0"; + string _maxlength = "0"; + string _defaultvalue = ""; + string _isrequired = "False"; + string _isprivate = "False"; protected override async Task OnInitializedAsync() { @@ -107,25 +107,25 @@ { if (PageState.QueryString.ContainsKey("id")) { - profileid = Int32.Parse(PageState.QueryString["id"]); - Profile profile = await ProfileService.GetProfileAsync(profileid); + _profileid = Int32.Parse(PageState.QueryString["id"]); + Profile profile = await ProfileService.GetProfileAsync(_profileid); if (profile != null) { - name = profile.Name; - title = profile.Title; - description = profile.Description; - category = profile.Category; - vieworder = profile.ViewOrder.ToString(); - maxlength = profile.MaxLength.ToString(); - defaultvalue = profile.DefaultValue; - isrequired = profile.IsRequired.ToString(); - isprivate = profile.IsPrivate.ToString(); + _name = profile.Name; + _title = profile.Title; + _description = profile.Description; + _category = profile.Category; + _vieworder = profile.ViewOrder.ToString(); + _maxlength = profile.MaxLength.ToString(); + _defaultvalue = profile.DefaultValue; + _isrequired = profile.IsRequired.ToString(); + _isprivate = profile.IsPrivate.ToString(); } } } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Profile {ProfileId} {Error}", profileid, ex.Message); + await logger.LogError(ex, "Error Loading Profile {ProfileId} {Error}", _profileid, ex.Message); AddModuleMessage("Error Loading Profile", MessageType.Error); } } @@ -135,30 +135,30 @@ try { Profile profile; - if (profileid != -1) + if (_profileid != -1) { - profile = await ProfileService.GetProfileAsync(profileid); + profile = await ProfileService.GetProfileAsync(_profileid); } else { profile = new Profile(); } - profile.Name = name; - profile.Title = title; - profile.Description = description; - profile.Category = category; - profile.ViewOrder = int.Parse(vieworder); - profile.MaxLength = int.Parse(maxlength); - profile.DefaultValue = defaultvalue; - profile.IsRequired = (isrequired == null ? false : Boolean.Parse(isrequired)); - profile.IsPrivate = (isprivate == null ? false : Boolean.Parse(isprivate)); + profile.Name = _name; + profile.Title = _title; + profile.Description = _description; + profile.Category = _category; + profile.ViewOrder = int.Parse(_vieworder); + profile.MaxLength = int.Parse(_maxlength); + profile.DefaultValue = _defaultvalue; + profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired)); + profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate)); profile = await ProfileService.UpdateProfileAsync(profile); await logger.LogInformation("Profile Saved {Profile}", profile); NavigationManager.NavigateTo(NavigateUrl()); } catch (Exception ex) { - await logger.LogError(ex, "Error Saving Profile {ProfleId} {Error}", profileid, ex.Message); + await logger.LogError(ex, "Error Saving Profile {ProfleId} {Error}", _profileid, ex.Message); AddModuleMessage("Error Saving Profile", MessageType.Error); } } diff --git a/Oqtane.Client/Modules/Admin/Profiles/Index.razor b/Oqtane.Client/Modules/Admin/Profiles/Index.razor index b362895a..593ccca4 100644 --- a/Oqtane.Client/Modules/Admin/Profiles/Index.razor +++ b/Oqtane.Client/Modules/Admin/Profiles/Index.razor @@ -2,7 +2,7 @@ @inherits ModuleBase @inject IProfileService ProfileService -@if (Profiles == null) +@if (_profiles == null) {

Loading...

} @@ -10,7 +10,7 @@ else { - +
           -
- +
-
- @@ -36,7 +36,7 @@ - @@ -49,40 +49,40 @@ @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - int roleid; - string name = ""; - string description = ""; - string isautoassigned = "False"; - string issystem = "False"; + int _roleid; + string _name = ""; + string _description = ""; + string _isautoassigned = "False"; + string _issystem = "False"; protected override async Task OnInitializedAsync() { try { - roleid = Int32.Parse(PageState.QueryString["id"]); - Role role = await RoleService.GetRoleAsync(roleid); + _roleid = Int32.Parse(PageState.QueryString["id"]); + Role role = await RoleService.GetRoleAsync(_roleid); if (role != null) { - name = role.Name; - description = role.Description; - isautoassigned = role.IsAutoAssigned.ToString(); - issystem = role.IsSystem.ToString(); + _name = role.Name; + _description = role.Description; + _isautoassigned = role.IsAutoAssigned.ToString(); + _issystem = role.IsSystem.ToString(); } } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Role {RoleId} {Error}", roleid, ex.Message); + await logger.LogError(ex, "Error Loading Role {RoleId} {Error}", _roleid, ex.Message); AddModuleMessage("Error Loading Role", MessageType.Error); } } private async Task SaveRole() { - Role role = await RoleService.GetRoleAsync(roleid); - role.Name = name; - role.Description = description; - role.IsAutoAssigned = (isautoassigned == null ? false : Boolean.Parse(isautoassigned)); - role.IsSystem = (issystem == null ? false : Boolean.Parse(issystem)); + Role role = await RoleService.GetRoleAsync(_roleid); + role.Name = _name; + role.Description = _description; + role.IsAutoAssigned = (_isautoassigned != null && Boolean.Parse(_isautoassigned)); + role.IsSystem = (_issystem != null && Boolean.Parse(_issystem)); try { diff --git a/Oqtane.Client/Modules/Admin/Roles/Index.razor b/Oqtane.Client/Modules/Admin/Roles/Index.razor index 79124760..29378752 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Index.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Index.razor @@ -2,7 +2,7 @@ @inherits ModuleBase @inject IRoleService RoleService -@if (Roles == null) +@if (_roles == null) {

Loading...

} @@ -10,7 +10,7 @@ else { - +
    -
- +
-
- - @foreach (KeyValuePair panelayout in panelayouts) + @foreach (KeyValuePair panelayout in _panelayouts) { } @@ -77,23 +77,23 @@ else - - @foreach (KeyValuePaircontainer in containers) + @foreach (KeyValuePaircontainer in _containers) { }
- +
- +
@@ -12,7 +12,7 @@ @@ -20,7 +20,7 @@ @@ -30,7 +30,7 @@ @@ -137,7 +137,7 @@
- +
- +
- - @if (children != null && children.Count > 0) + @if (_children != null && _children.Count > 0) { } - @if (children != null && children.Count > 0 && (insert == "<" || insert == ">")) + @if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">")) { - - @foreach (Page page in children) + @foreach (Page page in _children) { } @@ -68,7 +68,7 @@ - @@ -79,7 +79,7 @@ - @@ -90,7 +90,7 @@ - @@ -103,7 +103,7 @@ - - @foreach (KeyValuePair panelayout in panelayouts) + @foreach (KeyValuePair panelayout in _panelayouts) { } @@ -129,7 +129,7 @@ - +
- +
@@ -148,45 +148,45 @@ @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - Dictionary themes = new Dictionary(); - Dictionary panelayouts = new Dictionary(); + Dictionary _themes = new Dictionary(); + Dictionary _panelayouts = new Dictionary(); - List Themes; - List pages; - string name; - string path = ""; - string parentid; - string insert = ">>"; - List children; - int childid = -1; - string isnavigation = "True"; - string ispersonalizable = "False"; - string mode = "view"; - string themetype = ""; - string layouttype = ""; - string icon = ""; - string permissions = ""; + List _themeList; + List _pageList; + string _name; + string _path = ""; + string _parentid; + string _insert = ">>"; + List _children; + int _childid = -1; + string _isnavigation = "True"; + string _ispersonalizable = "False"; + string _mode = "view"; + string _themetype = ""; + string _layouttype = ""; + string _icon = ""; + string _permissions = ""; - PermissionGrid permissiongrid; + PermissionGrid _permissionGrid; protected override async Task OnInitializedAsync() { try { - Themes = await ThemeService.GetThemesAsync(); - pages = PageState.Pages; - children = PageState.Pages.Where(item => item.ParentId == null).ToList(); + _themeList = await ThemeService.GetThemesAsync(); + _pageList = PageState.Pages; + _children = PageState.Pages.Where(item => item.ParentId == null).ToList(); - themes = ThemeService.GetThemeTypes(Themes); - themetype = PageState.Site.DefaultThemeType; + _themes = ThemeService.GetThemeTypes(_themeList); + _themetype = PageState.Site.DefaultThemeType; - panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); - layouttype = PageState.Site.DefaultLayoutType; + _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); + _layouttype = PageState.Site.DefaultLayoutType; List permissionstrings = new List(); permissionstrings.Add(new PermissionString { PermissionName = PermissionNames.View, Permissions = Constants.AdminRole }); permissionstrings.Add(new PermissionString { PermissionName = PermissionNames.Edit, Permissions = Constants.AdminRole }); - permissions = UserSecurity.SetPermissionStrings(permissionstrings); + _permissions = UserSecurity.SetPermissionStrings(permissionstrings); } catch (Exception ex) { @@ -199,20 +199,20 @@ { try { - parentid = (string)e.Value; - if (parentid == "-1") + _parentid = (string)e.Value; + if (_parentid == "-1") { - children = PageState.Pages.Where(item => item.ParentId == null).ToList(); + _children = PageState.Pages.Where(item => item.ParentId == null).ToList(); } else { - children = PageState.Pages.Where(item => item.ParentId == int.Parse(parentid)).ToList(); + _children = PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)).ToList(); } StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", parentid, ex.Message); + await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message); AddModuleMessage("Error Loading Child Pages For Parent", MessageType.Error); } } @@ -221,20 +221,20 @@ { try { - themetype = (string)e.Value; - if (themetype != "") + _themetype = (string)e.Value; + if (_themetype != "") { - panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); + _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); } else { - panelayouts = new Dictionary(); + _panelayouts = new Dictionary(); } StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", themetype, ex.Message); + await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); AddModuleMessage("Error Loading Pane Layouts For Theme", MessageType.Error); } } @@ -244,61 +244,61 @@ Page page = null; try { - if (name != "" && !string.IsNullOrEmpty(themetype) && (panelayouts.Count == 0 || !string.IsNullOrEmpty(layouttype))) + if (_name != "" && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype))) { page = new Page(); page.SiteId = PageState.Page.SiteId; - page.Name = name; - if (path == "") + page.Name = _name; + if (_path == "") { - path = name; + _path = _name; } - if (path.Contains("/")) + if (_path.Contains("/")) { - path = path.Substring(path.LastIndexOf("/") + 1); + _path = _path.Substring(_path.LastIndexOf("/") + 1); } - if (string.IsNullOrEmpty(parentid)) + if (string.IsNullOrEmpty(_parentid)) { page.ParentId = null; - page.Path = Utilities.GetFriendlyUrl(path); + page.Path = Utilities.GetFriendlyUrl(_path); } else { - page.ParentId = Int32.Parse(parentid); + page.ParentId = Int32.Parse(_parentid); Page parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault(); if (parent.Path == "") { - page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(path); + page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path); } else { - page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(path); + page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); } } Page child; - switch (insert) + switch (_insert) { case "<<": page.Order = 0; break; case "<": - child = PageState.Pages.Where(item => item.PageId == childid).FirstOrDefault(); + 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(); + 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.EditMode = (mode == "edit" ? true : false); - page.ThemeType = themetype; - page.LayoutType = (layouttype == null ? "" : layouttype); - page.Icon = (icon == null ? "" : icon); - page.Permissions = permissiongrid.GetPermissions(); + page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation)); + page.EditMode = (_mode == "edit" ? true : false); + page.ThemeType = _themetype; + page.LayoutType = (_layouttype == null ? "" : _layouttype); + page.Icon = (_icon == null ? "" : _icon); + page.Permissions = _permissionGrid.GetPermissions(); if (page.ThemeType == PageState.Site.DefaultThemeType) { @@ -308,7 +308,7 @@ { page.LayoutType = ""; } - page.IsPersonalizable = (ispersonalizable == null ? false : Boolean.Parse(ispersonalizable)); + page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable)); page.UserId = null; page = await PageService.AddPageAsync(page); diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index cad64681..ba2eef54 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -4,7 +4,7 @@ @inject IPageService PageService @inject IThemeService ThemeService -@if (Themes != null) +@if (_themeList != null) { @@ -12,7 +12,7 @@ @@ -20,7 +20,7 @@ @@ -30,9 +30,9 @@ @@ -155,7 +155,7 @@
- +
- +
- + @if (_parentid == _currentparentid) { } - @if (children != null && children.Count > 0) + @if (_children != null && _children.Count > 0) { } - @if (children != null && children.Count > 0 && (insert == "<" || insert == ">")) + @if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">")) { - - @foreach (Page page in children) + @foreach (Page page in _children) { } @@ -79,7 +79,7 @@ - @@ -90,7 +90,7 @@ - @@ -101,7 +101,7 @@ - @@ -114,9 +114,9 @@ - - @foreach (KeyValuePair panelayout in panelayouts) + @foreach (KeyValuePair panelayout in _panelayouts) { } @@ -147,7 +147,7 @@ - +
- +
@@ -163,89 +163,91 @@ Cancel

- + } @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - Dictionary themes = new Dictionary(); - Dictionary panelayouts = new Dictionary(); + Dictionary _themes = new Dictionary(); + Dictionary _panelayouts = new Dictionary(); - List Themes; - List pages; - int PageId; - string name; - string path; - string currentparentid; - string parentid; - string insert = "="; - List children; - int childid = -1; - string isnavigation; - string ispersonalizable; - string mode; - string themetype; - string layouttype; - string icon; - string permissions; - string createdby; - DateTime createdon; - string modifiedby; - DateTime modifiedon; - string deletedby; - DateTime? deletedon; + List _themeList; + List _pageList; + int _pageId; + string _name; + string _path; + string _currentparentid; + string _parentid; + string _insert = "="; + List _children; + int _childid = -1; + string _isnavigation; + string _ispersonalizable; + string _mode; + string _themetype; + string _layouttype; + string _icon; + string _permissions; + string _createdby; + DateTime _createdon; + string _modifiedby; + DateTime _modifiedon; + string _deletedby; + DateTime? _deletedon; - PermissionGrid permissiongrid; +#pragma warning disable 649 + PermissionGrid _permissionGrid; +#pragma warning restore 649 protected override async Task OnInitializedAsync() { try { - Themes = await ThemeService.GetThemesAsync(); - pages = PageState.Pages; - children = PageState.Pages.Where(item => item.ParentId == null).ToList(); + _themeList = await ThemeService.GetThemesAsync(); + _pageList = PageState.Pages; + _children = PageState.Pages.Where(item => item.ParentId == null).ToList(); - themes = ThemeService.GetThemeTypes(Themes); + _themes = ThemeService.GetThemeTypes(_themeList); - PageId = Int32.Parse(PageState.QueryString["id"]); - Page page = PageState.Pages.FirstOrDefault(item => item.PageId == PageId); + _pageId = Int32.Parse(PageState.QueryString["id"]); + Page page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); if (page != null) { - name = page.Name; - path = page.Path; - if (path.Contains("/")) + _name = page.Name; + _path = page.Path; + if (_path.Contains("/")) { - path = path.Substring(path.LastIndexOf("/") + 1); + _path = _path.Substring(_path.LastIndexOf("/") + 1); } if (page.ParentId == null) { - parentid = ""; + _parentid = ""; } else { - parentid = page.ParentId.ToString(); + _parentid = page.ParentId.ToString(); } - currentparentid = parentid; - isnavigation = page.IsNavigation.ToString(); - ispersonalizable = page.IsPersonalizable.ToString(); - mode = (page.EditMode) ? "edit" : "view"; - themetype = page.ThemeType; - panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); - layouttype = page.LayoutType; - icon = page.Icon; - permissions = page.Permissions; - createdby = page.CreatedBy; - createdon = page.CreatedOn; - modifiedby = page.ModifiedBy; - modifiedon = page.ModifiedOn; - deletedby = page.DeletedBy; - deletedon = page.DeletedOn; + _currentparentid = _parentid; + _isnavigation = page.IsNavigation.ToString(); + _ispersonalizable = page.IsPersonalizable.ToString(); + _mode = (page.EditMode) ? "edit" : "view"; + _themetype = page.ThemeType; + _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); + _layouttype = page.LayoutType; + _icon = page.Icon; + _permissions = page.Permissions; + _createdby = page.CreatedBy; + _createdon = page.CreatedOn; + _modifiedby = page.ModifiedBy; + _modifiedon = page.ModifiedOn; + _deletedby = page.DeletedBy; + _deletedon = page.DeletedOn; } } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Page {PageId} {Error}", PageId, ex.Message); + await logger.LogError(ex, "Error Loading Page {PageId} {Error}", _pageId, ex.Message); AddModuleMessage("Error Loading Page", MessageType.Error); } } @@ -254,28 +256,28 @@ { try { - parentid = (string)e.Value; - if (parentid == "-1") + _parentid = (string)e.Value; + if (_parentid == "-1") { - children = PageState.Pages.Where(item => item.ParentId == null).ToList(); + _children = PageState.Pages.Where(item => item.ParentId == null).ToList(); } else { - children = PageState.Pages.Where(item => item.ParentId == int.Parse(parentid)).ToList(); + _children = PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)).ToList(); } - if (parentid == currentparentid) + if (_parentid == _currentparentid) { - insert = "="; + _insert = "="; } else { - insert = ">>"; + _insert = ">>"; } StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", parentid, ex.Message); + await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message); AddModuleMessage("Error Loading Child Pages For Parent", MessageType.Error); } } @@ -284,20 +286,20 @@ { try { - themetype = (string)e.Value; - if (themetype != "") + _themetype = (string)e.Value; + if (_themetype != "") { - panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); + _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); } else { - panelayouts = new Dictionary(); + _panelayouts = new Dictionary(); } StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", themetype, ex.Message); + await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); AddModuleMessage("Error Loading Pane Layouts For Theme", MessageType.Error); } } @@ -307,52 +309,52 @@ Page page = null; try { - if (name != "" && !string.IsNullOrEmpty(themetype) && (panelayouts.Count == 0 || !string.IsNullOrEmpty(layouttype))) + if (_name != "" && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype))) { - page = PageState.Pages.Where(item => item.PageId == PageId).FirstOrDefault(); + page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); string currentPath = page.Path; - page.Name = name; - if (path == "" && name.ToLower() != "home") + page.Name = _name; + if (_path == "" && _name.ToLower() != "home") { - path = name; + _path = _name; } - if (path.Contains("/")) + if (_path.Contains("/")) { - path = path.Substring(path.LastIndexOf("/") + 1); + _path = _path.Substring(_path.LastIndexOf("/") + 1); } - if (string.IsNullOrEmpty(parentid)) + if (string.IsNullOrEmpty(_parentid)) { page.ParentId = null; - page.Path = Utilities.GetFriendlyUrl(path); + page.Path = Utilities.GetFriendlyUrl(_path); } else { - page.ParentId = Int32.Parse(parentid); + page.ParentId = Int32.Parse(_parentid); Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId); if (parent.Path == "") { - page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(path); + page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path); } else { - page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(path); + page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); } } - if (insert != "=") + if (_insert != "=") { Page child; - switch (insert) + switch (_insert) { case "<<": page.Order = 0; break; case "<": - child = PageState.Pages.FirstOrDefault(item => item.PageId == childid); + 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); + child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid); if (child != null) page.Order = child.Order + 1; break; case ">>": @@ -360,12 +362,12 @@ break; } } - page.IsNavigation = (isnavigation == null ? true : Boolean.Parse(isnavigation)); - page.EditMode = (mode == "edit" ? true : false); - page.ThemeType = themetype; - page.LayoutType = layouttype ?? ""; - page.Icon = icon ?? ""; - page.Permissions = permissiongrid.GetPermissions(); + page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation)); + page.EditMode = (_mode == "edit"); + page.ThemeType = _themetype; + page.LayoutType = _layouttype ?? ""; + page.Icon = _icon ?? ""; + page.Permissions = _permissionGrid.GetPermissions(); if (page.ThemeType == PageState.Site.DefaultThemeType) { @@ -375,22 +377,22 @@ { page.LayoutType = ""; } - page.IsPersonalizable = (ispersonalizable != null && Boolean.Parse(ispersonalizable)); + page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable)); page.UserId = null; page = await PageService.UpdatePageAsync(page); await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId); - if (currentparentid == "") + if (_currentparentid == "") { await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, null); } else { - await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, int.Parse(currentparentid)); + await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, int.Parse(_currentparentid)); } // update child paths - if (parentid != currentparentid) + if (_parentid != _currentparentid) { foreach (Page p in PageState.Pages.Where(item => item.Path.StartsWith(currentPath))) { diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index b3692a49..a6bdcac0 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -7,7 +7,7 @@ @inject IThemeService ThemeService @inject ISettingService SettingService -@if (themes != null) +@if (_themes != null) { @@ -15,7 +15,7 @@ @@ -23,7 +23,7 @@ @@ -31,7 +31,7 @@ @@ -39,7 +39,7 @@ @@ -49,9 +49,9 @@ @@ -122,7 +122,7 @@ @@ -130,7 +130,7 @@ @@ -138,7 +138,7 @@ @@ -146,7 +146,7 @@
- +
- +
- +
- +
- - @foreach (KeyValuePair panelayout in panelayouts) + @foreach (KeyValuePair panelayout in _panelayouts) { } @@ -82,9 +82,9 @@ - - @foreach (KeyValuePair container in containers) + @foreach (KeyValuePair container in _containers) { } @@ -96,7 +96,7 @@ - @@ -114,7 +114,7 @@ - +
- +
- +
- +
- +
@@ -156,85 +156,85 @@ Cancel

- + } @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - Dictionary themes; - Dictionary panelayouts; - Dictionary containers; + Dictionary _themes; + Dictionary _panelayouts; + Dictionary _containers; - List Themes; - string name = ""; - List tenants; - string tenant = ""; - List aliases; - string urls = ""; - int logofileid = -1; - FileManager filemanager; - string themetype; - string layouttype; - string containertype; + List _themeList; + string _name = ""; + List _tenantList; + string _tenant = ""; + List _aliasList; + string _urls = ""; + int _logofileid = -1; + FileManager _filemanager; + string _themetype; + string _layouttype; + string _containertype; - string smtphost = ""; - string smtpport = ""; - string smtpssl = ""; - string smtpusername = ""; - string smtppassword = ""; + string _smtphost = ""; + string _smtpport = ""; + string _smtpssl = ""; + string _smtpusername = ""; + string _smtppassword = ""; - string createdby; - DateTime createdon; - string modifiedby; - DateTime modifiedon; - string deletedby; - DateTime? deletedon; - string isdeleted; + string _createdby; + DateTime _createdon; + string _modifiedby; + DateTime _modifiedon; + string _deletedby; + DateTime? _deletedon; + string _isdeleted; protected override async Task OnInitializedAsync() { try { - Themes = await ThemeService.GetThemesAsync(); - aliases = await AliasService.GetAliasesAsync(); + _themeList = await ThemeService.GetThemesAsync(); + _aliasList = await AliasService.GetAliasesAsync(); Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId, PageState.Alias); if (site != null) { - name = site.Name; - tenants = await TenantService.GetTenantsAsync(); - tenant = tenants.Find(item => item.TenantId == site.TenantId).Name; - foreach (Alias alias in aliases.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList()) + _name = site.Name; + _tenantList = await TenantService.GetTenantsAsync(); + _tenant = _tenantList.Find(item => item.TenantId == site.TenantId).Name; + foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList()) { - urls += alias.Name + "\n"; + _urls += alias.Name + "\n"; } if (site.LogoFileId != null) { - logofileid = site.LogoFileId.Value; + _logofileid = site.LogoFileId.Value; } - themetype = site.DefaultThemeType; - panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); - layouttype = site.DefaultLayoutType; - containertype = site.DefaultContainerType; + _themetype = site.DefaultThemeType; + _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); + _layouttype = site.DefaultLayoutType; + _containertype = site.DefaultContainerType; Dictionary settings = await SettingService.GetSiteSettingsAsync(site.SiteId); - smtphost = SettingService.GetSetting(settings, "SMTPHost", ""); - smtpport = SettingService.GetSetting(settings, "SMTPPort", ""); - smtpssl = SettingService.GetSetting(settings, "SMTPSSL", ""); - smtpusername = SettingService.GetSetting(settings, "SMTPUsername", ""); - smtppassword = SettingService.GetSetting(settings, "SMTPPassword", ""); + _smtphost = SettingService.GetSetting(settings, "SMTPHost", ""); + _smtpport = SettingService.GetSetting(settings, "SMTPPort", ""); + _smtpssl = SettingService.GetSetting(settings, "SMTPSSL", ""); + _smtpusername = SettingService.GetSetting(settings, "SMTPUsername", ""); + _smtppassword = SettingService.GetSetting(settings, "SMTPPassword", ""); - createdby = site.CreatedBy; - createdon = site.CreatedOn; - modifiedby = site.ModifiedBy; - modifiedon = site.ModifiedOn; - deletedby = site.DeletedBy; - deletedon = site.DeletedOn; - isdeleted = site.IsDeleted.ToString(); + _createdby = site.CreatedBy; + _createdon = site.CreatedOn; + _modifiedby = site.ModifiedBy; + _modifiedon = site.ModifiedOn; + _deletedby = site.DeletedBy; + _deletedon = site.DeletedOn; + _isdeleted = site.IsDeleted.ToString(); } - themes = ThemeService.GetThemeTypes(Themes); - containers = ThemeService.GetContainerTypes(Themes); + _themes = ThemeService.GetThemeTypes(_themeList); + _containers = ThemeService.GetContainerTypes(_themeList); } catch (Exception ex) { @@ -247,20 +247,20 @@ { try { - themetype = (string)e.Value; - if (themetype != "") + _themetype = (string)e.Value; + if (_themetype != "") { - panelayouts = ThemeService.GetPaneLayoutTypes(Themes, themetype); + _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); } else { - panelayouts = new Dictionary(); + _panelayouts = new Dictionary(); } StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", themetype, ex.Message); + await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); AddModuleMessage("Error Loading Pane Layouts For Theme", MessageType.Error); } } @@ -269,12 +269,12 @@ { try { - if (name != "" && urls != "" && !string.IsNullOrEmpty(themetype) && (panelayouts.Count == 0 || !string.IsNullOrEmpty(layouttype)) && !string.IsNullOrEmpty(containertype)) + if (_name != "" && _urls != "" && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)) && !string.IsNullOrEmpty(_containertype)) { bool unique = true; - foreach (string name in urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { - if (aliases.Exists(item => item.Name == name && item.SiteId != PageState.Alias.SiteId && item.TenantId != PageState.Alias.TenantId)) + if (_aliasList.Exists(item => item.Name == name && item.SiteId != PageState.Alias.SiteId && item.TenantId != PageState.Alias.TenantId)) { unique = false; } @@ -284,23 +284,23 @@ Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId, PageState.Alias); if (site != null) { - site.Name = name; + site.Name = _name; site.LogoFileId = null; - int logofileid = filemanager.GetFileId(); + int logofileid = _filemanager.GetFileId(); if (logofileid != -1) { site.LogoFileId = logofileid; } - site.DefaultThemeType = themetype; - site.DefaultLayoutType = (layouttype == null ? "" : layouttype); - site.DefaultContainerType = containertype; - site.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted)); + site.DefaultThemeType = _themetype; + site.DefaultLayoutType = (_layouttype == null ? "" : _layouttype); + site.DefaultContainerType = _containertype; + site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted)); site = await SiteService.UpdateSiteAsync(site, PageState.Alias); - urls = urls.Replace("\n", ","); - string[] names = urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - foreach (Alias alias in aliases.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList()) + _urls = _urls.Replace("\n", ","); + string[] names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList()) { if (!names.Contains(alias.Name)) { @@ -309,7 +309,7 @@ } foreach (string name in names) { - if (!aliases.Exists(item => item.Name == name)) + if (!_aliasList.Exists(item => item.Name == name)) { Alias alias = new Alias(); alias.Name = name; @@ -320,11 +320,11 @@ } Dictionary settings = await SettingService.GetSiteSettingsAsync(site.SiteId); - SettingService.SetSetting(settings, "SMTPHost", smtphost); - SettingService.SetSetting(settings, "SMTPPort", smtpport); - SettingService.SetSetting(settings, "SMTPSSL", smtpssl); - SettingService.SetSetting(settings, "SMTPUsername", smtpusername); - SettingService.SetSetting(settings, "SMTPPassword", smtppassword); + SettingService.SetSetting(settings, "SMTPHost", _smtphost); + SettingService.SetSetting(settings, "SMTPPort", _smtpport); + SettingService.SetSetting(settings, "SMTPSSL", _smtpssl); + SettingService.SetSetting(settings, "SMTPUsername", _smtpusername); + SettingService.SetSetting(settings, "SMTPPassword", _smtppassword); await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId); await logger.LogInformation("Site Saved {Site}", site); diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor index bc3b499c..9ff55903 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor @@ -6,7 +6,7 @@ @inject IAliasService AliasService @inject IThemeService ThemeService -@if (themes != null) +@if (_themes != null) { @@ -14,7 +14,7 @@ @@ -22,7 +22,7 @@ @@ -30,7 +30,7 @@
- +
- +
- +
- + - @@ -33,10 +33,10 @@
- + - diff --git a/Oqtane.Client/Modules/Controls/Label.razor b/Oqtane.Client/Modules/Controls/Label.razor new file mode 100644 index 00000000..6152baa1 --- /dev/null +++ b/Oqtane.Client/Modules/Controls/Label.razor @@ -0,0 +1,42 @@ +@namespace Oqtane.Modules.Controls +@inherits ModuleBase + +@if (!string.IsNullOrEmpty(HelpText)) +{ + @((MarkupString)_openLabel)@ChildContent@((MarkupString)_closeLabel) +} +else +{ + @((MarkupString)_openLabel)@ChildContent@((MarkupString)_closeLabel) +} + +@code { + string _openLabel = ""; + string _closeLabel = ""; + + [Parameter] + public RenderFragment ChildContent { get; set; } // required - the title of the label + + [Parameter] + public string For { get; set; } // optional - the id of the associated input control for accessibility + + [Parameter] + public string Class { get; set; } // optional - the class for the label ( ie. control-label ) + + [Parameter] + public string HelpText { get; set; } // optional - tooltip for this label + + protected override void OnParametersSet() + { + _openLabel = "Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=KugC=K=Jss|El70=7v+K~y+TV`M-A zCDjcauU@|LU}R*h`S9_R@vmRMz~XFdtPFwz0%!mJ{oBIJ%d=zJgzg_;2_S$Q(A?Rp zcJ|zbn%}?w#QyvD4=RcZn3$Lt1O)|7OG!#3b+y!A0f~VP0BOE{|U_1sNIt z0~LPx@%_irZ{NSa7ZBk0|M>Bf=Kufy!RooWxt>c)OF2yJYQ4n>a`v4&cW3?l`70JA z$;!&gAR#W=J!?vT8Hf)9X*oqvkDol92xN+Y_(0bz77-RoV*>dZ2F- z$y^3Tp=E1*)mo7FnR6HHe*E|$2v!6HKyNTWJcI?Xv$Nln5ErvwvvmIZkmy8SU?A@P z@#E(lkS1in#LUbLWuk-sKiN4r99p~k1Azgv0vH1>Pzg9-VPRol5)u?V3+1B#V9e+} zc<^ZPeIWk+R#=>f)8 z3zM*r;0~Zkr=j9-08I1Gv(l12f-o>9c0q;VK!BhBw2X}O4(8J*j{OM?4ml1?6oJ4P z;f2aG{QU7#&euOsN?u8g@6FqH*+3xyhzukttE(!9v^Um1gJ-7tmJXe}_a4l9_x^(^ zdPu_n$k`y9C8Z>idRiM0nF$8sQ?ogM0p`KU!BGP=$QbBHusA5Rftl;<*RNk&6crVA f05wCiGXnzviHabpJT*b900000NkvXXu0mjfSXFW4 literal 0 HcmV?d00001 diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index 26457c53..3825d454 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -8,6 +8,7 @@ using Oqtane.Shared; using System; using System.Diagnostics; using Oqtane.Infrastructure.Interfaces; +using Microsoft.Extensions.Caching.Memory; namespace Oqtane.Infrastructure { @@ -15,11 +16,13 @@ namespace Oqtane.Infrastructure { private readonly IHostApplicationLifetime _hostApplicationLifetime; private readonly IWebHostEnvironment _environment; + private readonly IMemoryCache _cache; - public InstallationManager(IHostApplicationLifetime hostApplicationLifetime, IWebHostEnvironment environment) + public InstallationManager(IHostApplicationLifetime hostApplicationLifetime, IWebHostEnvironment environment, IMemoryCache cache) { _hostApplicationLifetime = hostApplicationLifetime; _environment = environment; + _cache = cache; } public void InstallPackages(string folders, bool restart) @@ -107,8 +110,15 @@ namespace Oqtane.Infrastructure if (install && restart) { - // restart application - RestartApplication(); + if (restart) + { + RestartApplication(); + } + else + { + _cache.Remove("moduledefinitions"); + _cache.Remove("jobs"); + } } } diff --git a/Oqtane.Server/Repository/JobRepository.cs b/Oqtane.Server/Repository/JobRepository.cs index 2ea8b7d1..cbe51c19 100644 --- a/Oqtane.Server/Repository/JobRepository.cs +++ b/Oqtane.Server/Repository/JobRepository.cs @@ -31,6 +31,7 @@ namespace Oqtane.Repository { _db.Job.Add(job); _db.SaveChanges(); + _cache.Remove("jobs"); return job; } @@ -38,6 +39,7 @@ namespace Oqtane.Repository { _db.Entry(job).State = EntityState.Modified; _db.SaveChanges(); + _cache.Remove("jobs"); return job; } @@ -51,6 +53,7 @@ namespace Oqtane.Repository Job job = _db.Job.Find(jobId); _db.Job.Remove(job); _db.SaveChanges(); + _cache.Remove("jobs"); } } } diff --git a/Oqtane.Server/wwwroot/css/app.css b/Oqtane.Server/wwwroot/css/app.css index e4253c17..99f6e73e 100644 --- a/Oqtane.Server/wwwroot/css/app.css +++ b/Oqtane.Server/wwwroot/css/app.css @@ -110,3 +110,68 @@ app { padding: inherit; vertical-align: inherit; } + +/* Tooltips */ +.app-tooltip { + cursor: help; + position: relative; +} + + .app-tooltip::before, + .app-tooltip::after { + left: 50%; + opacity: 0; + position: absolute; + z-index: -100; + } + + .app-tooltip:hover::before, + .app-tooltip:focus::before, + .app-tooltip:hover::after, + .app-tooltip:focus::after { + opacity: 1; + transform: scale(1) translateY(0); + z-index: 100; + } + + .app-tooltip::before { + border-style: solid; + border-width: 1em 0.75em 0 0.75em; + border-color: #3E474F transparent transparent transparent; + bottom: 100%; + content: ""; + margin-left: -0.5em; + transition: all .65s cubic-bezier(.84,-0.18,.31,1.26), opacity .65s .5s; + transform: scale(.6) translateY(-90%); + } + + .app-tooltip:hover::before, + .app-tooltip:focus::before { + transition: all .65s cubic-bezier(.84,-0.18,.31,1.26) .2s; + } + + .app-tooltip::after { + background: #3E474F; + border-radius: .25em; + bottom: 180%; + color: #EDEFF0; + content: attr(data-tip); + margin-left: -8.75em; + padding: 1em; + transition: all .65s cubic-bezier(.84,-0.18,.31,1.26) .2s; + transform: scale(.6) translateY(50%); + width: 17.5em; + } + + .app-tooltip:hover::after, + .app-tooltip:focus::after { + transition: all .65s cubic-bezier(.84,-0.18,.31,1.26); + } + +@media (max-width: 760px) { + .app-tooltip::after { + font-size: .75em; + margin-left: -5em; + width: 10em; + } +} \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/images/help.png b/Oqtane.Server/wwwroot/images/help.png new file mode 100644 index 0000000000000000000000000000000000000000..f380be588a5c9285328484aeabd729734626286e GIT binary patch literal 801 zcmV++1K#|JP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=KugC=K=Jss|El70=7v+K~y+TV`M-A zCDjcauU@|LU}R*h`S9_R@vmRMz~XFdtPFwz0%!mJ{oBIJ%d=zJgzg_;2_S$Q(A?Rp zcJ|zbn%}?w#QyvD4=RcZn3$Lt1O)|7OG!#3b+y!A0f~VP0BOE{|U_1sNIt z0~LPx@%_irZ{NSa7ZBk0|M>Bf=Kufy!RooWxt>c)OF2yJYQ4n>a`v4&cW3?l`70JA z$;!&gAR#W=J!?vT8Hf)9X*oqvkDol92xN+Y_(0bz77-RoV*>dZ2F- z$y^3Tp=E1*)mo7FnR6HHe*E|$2v!6HKyNTWJcI?Xv$Nln5ErvwvvmIZkmy8SU?A@P z@#E(lkS1in#LUbLWuk-sKiN4r99p~k1Azgv0vH1>Pzg9-VPRol5)u?V3+1B#V9e+} zc<^ZPeIWk+R#=>f)8 z3zM*r;0~Zkr=j9-08I1Gv(l12f-o>9c0q;VK!BhBw2X}O4(8J*j{OM?4ml1?6oJ4P z;f2aG{QU7#&euOsN?u8g@6FqH*+3xyhzukttE(!9v^Um1gJ-7tmJXe}_a4l9_x^(^ zdPu_n$k`y9C8Z>idRiM0nF$8sQ?ogM0p`KU!BGP=$QbBHusA5Rftl;<*RNk&6crVA f05wCiGXnzviHabpJT*b900000NkvXXu0mjfSXFW4 literal 0 HcmV?d00001 From 65d39974b52000ed51eea1b784d917db5bf55380 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Wed, 18 Mar 2020 08:53:26 +0100 Subject: [PATCH 075/265] Magic Strings - EntityNames --- Oqtane.Client/Modules/Admin/Files/Edit.razor | 2 +- .../Modules/Admin/ModuleDefinitions/Edit.razor | 2 +- .../Modules/Admin/Modules/Settings.razor | 2 +- Oqtane.Client/Modules/Admin/Pages/Add.razor | 2 +- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 2 +- Oqtane.Client/Services/FolderService.cs | 2 +- Oqtane.Client/Services/ModuleDefinitionService.cs | 2 +- Oqtane.Client/UI/SiteRouter.razor | 6 +++--- .../Infrastructure/Jobs/NotificationJob.cs | 2 +- Oqtane.Server/Repository/FileRepository.cs | 5 +++-- Oqtane.Server/Repository/FolderRepository.cs | 13 +++++++------ .../Repository/ModuleDefinitionRepository.cs | 12 ++++++------ Oqtane.Server/Repository/PageRepository.cs | 15 ++++++++------- Oqtane.Server/Startup.cs | 8 ++++---- Oqtane.Shared/Shared/EntityNames.cs | 1 + 15 files changed, 40 insertions(+), 36 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index bbe37f9b..a115bcd3 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -36,7 +36,7 @@ - +
diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index 3b4ad4df..64b28fc1 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -17,7 +17,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index ae515412..73bd79c7 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -34,7 +34,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index e912cc9d..07c0954c 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -137,7 +137,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index ba2eef54..b91f8b6d 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -155,7 +155,7 @@ - + diff --git a/Oqtane.Client/Services/FolderService.cs b/Oqtane.Client/Services/FolderService.cs index d6f5377e..3c80957c 100644 --- a/Oqtane.Client/Services/FolderService.cs +++ b/Oqtane.Client/Services/FolderService.cs @@ -24,7 +24,7 @@ namespace Oqtane.Services _navigationManager = navigationManager; } - private string ApiUrl => CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "Folder"); + private string ApiUrl => CreateApiUrl(_siteState.Alias, _navigationManager.Uri, EntityNames.Folder); public async Task> GetFoldersAsync(int siteId) { diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index 2afbd1fa..ea1e658f 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -28,7 +28,7 @@ namespace Oqtane.Services private string Apiurl { - get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "ModuleDefinition"); } + get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, EntityNames.ModuleDefinition); } } public async Task> GetModuleDefinitionsAsync(int siteId) diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index af0ec10e..6bc85872 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -116,11 +116,11 @@ // process any sync events for site or page if (reload != Reload.Site && alias.SyncEvents.Any()) { - if (PageState != null && alias.SyncEvents.Exists(item => item.EntityName == "Page" && item.EntityId == PageState.Page.PageId)) + if (PageState != null && alias.SyncEvents.Exists(item => item.EntityName == EntityNames.Page && item.EntityId == PageState.Page.PageId)) { reload = Reload.Page; } - if (alias.SyncEvents.Exists(item => item.EntityName == "Site" && item.EntityId == alias.SiteId)) + if (alias.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == alias.SiteId)) { reload = Reload.Site; } @@ -154,7 +154,7 @@ // process any sync events for user if (reload != Reload.Site && user != null && alias.SyncEvents.Any()) { - if (alias.SyncEvents.Exists(item => item.EntityName == "User" && item.EntityId == user.UserId)) + if (alias.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId)) { reload = Reload.Site; } diff --git a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs index eea37e47..bd667486 100644 --- a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs +++ b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs @@ -41,7 +41,7 @@ namespace Oqtane.Infrastructure log += "Processing Notifications For Site: " + site.Name + "\n\n"; // get site settings - List sitesettings = settingRepository.GetSettings("Site", site.SiteId).ToList(); + List sitesettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList(); Dictionary settings = GetSettings(sitesettings); if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "") { diff --git a/Oqtane.Server/Repository/FileRepository.cs b/Oqtane.Server/Repository/FileRepository.cs index fe5d3380..6f725d9d 100644 --- a/Oqtane.Server/Repository/FileRepository.cs +++ b/Oqtane.Server/Repository/FileRepository.cs @@ -2,6 +2,7 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Shared; namespace Oqtane.Repository { @@ -18,7 +19,7 @@ namespace Oqtane.Repository public IEnumerable GetFiles(int folderId) { - IEnumerable permissions = _permissions.GetPermissions("Folder", folderId).ToList(); + IEnumerable permissions = _permissions.GetPermissions(EntityNames.Folder, folderId).ToList(); IEnumerable files = _db.File.Where(item => item.FolderId == folderId).Include(item => item.Folder); foreach (File file in files) { @@ -46,7 +47,7 @@ namespace Oqtane.Repository File file = _db.File.Where(item => item.FileId == fileId).Include(item => item.Folder).FirstOrDefault(); if (file != null) { - IEnumerable permissions = _permissions.GetPermissions("Folder", file.FolderId).ToList(); + IEnumerable permissions = _permissions.GetPermissions(EntityNames.Folder, file.FolderId).ToList(); file.Folder.Permissions = _permissions.EncodePermissions(file.FolderId, permissions); } return file; diff --git a/Oqtane.Server/Repository/FolderRepository.cs b/Oqtane.Server/Repository/FolderRepository.cs index b3d6f4e3..55ea5cfa 100644 --- a/Oqtane.Server/Repository/FolderRepository.cs +++ b/Oqtane.Server/Repository/FolderRepository.cs @@ -2,6 +2,7 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Shared; namespace Oqtane.Repository { @@ -18,7 +19,7 @@ namespace Oqtane.Repository public IEnumerable GetFolders(int siteId) { - IEnumerable permissions = _permissions.GetPermissions(siteId, "Folder").ToList(); + IEnumerable permissions = _permissions.GetPermissions(siteId, EntityNames.Folder).ToList(); IEnumerable folders = _db.Folder.Where(item => item.SiteId == siteId); foreach(Folder folder in folders) { @@ -31,7 +32,7 @@ namespace Oqtane.Repository { _db.Folder.Add(folder); _db.SaveChanges(); - _permissions.UpdatePermissions(folder.SiteId, "Folder", folder.FolderId, folder.Permissions); + _permissions.UpdatePermissions(folder.SiteId, EntityNames.Folder, folder.FolderId, folder.Permissions); return folder; } @@ -39,7 +40,7 @@ namespace Oqtane.Repository { _db.Entry(folder).State = EntityState.Modified; _db.SaveChanges(); - _permissions.UpdatePermissions(folder.SiteId, "Folder", folder.FolderId, folder.Permissions); + _permissions.UpdatePermissions(folder.SiteId, EntityNames.Folder, folder.FolderId, folder.Permissions); return folder; } @@ -48,7 +49,7 @@ namespace Oqtane.Repository Folder folder = _db.Folder.Find(folderId); if (folder != null) { - IEnumerable permissions = _permissions.GetPermissions("Folder", folder.FolderId).ToList(); + IEnumerable permissions = _permissions.GetPermissions(EntityNames.Folder, folder.FolderId).ToList(); folder.Permissions = _permissions.EncodePermissions(folder.FolderId, permissions); } return folder; @@ -59,7 +60,7 @@ namespace Oqtane.Repository Folder folder = _db.Folder.Where(item => item.SiteId == siteId && item.Path == path).FirstOrDefault(); if (folder != null) { - IEnumerable permissions = _permissions.GetPermissions("Folder", folder.FolderId).ToList(); + IEnumerable permissions = _permissions.GetPermissions(EntityNames.Folder, folder.FolderId).ToList(); folder.Permissions = _permissions.EncodePermissions(folder.FolderId, permissions); } return folder; @@ -68,7 +69,7 @@ namespace Oqtane.Repository public void DeleteFolder(int folderId) { Folder folder = _db.Folder.Find(folderId); - _permissions.DeletePermissions(folder.SiteId, "Folder", folderId); + _permissions.DeletePermissions(folder.SiteId, EntityNames.Folder, folderId); _db.Folder.Remove(folder); _db.SaveChanges(); } diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index 509a7bcf..001a4092 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -35,14 +35,14 @@ namespace Oqtane.Repository public void UpdateModuleDefinition(ModuleDefinition moduleDefinition) { - _permissions.UpdatePermissions(moduleDefinition.SiteId, "ModuleDefinition", moduleDefinition.ModuleDefinitionId, moduleDefinition.Permissions); + _permissions.UpdatePermissions(moduleDefinition.SiteId, EntityNames.ModuleDefinition, moduleDefinition.ModuleDefinitionId, moduleDefinition.Permissions); _cache.Remove("moduledefinitions"); } public void DeleteModuleDefinition(int moduleDefinitionId, int siteId) { ModuleDefinition moduleDefinition = _db.ModuleDefinition.Find(moduleDefinitionId); - _permissions.DeletePermissions(siteId, "ModuleDefinition", moduleDefinitionId); + _permissions.DeletePermissions(siteId, EntityNames.ModuleDefinition, moduleDefinitionId); _db.ModuleDefinition.Remove(moduleDefinition); _db.SaveChanges(); _cache.Remove("moduledefinitions"); @@ -60,7 +60,7 @@ namespace Oqtane.Repository }); // get module defintion permissions for site - List permissions = _permissions.GetPermissions(siteId, "ModuleDefinition").ToList(); + List permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList(); // get module definitions in database List moduledefs = _db.ModuleDefinition.ToList(); @@ -75,14 +75,14 @@ namespace Oqtane.Repository moduledef = new ModuleDefinition { ModuleDefinitionName = moduledefinition.ModuleDefinitionName }; _db.ModuleDefinition.Add(moduledef); _db.SaveChanges(); - _permissions.UpdatePermissions(siteId, "ModuleDefinition", moduledef.ModuleDefinitionId, moduledefinition.Permissions); + _permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions); } else { // existing module definition if (permissions.Count == 0) { - _permissions.UpdatePermissions(siteId, "ModuleDefinition", moduledef.ModuleDefinitionId, moduledefinition.Permissions); + _permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions); } else { @@ -102,7 +102,7 @@ namespace Oqtane.Repository // any remaining module definitions are orphans foreach (ModuleDefinition moduledefinition in moduledefs) { - _permissions.DeletePermissions(siteId, "ModuleDefinition", moduledefinition.ModuleDefinitionId); + _permissions.DeletePermissions(siteId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId); _db.ModuleDefinition.Remove(moduledefinition); // delete } diff --git a/Oqtane.Server/Repository/PageRepository.cs b/Oqtane.Server/Repository/PageRepository.cs index f5b726f3..d967f37e 100644 --- a/Oqtane.Server/Repository/PageRepository.cs +++ b/Oqtane.Server/Repository/PageRepository.cs @@ -2,6 +2,7 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Shared; namespace Oqtane.Repository { @@ -20,7 +21,7 @@ namespace Oqtane.Repository public IEnumerable GetPages(int siteId) { - IEnumerable permissions = _permissions.GetPermissions(siteId, "Page").ToList(); + IEnumerable permissions = _permissions.GetPermissions(siteId, EntityNames.Page).ToList(); IEnumerable pages = _db.Page.Where(item => item.SiteId == siteId && item.UserId == null); foreach(Page page in pages) { @@ -33,7 +34,7 @@ namespace Oqtane.Repository { _db.Page.Add(page); _db.SaveChanges(); - _permissions.UpdatePermissions(page.SiteId, "Page", page.PageId, page.Permissions); + _permissions.UpdatePermissions(page.SiteId, EntityNames.Page, page.PageId, page.Permissions); return page; } @@ -41,7 +42,7 @@ namespace Oqtane.Repository { _db.Entry(page).State = EntityState.Modified; _db.SaveChanges(); - _permissions.UpdatePermissions(page.SiteId, "Page", page.PageId, page.Permissions); + _permissions.UpdatePermissions(page.SiteId, EntityNames.Page, page.PageId, page.Permissions); return page; } @@ -50,7 +51,7 @@ namespace Oqtane.Repository Page page = _db.Page.Find(pageId); if (page != null) { - IEnumerable permissions = _permissions.GetPermissions("Page", page.PageId).ToList(); + IEnumerable permissions = _permissions.GetPermissions(EntityNames.Page, page.PageId).ToList(); page.Permissions = _permissions.EncodePermissions(page.PageId, permissions); } return page; @@ -68,7 +69,7 @@ namespace Oqtane.Repository } if (page != null) { - IEnumerable permissions = _permissions.GetPermissions("Page", page.PageId).ToList(); + IEnumerable permissions = _permissions.GetPermissions(EntityNames.Page, page.PageId).ToList(); page.Permissions = _permissions.EncodePermissions(page.PageId, permissions); } } @@ -80,7 +81,7 @@ namespace Oqtane.Repository Page page = _db.Page.Where(item => item.Path == path && item.SiteId == siteId).FirstOrDefault(); if (page != null) { - IEnumerable permissions = _permissions.GetPermissions("Page", page.PageId).ToList(); + IEnumerable permissions = _permissions.GetPermissions(EntityNames.Page, page.PageId).ToList(); page.Permissions = _permissions.EncodePermissions(page.PageId, permissions); } return page; @@ -89,7 +90,7 @@ namespace Oqtane.Repository public void DeletePage(int pageId) { Page page = _db.Page.Find(pageId); - _permissions.DeletePermissions(page.SiteId, "Page", pageId); + _permissions.DeletePermissions(page.SiteId, EntityNames.Page, pageId); IEnumerable pageModules = _db.PageModule.Where(item => item.PageId == pageId).ToList(); foreach (var pageModule in pageModules) { diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 79a2d31c..da9e633f 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -246,10 +246,10 @@ namespace Oqtane // register authorization services services.AddAuthorizationCore(options => { - options.AddPolicy("ViewPage", policy => policy.Requirements.Add(new PermissionRequirement("Page", PermissionNames.View))); - options.AddPolicy("EditPage", policy => policy.Requirements.Add(new PermissionRequirement("Page", PermissionNames.Edit))); - options.AddPolicy("ViewModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", PermissionNames.View))); - options.AddPolicy("EditModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", PermissionNames.Edit))); + options.AddPolicy("ViewPage", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.View))); + options.AddPolicy("EditPage", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.Edit))); + options.AddPolicy("ViewModule", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Module, PermissionNames.View))); + options.AddPolicy("EditModule", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Module, PermissionNames.Edit))); }); // register scoped core services diff --git a/Oqtane.Shared/Shared/EntityNames.cs b/Oqtane.Shared/Shared/EntityNames.cs index ffff34f0..569e0603 100644 --- a/Oqtane.Shared/Shared/EntityNames.cs +++ b/Oqtane.Shared/Shared/EntityNames.cs @@ -3,6 +3,7 @@ public class EntityNames { public const string Module = "Module"; + public const string ModuleDefinition = "ModuleDefinition"; public const string PageModule = "PageModule"; public const string Host = "Host"; public const string Site = "Site"; From 50bbc7f5b849d5b38ec82b742ea6b4d6f695a239 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 18 Mar 2020 08:59:41 -0400 Subject: [PATCH 076/265] moved to version 0.0.9 in preparation for MVP release --- Oqtane.Client/Oqtane.Client.csproj | 2 +- Oqtane.Framework.nuspec | 9 +++++---- Oqtane.Server/Oqtane.Server.csproj | 2 +- Oqtane.Shared/Oqtane.Shared.csproj | 2 +- Oqtane.Shared/Shared/Constants.cs | 2 +- Oqtane.Upgrade/Oqtane.Upgrade.csproj | 14 +++++++++++++- 6 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index 150eb596..d66027f3 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -11,7 +11,7 @@ 7.3 3.0 Debug;Release;Wasm - 0.0.1 + 0.0.9 Oqtane Shaun Walker .NET Foundation diff --git a/Oqtane.Framework.nuspec b/Oqtane.Framework.nuspec index 50e48e6f..3b82d16e 100644 --- a/Oqtane.Framework.nuspec +++ b/Oqtane.Framework.nuspec @@ -2,7 +2,7 @@ Oqtane.Framework - 0.0.1 + 0.0.9 Shaun Walker .NET Foundation Oqtane Framework @@ -17,8 +17,9 @@ A modular application framework for Blazor - - - + + + + \ No newline at end of file diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 2a243897..0e0e0d95 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -9,7 +9,7 @@ true Debug;Release;Wasm - 0.0.1 + 0.0.9 Oqtane Shaun Walker .NET Foundation diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index 79522771..d252b2aa 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -4,7 +4,7 @@ netstandard2.1 7.3 Debug;Release;Wasm - 0.0.1 + 0.0.9 Oqtane Shaun Walker .NET Foundation diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 09f5f334..c5b2926d 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -3,7 +3,7 @@ public class Constants { public const string PackageId = "Oqtane.Framework"; - public const string Version = "0.0.1"; + public const string Version = "0.0.9"; public const string PageComponent = "Oqtane.UI.ThemeBuilder, Oqtane.Client"; public const string ContainerComponent = "Oqtane.UI.ContainerBuilder, Oqtane.Client"; diff --git a/Oqtane.Upgrade/Oqtane.Upgrade.csproj b/Oqtane.Upgrade/Oqtane.Upgrade.csproj index 3e32c64c..dad498df 100644 --- a/Oqtane.Upgrade/Oqtane.Upgrade.csproj +++ b/Oqtane.Upgrade/Oqtane.Upgrade.csproj @@ -1,8 +1,20 @@ - Exe netcoreapp3.1 + 7.3 + Exe + 0.0.9 + Oqtane + Shaun Walker + .NET Foundation + Modular Application Framework for Blazor + .NET Foundation + https://www.oqtane.org + https://github.com/oqtane + Git + Not for production use. + Oqtane From 18a843e74f6bc2bab9b71dc5e37262f473b37298 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 19 Mar 2020 12:07:33 -0400 Subject: [PATCH 077/265] extensibility enhancements for site templates --- Oqtane.Client/App.razor | 4 +- Oqtane.Client/Modules/Admin/Files/Edit.razor | 6 +- Oqtane.Client/Modules/Admin/Pages/Add.razor | 7 +- Oqtane.Client/Modules/Admin/Sites/Add.razor | 41 +- Oqtane.Client/Modules/Admin/Tenants/Add.razor | 8 +- .../Modules/Controls/PermissionGrid.razor | 16 +- Oqtane.Client/Services/InstallationService.cs | 12 +- .../Interfaces/IInstallationService.cs | 6 +- .../Interfaces/ISiteTemplateService.cs | 11 + Oqtane.Client/Services/SiteTemplateService.cs | 35 ++ Oqtane.Client/Startup.cs | 1 + Oqtane.Client/UI/Installer.razor | 6 +- Oqtane.Server/Controllers/FolderController.cs | 8 +- .../Controllers/InstallationController.cs | 40 +- Oqtane.Server/Controllers/PageController.cs | 24 +- .../Controllers/SiteTemplateController.cs | 25 ++ .../OqtaneServiceCollectionExtensions.cs | 14 +- .../Interfaces/ISiteTemplate.cs | 12 + Oqtane.Server/Infrastructure/LogManager.cs | 2 +- .../SiteTemplates/DefaultSiteTemplate.cs | 151 +++++++ Oqtane.Server/Repository/FileRepository.cs | 4 +- Oqtane.Server/Repository/FolderRepository.cs | 6 +- .../Interfaces/IPermissionRepository.cs | 2 +- .../Interfaces/ISiteTemplateRepository.cs | 10 + .../Repository/ModuleDefinitionRepository.cs | 2 +- Oqtane.Server/Repository/ModuleRepository.cs | 2 +- .../Repository/PageModuleRepository.cs | 8 +- Oqtane.Server/Repository/PageRepository.cs | 8 +- .../Repository/PermissionRepository.cs | 4 +- Oqtane.Server/Repository/SiteRepository.cs | 380 +++++++++++++----- .../Repository/SiteTemplateRepository.cs | 57 +++ Oqtane.Server/Security/UserPermissions.cs | 2 +- Oqtane.Server/Startup.cs | 5 + Oqtane.Server/wwwroot/images/logo.png | Bin 0 -> 7963 bytes .../{GenericResponse.cs => Installation.cs} | 2 +- Oqtane.Shared/Models/Permission.cs | 18 + Oqtane.Shared/Models/Site.cs | 5 +- .../{PageTemplate.cs => SiteTemplate.cs} | 6 + Oqtane.Shared/Shared/Constants.cs | 2 + 39 files changed, 758 insertions(+), 194 deletions(-) create mode 100644 Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs create mode 100644 Oqtane.Client/Services/SiteTemplateService.cs create mode 100644 Oqtane.Server/Controllers/SiteTemplateController.cs create mode 100644 Oqtane.Server/Infrastructure/Interfaces/ISiteTemplate.cs create mode 100644 Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs create mode 100644 Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs create mode 100644 Oqtane.Server/Repository/SiteTemplateRepository.cs create mode 100644 Oqtane.Server/wwwroot/images/logo.png rename Oqtane.Shared/Models/{GenericResponse.cs => Installation.cs} (79%) rename Oqtane.Shared/Models/{PageTemplate.cs => SiteTemplate.cs} (86%) diff --git a/Oqtane.Client/App.razor b/Oqtane.Client/App.razor index 66e5b15f..ffc2a160 100644 --- a/Oqtane.Client/App.razor +++ b/Oqtane.Client/App.razor @@ -23,8 +23,8 @@ protected override async Task OnParametersSetAsync() { - var response = await InstallationService.IsInstalled(); - _installed = response.Success; + var installation = await InstallationService.IsInstalled(); + _installed = installation.Success; _initialized = true; } diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index bbe37f9b..c965a8ea 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -101,11 +101,7 @@ else { _parentId = _folders[0].FolderId; - List permissionstrings = new List(); - permissionstrings.Add(new PermissionString { PermissionName = PermissionNames.Browse, Permissions = Constants.AdminRole }); - permissionstrings.Add(new PermissionString { PermissionName = PermissionNames.View, Permissions = Constants.AdminRole }); - permissionstrings.Add(new PermissionString { PermissionName = PermissionNames.Edit, Permissions = Constants.AdminRole }); - _permissions = UserSecurity.SetPermissionStrings(permissionstrings); + _permissions = ""; } } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index e912cc9d..d67c81a4 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -165,7 +165,7 @@ string _themetype = ""; string _layouttype = ""; string _icon = ""; - string _permissions = ""; + string _permissions = ""; PermissionGrid _permissionGrid; @@ -183,10 +183,7 @@ _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); _layouttype = PageState.Site.DefaultLayoutType; - List permissionstrings = new List(); - permissionstrings.Add(new PermissionString { PermissionName = PermissionNames.View, Permissions = Constants.AdminRole }); - permissionstrings.Add(new PermissionString { PermissionName = PermissionNames.Edit, Permissions = Constants.AdminRole }); - _permissions = UserSecurity.SetPermissionStrings(permissionstrings); + _permissions = ""; } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index a692d081..7700a3b0 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -5,6 +5,7 @@ @inject IAliasService AliasService @inject ISiteService SiteService @inject IThemeService ThemeService +@inject ISiteTemplateService SiteTemplateService @inject IUserService UserService @if (_tenants == null) @@ -16,7 +17,7 @@ else + + + + @if (!_isinitialized) { - - + + diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor index 924112ac..a6522a07 100644 --- a/Oqtane.Client/Modules/Controls/ActionDialog.razor +++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor @@ -30,7 +30,7 @@ { if (Disabled) { - + } else { diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index 7585952f..48b8116e 100644 --- a/Oqtane.Client/Modules/Controls/ActionLink.razor +++ b/Oqtane.Client/Modules/Controls/ActionLink.razor @@ -6,7 +6,7 @@ { if (Disabled) { - @((MarkupString)_iconSpan) @_text + } else { @@ -82,7 +82,7 @@ if (!string.IsNullOrEmpty(IconName)) { _iconSpan = $" "; - } + } _url = EditUrl(Action, _parameters); _authorized = IsAuthorized(); From 24263025c93e68dbeb958a256f7e6580ebf94668 Mon Sep 17 00:00:00 2001 From: Mike Casas Date: Tue, 24 Mar 2020 21:27:35 -0400 Subject: [PATCH 086/265] Adds an icon for the link/button. --- .../Modules/Controls/ActionDialog.razor | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor index a6522a07..bd3d5e2c 100644 --- a/Oqtane.Client/Modules/Controls/ActionDialog.razor +++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor @@ -17,7 +17,7 @@ @@ -30,11 +30,11 @@ { if (Disabled) { - + } else { - + } } @@ -66,9 +66,13 @@ [Parameter] public Action OnClick { get; set; } // required if an Action is specified - executes a method in the calling component + [Parameter] + public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon + bool _visible = false; bool _editmode = true; bool _authorized = false; + string _iconSpan = ""; protected override void OnParametersSet() { @@ -84,6 +88,12 @@ { _editmode = bool.Parse(EditMode); } + + if (!string.IsNullOrEmpty(IconName)) + { + _iconSpan = $" "; + } + _authorized = IsAuthorized(); } From 1e688dcf5e73521e2fba387f1821ea1f68caeef2 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 25 Mar 2020 10:54:34 -0400 Subject: [PATCH 087/265] Fixed some display issues for mobile rendering --- Oqtane.Client/Modules/Admin/Files/Edit.razor | 6 ++---- .../Modules/Admin/ModuleDefinitions/Edit.razor | 6 ++---- Oqtane.Client/Modules/Admin/Modules/Settings.razor | 4 +--- Oqtane.Client/Modules/Admin/Pages/Add.razor | 4 +--- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 6 ++---- Oqtane.Client/Modules/Admin/Roles/Add.razor | 12 ++++++------ Oqtane.Client/Modules/Admin/Roles/Edit.razor | 12 ++++++------ Oqtane.Client/Modules/Controls/PermissionGrid.razor | 6 +++--- Oqtane.Client/Themes/Controls/ControlPanel.razor | 2 +- Oqtane.Client/wwwroot/css/app.css | 1 - .../SiteTemplates/DefaultSiteTemplate.cs | 2 +- Oqtane.Server/wwwroot/css/app.css | 1 - 12 files changed, 25 insertions(+), 37 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index c19ba94a..d7bc822c 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -32,10 +32,8 @@ - - diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index 64b28fc1..fb6c723e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -13,10 +13,8 @@ - - diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index 560fa398..3f27530c 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -54,10 +54,8 @@ - - diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 077dfd73..fc0644a3 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -133,10 +133,8 @@ - - diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index b91f8b6d..91a57188 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -151,11 +151,9 @@ - -
- +
- + @@ -38,7 +39,7 @@ else
- + @@ -46,7 +47,7 @@ else
- +
- +
- +
+ + + +
- + @@ -98,7 +113,7 @@ else
- + @@ -116,6 +131,7 @@ else Dictionary _themes = new Dictionary(); Dictionary _panelayouts = new Dictionary(); Dictionary _containers = new Dictionary(); + List _siteTemplates; List _themeList; List _tenants; @@ -125,6 +141,7 @@ else string _themetype = ""; string _layouttype = ""; string _containertype = ""; + string _sitetemplatetype = ""; bool _isinitialized = true; string _username = ""; string _password = ""; @@ -136,6 +153,7 @@ else _urls = PageState.Alias.Name; _themes = ThemeService.GetThemeTypes(_themeList); _containers = ThemeService.GetContainerTypes(_themeList); + _siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync(); _username = Constants.HostUser; } @@ -185,7 +203,7 @@ else private async Task SaveSite() { - if (_tenantid != "-1" && _name != "" && _urls != "" && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)) && !string.IsNullOrEmpty(_containertype)) + if (_tenantid != "-1" && _name != "" && _urls != "" && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)) && !string.IsNullOrEmpty(_containertype) && !string.IsNullOrEmpty(_sitetemplatetype)) { bool unique = true; List aliases = await AliasService.GetAliasesAsync(); @@ -233,6 +251,7 @@ else site.DefaultThemeType = _themetype; site.DefaultLayoutType = (_layouttype == null ? "" : _layouttype); site.DefaultContainerType = _containertype; + site.SiteTemplateType = _sitetemplatetype; site = await SiteService.AddSiteAsync(site, aliases[0]); foreach (Alias alias in aliases) @@ -276,7 +295,7 @@ else } else { - AddModuleMessage("You Must Provide A Tenant, Site Name, Alias, And Default Theme/Container", MessageType.Warning); + AddModuleMessage("You Must Provide A Tenant, Site Name, Alias, Default Theme/Container, And Site Template", MessageType.Warning); } } diff --git a/Oqtane.Client/Modules/Admin/Tenants/Add.razor b/Oqtane.Client/Modules/Admin/Tenants/Add.razor index baccdfa8..4348bda7 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Add.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Add.razor @@ -127,8 +127,8 @@ } } - GenericResponse response = await InstallationService.Install(connectionstring); - if (response.Success) + Installation installation = await InstallationService.Install(connectionstring); + if (installation.Success) { Tenant tenant = new Tenant(); tenant.Name = name; @@ -142,8 +142,8 @@ } else { - await logger.LogError("Error Creating Tenant {Error}", response.Message); - AddModuleMessage(response.Message, MessageType.Error); + await logger.LogError("Error Creating Tenant {Error}", installation.Message); + AddModuleMessage(installation.Message, MessageType.Error); } } else diff --git a/Oqtane.Client/Modules/Controls/PermissionGrid.razor b/Oqtane.Client/Modules/Controls/PermissionGrid.razor index 3d240dec..5e253a9d 100644 --- a/Oqtane.Client/Modules/Controls/PermissionGrid.razor +++ b/Oqtane.Client/Modules/Controls/PermissionGrid.razor @@ -94,22 +94,26 @@ { if (string.IsNullOrEmpty(PermissionNames)) { - _permissionnames = "View,Edit"; + _permissionnames = Shared.PermissionNames.View + "," + Shared.PermissionNames.Edit; } else { _permissionnames = PermissionNames; } + _roles = await RoleService.GetRolesAsync(ModuleState.SiteId); _roles.Insert(0, new Role { Name = Constants.AllUsersRole }); + _permissions = new List(); + foreach (string permissionname in _permissionnames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + { + // initialize with admin role + _permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = Constants.AdminRole }); + } + if (!string.IsNullOrEmpty(Permissions)) { - _permissions = new List(); - foreach (string permissionname in _permissionnames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) - { - _permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = "" }); - } + // populate permissions foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions)) { if (_permissions.Find(item => item.PermissionName == permissionstring.PermissionName) != null) diff --git a/Oqtane.Client/Services/InstallationService.cs b/Oqtane.Client/Services/InstallationService.cs index 9d497e68..31a1e3dd 100644 --- a/Oqtane.Client/Services/InstallationService.cs +++ b/Oqtane.Client/Services/InstallationService.cs @@ -24,19 +24,19 @@ namespace Oqtane.Services get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "Installation"); } } - public async Task IsInstalled() + public async Task IsInstalled() { - return await _http.GetJsonAsync(Apiurl + "/installed"); + return await _http.GetJsonAsync(Apiurl + "/installed"); } - public async Task Install(string connectionstring) + public async Task Install(string connectionstring) { - return await _http.PostJsonAsync(Apiurl, connectionstring); + return await _http.PostJsonAsync(Apiurl, connectionstring); } - public async Task Upgrade() + public async Task Upgrade() { - return await _http.GetJsonAsync(Apiurl + "/upgrade"); + return await _http.GetJsonAsync(Apiurl + "/upgrade"); } } } diff --git a/Oqtane.Client/Services/Interfaces/IInstallationService.cs b/Oqtane.Client/Services/Interfaces/IInstallationService.cs index 91a3a11a..580fd225 100644 --- a/Oqtane.Client/Services/Interfaces/IInstallationService.cs +++ b/Oqtane.Client/Services/Interfaces/IInstallationService.cs @@ -5,8 +5,8 @@ namespace Oqtane.Services { public interface IInstallationService { - Task IsInstalled(); - Task Install(string connectionstring); - Task Upgrade(); + Task IsInstalled(); + Task Install(string connectionstring); + Task Upgrade(); } } diff --git a/Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs b/Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs new file mode 100644 index 00000000..abb9a6c1 --- /dev/null +++ b/Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs @@ -0,0 +1,11 @@ +using Oqtane.Models; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Oqtane.Services +{ + public interface ISiteTemplateService + { + Task> GetSiteTemplatesAsync(); + } +} diff --git a/Oqtane.Client/Services/SiteTemplateService.cs b/Oqtane.Client/Services/SiteTemplateService.cs new file mode 100644 index 00000000..410f2abc --- /dev/null +++ b/Oqtane.Client/Services/SiteTemplateService.cs @@ -0,0 +1,35 @@ +using Oqtane.Models; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using Oqtane.Shared; + +namespace Oqtane.Services +{ + public class SiteTemplateService : ServiceBase, ISiteTemplateService + { + private readonly HttpClient _http; + private readonly SiteState _siteState; + private readonly NavigationManager _navigationManager; + + public SiteTemplateService(HttpClient http, SiteState siteState, NavigationManager navigationManager) + { + _http = http; + _siteState = siteState; + _navigationManager = navigationManager; + } + + private string Apiurl + { + get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "SiteTemplate"); } + } + + public async Task> GetSiteTemplatesAsync() + { + List siteTemplates = await _http.GetJsonAsync>(Apiurl); + return siteTemplates.OrderBy(item => item.Name).ToList(); + } + } +} diff --git a/Oqtane.Client/Startup.cs b/Oqtane.Client/Startup.cs index 817e36fa..b7991d90 100644 --- a/Oqtane.Client/Startup.cs +++ b/Oqtane.Client/Startup.cs @@ -58,6 +58,7 @@ namespace Oqtane.Client services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // dynamically register module contexts and repository services Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); diff --git a/Oqtane.Client/UI/Installer.razor b/Oqtane.Client/UI/Installer.razor index d8ea0606..071a97bb 100644 --- a/Oqtane.Client/UI/Installer.razor +++ b/Oqtane.Client/UI/Installer.razor @@ -175,8 +175,8 @@ } } - GenericResponse response = await InstallationService.Install(connectionstring); - if (response.Success) + Installation installation = await InstallationService.Install(connectionstring); + if (installation.Success) { Site site = new Site(); site.TenantId = -1; // will be populated on server @@ -199,7 +199,7 @@ } else { - _message = "
" + response.Message + "
"; + _message = "
" + installation.Message + "
"; _loadingDisplay = "display: none;"; } } diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 9fad4baf..1a2fdb2f 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -17,12 +17,14 @@ namespace Oqtane.Controllers { private readonly IFolderRepository _folders; private readonly IUserPermissions _userPermissions; + private readonly IPermissionRepository _permissionRepository; private readonly ILogManager _logger; - public FolderController(IFolderRepository folders, IUserPermissions userPermissions, ILogManager logger) + public FolderController(IFolderRepository folders, IUserPermissions userPermissions, IPermissionRepository permissionRepository, ILogManager logger) { _folders = folders; _userPermissions = userPermissions; + _permissionRepository = permissionRepository; _logger = logger; } @@ -98,7 +100,9 @@ namespace Oqtane.Controllers } else { - permissions = UserSecurity.SetPermissionStrings(new List { new PermissionString { PermissionName = PermissionNames.Edit, Permissions = Constants.AdminRole } }); + permissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }); } if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions)) { diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index cde7a4be..763a570d 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -31,9 +31,9 @@ namespace Oqtane.Controllers // POST api/ [HttpPost] - public GenericResponse Post([FromBody] string connectionString) + public Installation Post([FromBody] string connectionString) { - var response = new GenericResponse { Success = false, Message = "" }; + var installation = new Installation { Success = false, Message = "" }; if (ModelState.IsValid) { @@ -110,7 +110,7 @@ namespace Oqtane.Controllers } catch (Exception ex) { - response.Message = "Can Not Create Database - " + ex.Message; + installation.Message = "Can Not Create Database - " + ex.Message; } // sleep to allow SQL server to attach new database @@ -140,7 +140,7 @@ namespace Oqtane.Controllers var result = dbUpgrade.PerformUpgrade(); if (!result.Successful) { - response.Message = result.Error.Message; + installation.Message = result.Error.Message; } else { @@ -161,24 +161,24 @@ namespace Oqtane.Controllers } _config.Reload(); } - response.Success = true; + installation.Success = true; } } } } else { - response.Message = "Application Is Already Installed"; + installation.Message = "Application Is Already Installed"; } } - return response; + return installation; } // GET api//installed [HttpGet("installed")] - public GenericResponse IsInstalled() + public Installation IsInstalled() { - var response = new GenericResponse { Success = false, Message = "" }; + var installation = new Installation { Success = false, Message = "" }; string datadirectory = AppDomain.CurrentDomain.GetData("DataDirectory").ToString(); string connectionString = _config.GetConnectionString("DefaultConnection"); @@ -193,28 +193,28 @@ namespace Oqtane.Controllers { connection.Open(); } - response.Success = true; + installation.Success = true; } catch { // database does not exist - response.Message = "Database Does Not Exist"; + installation.Message = "Database Does Not Exist"; } } else { - response.Message = "Connection String Has Not Been Specified In Oqtane.Server\\appsettings.json"; + installation.Message = "Connection String Has Not Been Specified In Oqtane.Server\\appsettings.json"; } - if (response.Success) + if (installation.Success) { var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) .WithScript(new DbUp.Engine.SqlScript("Master.sql", "")); var dbUpgrade = dbUpgradeConfig.Build(); - response.Success = !dbUpgrade.IsUpgradeRequired(); - if (!response.Success) + installation.Success = !dbUpgrade.IsUpgradeRequired(); + if (!installation.Success) { - response.Message = "Master Installation Scripts Have Not Been Executed"; + installation.Message = "Master Installation Scripts Have Not Been Executed"; } else { @@ -264,7 +264,7 @@ namespace Oqtane.Controllers } } - return response; + return installation; } private void InstallModule(Assembly assembly, string connectionstring) @@ -284,11 +284,11 @@ namespace Oqtane.Controllers [HttpGet("upgrade")] [Authorize(Roles = Constants.HostRole)] - public GenericResponse Upgrade() + public Installation Upgrade() { - var response = new GenericResponse { Success = true, Message = "" }; + var installation = new Installation { Success = true, Message = "" }; _installationManager.UpgradeFramework(); - return response; + return installation; } } diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 01abb4ac..3a65407d 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -19,15 +19,17 @@ namespace Oqtane.Controllers private readonly IModuleRepository _modules; private readonly IPageModuleRepository _pageModules; private readonly IUserPermissions _userPermissions; + private readonly IPermissionRepository _permissionRepository; private readonly ISyncManager _syncManager; private readonly ILogManager _logger; - public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger) + public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IUserPermissions userPermissions, IPermissionRepository permissionRepository, ISyncManager syncManager, ILogManager logger) { _pages = pages; _modules = modules; _pageModules = pageModules; _userPermissions = userPermissions; + _permissionRepository = permissionRepository; _syncManager = syncManager; _logger = logger; } @@ -111,7 +113,9 @@ namespace Oqtane.Controllers } else { - permissions = UserSecurity.SetPermissionStrings(new List { new PermissionString { PermissionName = PermissionNames.Edit, Permissions = Constants.AdminRole } }); + permissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }); } if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions)) @@ -150,10 +154,10 @@ namespace Oqtane.Controllers page.ThemeType = parent.ThemeType; page.LayoutType = parent.LayoutType; page.Icon = parent.Icon; - List permissions = new List(); - permissions.Add(new PermissionString { PermissionName = PermissionNames.View, Permissions = "[" + userid + "]" }); - permissions.Add(new PermissionString { PermissionName = PermissionNames.Edit, Permissions = "[" + userid + "]" }); - page.Permissions = UserSecurity.SetPermissionStrings(permissions); + page.Permissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, userid, true), + new Permission(PermissionNames.Edit, userid, true) + }); page.IsPersonalizable = false; page.UserId = int.Parse(userid); page = _pages.AddPage(page); @@ -167,10 +171,10 @@ namespace Oqtane.Controllers module.SiteId = page.SiteId; module.PageId = page.PageId; module.ModuleDefinitionName = pm.Module.ModuleDefinitionName; - permissions = new List(); - permissions.Add(new PermissionString { PermissionName = PermissionNames.View, Permissions = "[" + userid + "]" }); - permissions.Add(new PermissionString { PermissionName = PermissionNames.Edit, Permissions = "[" + userid + "]" }); - module.Permissions = UserSecurity.SetPermissionStrings(permissions); + module.Permissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, userid, true), + new Permission(PermissionNames.Edit, userid, true) + }); module = _modules.AddModule(module); string content = _modules.ExportModule(pm.ModuleId); diff --git a/Oqtane.Server/Controllers/SiteTemplateController.cs b/Oqtane.Server/Controllers/SiteTemplateController.cs new file mode 100644 index 00000000..c74e69a4 --- /dev/null +++ b/Oqtane.Server/Controllers/SiteTemplateController.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; +using Oqtane.Models; +using Oqtane.Repository; + +namespace Oqtane.Controllers +{ + [Route("{site}/api/[controller]")] + public class SiteTemplateController : Controller + { + private readonly ISiteTemplateRepository _siteTemplates; + + public SiteTemplateController(ISiteTemplateRepository siteTemplates) + { + _siteTemplates = siteTemplates; + } + + // GET: api/ + [HttpGet] + public IEnumerable Get() + { + return _siteTemplates.GetSiteTemplates(); + } + } +} diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index 3911d03b..e2bf622e 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -43,6 +43,18 @@ namespace Microsoft.Extensions.DependencyInjection return services; } + public static IServiceCollection AddOqtaneSiteTemplates(this IServiceCollection services) + { + if (services is null) + { + throw new ArgumentNullException(nameof(services)); + } + + LoadAssemblies("SiteTemplate"); + + return services; + } + public static IServiceCollection AddOqtaneServices(this IServiceCollection services) { if (services is null) @@ -99,7 +111,7 @@ namespace Microsoft.Extensions.DependencyInjection var assembliesFolder = new DirectoryInfo(assemblyPath); - // iterate through Oqtane theme assemblies in /bin ( filter is narrow to optimize loading process ) + // iterate through Oqtane assemblies in /bin ( filter is narrow to optimize loading process ) foreach (var file in assembliesFolder.EnumerateFiles($"*.{pattern}.*.dll")) { // check if assembly is already loaded diff --git a/Oqtane.Server/Infrastructure/Interfaces/ISiteTemplate.cs b/Oqtane.Server/Infrastructure/Interfaces/ISiteTemplate.cs new file mode 100644 index 00000000..20b8cdb8 --- /dev/null +++ b/Oqtane.Server/Infrastructure/Interfaces/ISiteTemplate.cs @@ -0,0 +1,12 @@ +using Oqtane.Models; +using System.Collections.Generic; + +namespace Oqtane.Infrastructure.Interfaces +{ + public interface ISiteTemplate + { + string Name { get; } + + List CreateSite(Site site); + } +} diff --git a/Oqtane.Server/Infrastructure/LogManager.cs b/Oqtane.Server/Infrastructure/LogManager.cs index ddaaf4fb..47101016 100644 --- a/Oqtane.Server/Infrastructure/LogManager.cs +++ b/Oqtane.Server/Infrastructure/LogManager.cs @@ -112,7 +112,7 @@ namespace Oqtane.Infrastructure var section = _config.GetSection("Logging:LogLevel:Default"); if (section.Exists()) { - minlevel = Enum.Parse(_config.GetSection("Logging:LogLevel:Default").ToString()); + minlevel = Enum.Parse(section.Value); } if (Enum.Parse(log.Level) >= minlevel) diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs new file mode 100644 index 00000000..b524b6bd --- /dev/null +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -0,0 +1,151 @@ +using Oqtane.Models; +using Oqtane.Infrastructure.Interfaces; +using System.Collections.Generic; +using Oqtane.Repository; +using Microsoft.AspNetCore.Hosting; +using Oqtane.Shared; + +namespace Oqtane.SiteTemplates +{ + public class DefaultSiteTemplate : ISiteTemplate + { + private readonly IPermissionRepository _permissionRepository; + private readonly IWebHostEnvironment _environment; + private readonly ISiteRepository _siteRepository; + private readonly IFolderRepository _folderRepository; + private readonly IFileRepository _fileRepository; + + public DefaultSiteTemplate(IPermissionRepository permissionRepository, IWebHostEnvironment environment, ISiteRepository siteRepository, IFolderRepository folderRepository, IFileRepository fileRepository) + { + _permissionRepository = permissionRepository; + _environment = environment; + _siteRepository = siteRepository; + _folderRepository = folderRepository; + _fileRepository = fileRepository; + } + + public string Name + { + get { return "Default Site Template"; } + } + + public List CreateSite(Site site) + { + List _pageTemplates = new List(); + + _pageTemplates.Add(new PageTemplate + { + Name = "Home", + Parent = "", + Path = "", + Icon = "home", + IsNavigation = true, + IsPersonalizable = false, + EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AllUsersRole, true), + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }) , + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Welcome To Oqtane...", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AllUsersRole, true), + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "

Oqtane is an open source modular application framework built from the ground up using modern .NET Core technology. It leverages the revolutionary new Blazor component model to create a fully dynamic web development experience which can be executed on a client or server. Whether you are looking for a platform to accelerate your web development efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.

" + + "



Join Our Community  Clone Our Repo

" + + "

Blazor is a single-page app framework that lets you build interactive web applications using C# instead of JavaScript. Client-side Blazor relies on WebAssembly, an open web standard that does not require plugins or code transpilation in order to run natively in a web browser. Server-side Blazor uses SignalR to host your application on a web server and provide a responsive and robust debugging experience. Blazor applications works in all modern web browsers, including mobile browsers.

" + + "

Blazor is a feature of ASP.NET Core 3, the popular cross platform web development framework from Microsoft that extends the .NET developer platform with tools and libraries for building web apps.

" + }, + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AllUsersRole, true), + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "

Copyright (c) 2019-2020 .NET Foundation

" + + "

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

" + + "

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

" + + "

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

" + }, + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.RegisteredRole, true), + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "

Oqtane allows you to control access to your content using security roles. This module is only visible to Registered Users of the site.

" + } + } + }); + _pageTemplates.Add(new PageTemplate + { + Name = "Private", + Parent = "", + Path = "private", + Icon = "lock-locked", + IsNavigation = true, + IsPersonalizable = false, + EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.RegisteredRole, true), + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.RegisteredRole, true), + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "

Oqtane allows you to control access to your content using security roles. This page is only visible to Registered Users of the site.

" + } + } + }); + _pageTemplates.Add(new PageTemplate + { + Name = "My Page", + Parent = "", + Path = "mypage", + Icon = "target", + IsNavigation = true, + IsPersonalizable = true, + EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AllUsersRole, true), + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "My Page", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AllUsersRole, true), + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "

Oqtane offers native support for user personalized pages. If a page is identified as personalizable by the site administrator in the page settings, when an authenticated user visits the page they will see an edit button at the top right corner of the page next to their username. When they click this button the sytem will create a new version of the page and allow them to edit the page content.

" + } + } + }); + + if (System.IO.File.Exists(_environment.WebRootPath + "\\images\\logo.png")) + { + string folderpath = _environment.ContentRootPath + "\\Content\\Tenants\\" + site.TenantId.ToString() + "\\Sites\\" + site.SiteId.ToString() + "\\"; + System.IO.Directory.CreateDirectory(folderpath); + if (!System.IO.File.Exists(folderpath + "logo.png")) + { + System.IO.File.Copy(_environment.WebRootPath + "\\images\\logo.png", folderpath + "logo.png"); + } + Folder folder = _folderRepository.GetFolder(site.SiteId, ""); + File file = _fileRepository.AddFile(new File { FolderId = folder.FolderId, Name = "logo.png", Extension = "png", Size = 8192, ImageHeight = 80, ImageWidth = 250 }); + site.LogoFileId = file.FileId; + _siteRepository.UpdateSite(site); + } + + return _pageTemplates; + } + } +} diff --git a/Oqtane.Server/Repository/FileRepository.cs b/Oqtane.Server/Repository/FileRepository.cs index fe5d3380..63d964eb 100644 --- a/Oqtane.Server/Repository/FileRepository.cs +++ b/Oqtane.Server/Repository/FileRepository.cs @@ -22,7 +22,7 @@ namespace Oqtane.Repository IEnumerable files = _db.File.Where(item => item.FolderId == folderId).Include(item => item.Folder); foreach (File file in files) { - file.Folder.Permissions = _permissions.EncodePermissions(folderId, permissions); + file.Folder.Permissions = _permissions.EncodePermissions(permissions); } return files; } @@ -47,7 +47,7 @@ namespace Oqtane.Repository if (file != null) { IEnumerable permissions = _permissions.GetPermissions("Folder", file.FolderId).ToList(); - file.Folder.Permissions = _permissions.EncodePermissions(file.FolderId, permissions); + file.Folder.Permissions = _permissions.EncodePermissions(permissions); } return file; } diff --git a/Oqtane.Server/Repository/FolderRepository.cs b/Oqtane.Server/Repository/FolderRepository.cs index b3d6f4e3..a52f372f 100644 --- a/Oqtane.Server/Repository/FolderRepository.cs +++ b/Oqtane.Server/Repository/FolderRepository.cs @@ -22,7 +22,7 @@ namespace Oqtane.Repository IEnumerable folders = _db.Folder.Where(item => item.SiteId == siteId); foreach(Folder folder in folders) { - folder.Permissions = _permissions.EncodePermissions(folder.FolderId, permissions); + folder.Permissions = _permissions.EncodePermissions(permissions.Where(item => item.EntityId == folder.FolderId)); } return folders; } @@ -49,7 +49,7 @@ namespace Oqtane.Repository if (folder != null) { IEnumerable permissions = _permissions.GetPermissions("Folder", folder.FolderId).ToList(); - folder.Permissions = _permissions.EncodePermissions(folder.FolderId, permissions); + folder.Permissions = _permissions.EncodePermissions(permissions); } return folder; } @@ -60,7 +60,7 @@ namespace Oqtane.Repository if (folder != null) { IEnumerable permissions = _permissions.GetPermissions("Folder", folder.FolderId).ToList(); - folder.Permissions = _permissions.EncodePermissions(folder.FolderId, permissions); + folder.Permissions = _permissions.EncodePermissions(permissions); } return folder; } diff --git a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs index 2fe96254..5be9bb50 100644 --- a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs @@ -14,7 +14,7 @@ namespace Oqtane.Repository Permission GetPermission(int permissionId); void DeletePermission(int permissionId); void DeletePermissions(int siteId, string entityName, int entityId); - string EncodePermissions(int entityId, IEnumerable permissionList); + string EncodePermissions(IEnumerable permissionList); IEnumerable DecodePermissions(string permissions, int siteId, string entityName, int entityId); } } diff --git a/Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs b/Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs new file mode 100644 index 00000000..659226ff --- /dev/null +++ b/Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Oqtane.Models; + +namespace Oqtane.Repository +{ + public interface ISiteTemplateRepository + { + IEnumerable GetSiteTemplates(); + } +} diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index 509a7bcf..9bed525e 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -86,7 +86,7 @@ namespace Oqtane.Repository } else { - moduledefinition.Permissions = _permissions.EncodePermissions(moduledef.ModuleDefinitionId, permissions); + moduledefinition.Permissions = _permissions.EncodePermissions(permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId)); } // remove module definition from list moduledefs.Remove(moduledef); diff --git a/Oqtane.Server/Repository/ModuleRepository.cs b/Oqtane.Server/Repository/ModuleRepository.cs index 76644286..18d9df3b 100644 --- a/Oqtane.Server/Repository/ModuleRepository.cs +++ b/Oqtane.Server/Repository/ModuleRepository.cs @@ -53,7 +53,7 @@ namespace Oqtane.Repository if (module != null) { List permissions = _permissions.GetPermissions("Module", module.ModuleId).ToList(); - module.Permissions = _permissions.EncodePermissions(module.ModuleId, permissions); + module.Permissions = _permissions.EncodePermissions(permissions); } return module; } diff --git a/Oqtane.Server/Repository/PageModuleRepository.cs b/Oqtane.Server/Repository/PageModuleRepository.cs index 70f7c201..ee85270d 100644 --- a/Oqtane.Server/Repository/PageModuleRepository.cs +++ b/Oqtane.Server/Repository/PageModuleRepository.cs @@ -26,7 +26,7 @@ namespace Oqtane.Repository IEnumerable permissions = _permissions.GetPermissions(pagemodules.FirstOrDefault().Module.SiteId, "Module").ToList(); foreach (PageModule pagemodule in pagemodules) { - pagemodule.Module.Permissions = _permissions.EncodePermissions(pagemodule.ModuleId, permissions); + pagemodule.Module.Permissions = _permissions.EncodePermissions(permissions.Where(item => item.EntityId == pagemodule.ModuleId)); } } return pagemodules; @@ -46,7 +46,7 @@ namespace Oqtane.Repository IEnumerable permissions = _permissions.GetPermissions(pagemodules.FirstOrDefault().Module.SiteId, "Module").ToList(); foreach (PageModule pagemodule in pagemodules) { - pagemodule.Module.Permissions = _permissions.EncodePermissions(pagemodule.ModuleId, permissions); + pagemodule.Module.Permissions = _permissions.EncodePermissions(permissions.Where(item => item.EntityId == pagemodule.ModuleId)); } } return pagemodules; @@ -73,7 +73,7 @@ namespace Oqtane.Repository if (pagemodule != null) { IEnumerable permissions = _permissions.GetPermissions("Module", pagemodule.ModuleId).ToList(); - pagemodule.Module.Permissions = _permissions.EncodePermissions(pagemodule.ModuleId, permissions); + pagemodule.Module.Permissions = _permissions.EncodePermissions(permissions); } return pagemodule; } @@ -85,7 +85,7 @@ namespace Oqtane.Repository if (pagemodule != null) { IEnumerable permissions = _permissions.GetPermissions("Module", pagemodule.ModuleId).ToList(); - pagemodule.Module.Permissions = _permissions.EncodePermissions(pagemodule.ModuleId, permissions); + pagemodule.Module.Permissions = _permissions.EncodePermissions(permissions); } return pagemodule; } diff --git a/Oqtane.Server/Repository/PageRepository.cs b/Oqtane.Server/Repository/PageRepository.cs index f5b726f3..cd3f78ee 100644 --- a/Oqtane.Server/Repository/PageRepository.cs +++ b/Oqtane.Server/Repository/PageRepository.cs @@ -24,7 +24,7 @@ namespace Oqtane.Repository IEnumerable pages = _db.Page.Where(item => item.SiteId == siteId && item.UserId == null); foreach(Page page in pages) { - page.Permissions = _permissions.EncodePermissions(page.PageId, permissions); + page.Permissions = _permissions.EncodePermissions(permissions.Where(item => item.EntityId == page.PageId)); } return pages; } @@ -51,7 +51,7 @@ namespace Oqtane.Repository if (page != null) { IEnumerable permissions = _permissions.GetPermissions("Page", page.PageId).ToList(); - page.Permissions = _permissions.EncodePermissions(page.PageId, permissions); + page.Permissions = _permissions.EncodePermissions(permissions); } return page; } @@ -69,7 +69,7 @@ namespace Oqtane.Repository if (page != null) { IEnumerable permissions = _permissions.GetPermissions("Page", page.PageId).ToList(); - page.Permissions = _permissions.EncodePermissions(page.PageId, permissions); + page.Permissions = _permissions.EncodePermissions(permissions); } } return page; @@ -81,7 +81,7 @@ namespace Oqtane.Repository if (page != null) { IEnumerable permissions = _permissions.GetPermissions("Page", page.PageId).ToList(); - page.Permissions = _permissions.EncodePermissions(page.PageId, permissions); + page.Permissions = _permissions.EncodePermissions(permissions); } return page; } diff --git a/Oqtane.Server/Repository/PermissionRepository.cs b/Oqtane.Server/Repository/PermissionRepository.cs index 991d222c..fd9670d3 100644 --- a/Oqtane.Server/Repository/PermissionRepository.cs +++ b/Oqtane.Server/Repository/PermissionRepository.cs @@ -101,14 +101,14 @@ namespace Oqtane.Repository } // permissions are stored in the format "{permissionname:!rolename1;![userid1];rolename2;rolename3;[userid2];[userid3]}" where "!" designates Deny permissions - public string EncodePermissions(int entityId, IEnumerable permissionList) + public string EncodePermissions(IEnumerable permissionList) { List permissionstrings = new List(); string permissionname = ""; string permissions = ""; StringBuilder permissionsbuilder = new StringBuilder(); string securityid = ""; - foreach (Permission permission in permissionList.Where(item => item.EntityId == entityId).OrderBy(item => item.PermissionName)) + foreach (Permission permission in permissionList.OrderBy(item => item.PermissionName)) { // permission collections are grouped by permissionname if (permissionname != permission.PermissionName) diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 7dead20c..d1d0d1d5 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Oqtane.Infrastructure.Interfaces; using Oqtane.Models; using Oqtane.Modules; using Oqtane.Shared; @@ -22,10 +24,12 @@ namespace Oqtane.Repository private readonly IModuleRepository _moduleRepository; private readonly IPageModuleRepository _pageModuleRepository; private readonly IModuleDefinitionRepository _moduleDefinitionRepository; + private readonly IPermissionRepository _permissionRepository; private readonly IServiceProvider _serviceProvider; - private readonly List _siteTemplate; + private readonly List _pageTemplates; + private readonly IConfigurationRoot _config; - public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IFileRepository fileRepository, IPageRepository pageRepository, IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IServiceProvider serviceProvider) + public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IFileRepository fileRepository, IPageRepository pageRepository, IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IPermissionRepository permissionRepository, IServiceProvider serviceProvider, IConfigurationRoot config) { _db = context; _roleRepository = roleRepository; @@ -36,94 +40,259 @@ namespace Oqtane.Repository _moduleRepository = moduleRepository; _pageModuleRepository = pageModuleRepository; _moduleDefinitionRepository = moduleDefinitionRepository; + _permissionRepository = permissionRepository; _serviceProvider = serviceProvider; + _config = config; - // define the default site template - _siteTemplate = new List(); - _siteTemplate.Add(new PageTemplate { Name = "Home", Parent = "", Path = "", Icon = "home", IsNavigation = true, IsPersonalizable = false, EditMode = false, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Welcome To Oqtane...", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", - Content = "

Oqtane is an open source modular application framework built from the ground up using modern .NET Core technology. It leverages the revolutionary new Blazor component model to create a fully dynamic web development experience which can be executed on a client or server. Whether you are looking for a platform to accelerate your web development efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.

" + - "



Join Our Community  Clone Our Repo

" + - "

Blazor is a single-page app framework that lets you build interactive web applications using C# instead of JavaScript. Client-side Blazor relies on WebAssembly, an open web standard that does not require plugins or code transpilation in order to run natively in a web browser. Server-side Blazor uses SignalR to host your application on a web server and provide a responsive and robust debugging experience. Blazor applications works in all modern web browsers, including mobile browsers.

" + - "

Blazor is a feature of ASP.NET Core 3, the popular cross platform web development framework from Microsoft that extends the .NET developer platform with tools and libraries for building web apps.

" - }, - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", - Content = "

Copyright (c) 2019-2020 .NET Foundation

" + - "

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

" + - "

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

" + - "

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

" - } + // define the default site template ( admin pages ) + _pageTemplates = new List(); + _pageTemplates.Add(new PageTemplate { Name = "Admin", Parent = "", Path = "admin", Icon = "", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Dashboard, Oqtane.Client", Title = "Admin Dashboard", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + }}); + _pageTemplates.Add(new PageTemplate { Name = "Site Management", Parent = "Admin", Path = "admin/sites", Icon = "globe", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Sites, Oqtane.Client", Title = "Site Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } } - }); - _siteTemplate.Add(new PageTemplate { Name = "Private", Parent = "", Path = "private", Icon = "lock-locked", IsNavigation = true, IsPersonalizable = false, EditMode = false, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Registered Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Registered Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", - Content = "

Oqtane allows you to control access to your content using security roles. This page is only visible to Registered Users of the site.

" - } + }); + _pageTemplates.Add(new PageTemplate { Name = "Site Settings", Parent = "Admin", Path = "admin/site", Icon = "home", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Site, Oqtane.Client", Title = "Site Settings", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } } - }); - _siteTemplate.Add(new PageTemplate { Name = "My Page", Parent = "", Path = "mypage", Icon = "target", IsNavigation = true, IsPersonalizable = true, EditMode = false, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "My Page", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", - Content = "

Oqtane offers native support for user personalized pages. If a page is identified as personalizable by the site administrator in the page settings, when an authenticated user visits the page they will see an edit button at the top right corner of the page next to their username. When they click this button the sytem will create a new version of the page and allow them to edit the page content.

" - } + }); + _pageTemplates.Add(new PageTemplate { Name = "Page Management", Parent = "Admin", Path = "admin/pages", Icon = "layers", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Pages, Oqtane.Client", Title = "Page Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } } - }); - _siteTemplate.Add(new PageTemplate { Name = "Admin", Parent = "", Path = "admin", Icon = "", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Dashboard, Oqtane.Client", Title = "Admin Dashboard", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Site Management", Parent = "Admin", Path = "admin/sites", Icon = "globe", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Sites, Oqtane.Client", Title = "Site Management", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Site Settings", Parent = "Admin", Path = "admin/site", Icon = "home", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Site, Oqtane.Client", Title = "Site Settings", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Page Management", Parent = "Admin", Path = "admin/pages", Icon = "layers", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Pages, Oqtane.Client", Title = "Page Management", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "User Management", Parent = "Admin", Path = "admin/users", Icon = "people", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Users, Oqtane.Client", Title = "User Management", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Profile Management", Parent = "Admin", Path = "admin/profiles", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Profiles, Oqtane.Client", Title = "Profile Management", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Role Management", Parent = "Admin", Path = "admin/roles", Icon = "lock-locked", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Roles, Oqtane.Client", Title = "Role Management", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Event Log", Parent = "Admin", Path = "admin/log", Icon = "magnifying-glass", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Logs, Oqtane.Client", Title = "Event Log", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "File Management", Parent = "Admin", Path = "admin/files", Icon = "file", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Files, Oqtane.Client", Title = "File Management", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Recycle Bin", Parent = "Admin", Path = "admin/recyclebin", Icon = "trash", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.RecycleBin, Oqtane.Client", Title = "Recycle Bin", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Tenant Management", Parent = "Admin", Path = "admin/tenants", Icon = "list", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Tenants, Oqtane.Client", Title = "Tenant Management", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Module Management", Parent = "Admin", Path = "admin/modules", Icon = "browser", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.ModuleDefinitions, Oqtane.Client", Title = "Module Management", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = "brush", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Themes, Oqtane.Client", Title = "Theme Management", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = "timer", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Jobs, Oqtane.Client", Title = "Scheduled Jobs", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Upgrade Service", Parent = "Admin", Path = "admin/upgrade", Icon = "aperture", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Upgrade, Oqtane.Client", Title = "Upgrade Service", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Login", Parent = "", Path = "login", Icon = "lock-locked", IsNavigation = false, IsPersonalizable = false, EditMode = false, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Login, Oqtane.Client", Title = "User Login", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Register", Parent = "", Path = "register", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Register, Oqtane.Client", Title = "User Registration", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Reset", Parent = "", Path = "reset", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Reset, Oqtane.Client", Title = "Password Reset", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); - _siteTemplate.Add(new PageTemplate { Name = "Profile", Parent = "", Path = "profile", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.UserProfile, Oqtane.Client", Title = "User Profile", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"All Users;Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" } - }}); + }); + _pageTemplates.Add(new PageTemplate { Name = "User Management", Parent = "Admin", Path = "admin/users", Icon = "people", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Users, Oqtane.Client", Title = "User Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Profile Management", Parent = "Admin", Path = "admin/profiles", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Profiles, Oqtane.Client", Title = "Profile Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Role Management", Parent = "Admin", Path = "admin/roles", Icon = "lock-locked", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Roles, Oqtane.Client", Title = "Role Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Event Log", Parent = "Admin", Path = "admin/log", Icon = "magnifying-glass", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Logs, Oqtane.Client", Title = "Event Log", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "File Management", Parent = "Admin", Path = "admin/files", Icon = "file", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Files, Oqtane.Client", Title = "File Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Recycle Bin", Parent = "Admin", Path = "admin/recyclebin", Icon = "trash", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.RecycleBin, Oqtane.Client", Title = "Recycle Bin", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Tenant Management", Parent = "Admin", Path = "admin/tenants", Icon = "list", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Tenants, Oqtane.Client", Title = "Tenant Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Module Management", Parent = "Admin", Path = "admin/modules", Icon = "browser", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.ModuleDefinitions, Oqtane.Client", Title = "Module Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = "brush", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Themes, Oqtane.Client", Title = "Theme Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = "timer", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Jobs, Oqtane.Client", Title = "Scheduled Jobs", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Upgrade Service", Parent = "Admin", Path = "admin/upgrade", Icon = "aperture", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Upgrade, Oqtane.Client", Title = "Upgrade Service", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Login", Parent = "", Path = "login", Icon = "lock-locked", IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Login, Oqtane.Client", Title = "User Login", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Register", Parent = "", Path = "register", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Register, Oqtane.Client", Title = "User Registration", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + + _pageTemplates.Add(new PageTemplate { Name = "Reset", Parent = "", Path = "reset", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Reset, Oqtane.Client", Title = "Password Reset", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); + _pageTemplates.Add(new PageTemplate { Name = "Profile", Parent = "", Path = "profile", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions(new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List { + new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.UserProfile, Oqtane.Client", Title = "User Profile", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), Content = "" } + } + }); } public IEnumerable GetSites() @@ -160,6 +329,7 @@ namespace Oqtane.Repository private void CreateSite(Site site) { + // create default entities for site List roles = _roleRepository.GetRoles(site.SiteId, true).ToList(); if (!roles.Where(item => item.Name == Constants.AllUsersRole).Any()) { @@ -184,15 +354,38 @@ namespace Oqtane.Repository Folder folder = _folderRepository.AddFolder(new Folder { SiteId = site.SiteId, ParentId = null, Name = "Root", Path = "", Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); _folderRepository.AddFolder(new Folder { SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = "Users\\", Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); - if (site.Name == "Default Site") - { - File file = _fileRepository.AddFile(new File { FolderId = folder.FolderId, Name = "logo.png", Extension = "png", Size = 8192, ImageHeight = 80, ImageWidth = 250 }); - site.LogoFileId = file.FileId; - UpdateSite(site); - } + CreatePages(site, _pageTemplates); + + // process site template + if (string.IsNullOrEmpty(site.SiteTemplateType)) + { + var section = _config.GetSection("SiteTemplate"); + if (section.Exists()) + { + site.SiteTemplateType = section.Value; + } + else + { + site.SiteTemplateType = Constants.DefaultSiteTemplate; + } + } + Type siteTemplateType = Type.GetType(site.SiteTemplateType); + if (siteTemplateType != null) + { + var siteTemplateObject = ActivatorUtilities.CreateInstance(_serviceProvider, siteTemplateType); + List pageTemplates = ((ISiteTemplate)siteTemplateObject).CreateSite(site); + if (pageTemplates != null && pageTemplates.Count > 0) + { + CreatePages(site, pageTemplates); + } + } + } + + private void CreatePages(Site site, List pageTemplates) + { List moduledefinitions = _moduleDefinitionRepository.GetModuleDefinitions(site.SiteId).ToList(); - foreach (PageTemplate pagetemplate in _siteTemplate) + foreach (PageTemplate pagetemplate in pageTemplates) { int? parentid = null; if (pagetemplate.Parent != "") @@ -220,7 +413,7 @@ namespace Oqtane.Repository }; page = _pageRepository.AddPage(page); - foreach(PageTemplateModule pagetemplatemodule in pagetemplate.PageTemplateModules) + foreach (PageTemplateModule pagetemplatemodule in pagetemplate.PageTemplateModules) { if (pagetemplatemodule.ModuleDefinitionName != "") { @@ -264,7 +457,6 @@ namespace Oqtane.Repository }; _pageModuleRepository.AddPageModule(pagemodule); } - } } } diff --git a/Oqtane.Server/Repository/SiteTemplateRepository.cs b/Oqtane.Server/Repository/SiteTemplateRepository.cs new file mode 100644 index 00000000..4d3b5f7b --- /dev/null +++ b/Oqtane.Server/Repository/SiteTemplateRepository.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Models; + +namespace Oqtane.Repository +{ + public class SiteTemplateRepository : ISiteTemplateRepository + { + private readonly IServiceProvider _serviceProvider; + + public SiteTemplateRepository(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + private List LoadSiteTemplates() + { + List siteTemplates = new List(); + + // iterate through Oqtane site template assemblies + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() + .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".SiteTemplate.")).ToArray(); + foreach (Assembly assembly in assemblies) + { + siteTemplates = LoadSiteTemplatesFromAssembly(siteTemplates, assembly); + } + + return siteTemplates; + } + + private List LoadSiteTemplatesFromAssembly(List siteTemplates, Assembly assembly) + { + SiteTemplate siteTemplate; + Type[] siteTemplateTypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ISiteTemplate))).ToArray(); + foreach (Type siteTemplateType in siteTemplateTypes) + { + var siteTemplateObject = ActivatorUtilities.CreateInstance(_serviceProvider, siteTemplateType); + siteTemplate = new SiteTemplate + { + Name = (string)siteTemplateType.GetProperty("Name").GetValue(siteTemplateObject), + TypeName = siteTemplateType.AssemblyQualifiedName + }; + siteTemplates.Add(siteTemplate); + } + return siteTemplates; + } + + public IEnumerable GetSiteTemplates() + { + return LoadSiteTemplates(); + } + } +} diff --git a/Oqtane.Server/Security/UserPermissions.cs b/Oqtane.Server/Security/UserPermissions.cs index 2e20b062..72108b5d 100644 --- a/Oqtane.Server/Security/UserPermissions.cs +++ b/Oqtane.Server/Security/UserPermissions.cs @@ -19,7 +19,7 @@ namespace Oqtane.Security public bool IsAuthorized(ClaimsPrincipal user, string entityName, int entityId, string permissionName) { - return IsAuthorized(user, permissionName, _permissions.EncodePermissions(entityId, _permissions.GetPermissions(entityName, entityId, permissionName).ToList())); + return IsAuthorized(user, permissionName, _permissions.EncodePermissions(_permissions.GetPermissions(entityName, entityId, permissionName).ToList())); } public bool IsAuthorized(ClaimsPrincipal user, string permissionName, string permissions) diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 79a2d31c..ff1de600 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -104,6 +104,7 @@ namespace Oqtane services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddSingleton(); @@ -181,9 +182,11 @@ namespace Oqtane services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddOqtaneModules(); services.AddOqtaneThemes(); + services.AddOqtaneSiteTemplates(); services.AddMvc() .AddOqtaneApplicationParts() @@ -332,9 +335,11 @@ namespace Oqtane services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddOqtaneModules(); services.AddOqtaneThemes(); + services.AddOqtaneSiteTemplates(); services.AddMvc() .AddOqtaneApplicationParts() diff --git a/Oqtane.Server/wwwroot/images/logo.png b/Oqtane.Server/wwwroot/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..94454bf601612d061901f9d33121d19f5996d8bf GIT binary patch literal 7963 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 ZEyo$1+#R*=J79AfLqkPJxdCDq^FMa;ML_@n literal 0 HcmV?d00001 diff --git a/Oqtane.Shared/Models/GenericResponse.cs b/Oqtane.Shared/Models/Installation.cs similarity index 79% rename from Oqtane.Shared/Models/GenericResponse.cs rename to Oqtane.Shared/Models/Installation.cs index 2281d79f..4dc25840 100644 --- a/Oqtane.Shared/Models/GenericResponse.cs +++ b/Oqtane.Shared/Models/Installation.cs @@ -1,6 +1,6 @@ namespace Oqtane.Models { - public class GenericResponse + public class Installation { public bool Success { get; set; } public string Message { get; set; } diff --git a/Oqtane.Shared/Models/Permission.cs b/Oqtane.Shared/Models/Permission.cs index 0f7f7b61..aff910f4 100644 --- a/Oqtane.Shared/Models/Permission.cs +++ b/Oqtane.Shared/Models/Permission.cs @@ -19,5 +19,23 @@ namespace Oqtane.Models public DateTime ModifiedOn { get; set; } public Role Role { get; set; } + + public Permission() + { + } + + public Permission(string permissionName, string roleName, bool isAuthorized) + { + PermissionName = permissionName; + Role = new Role { Name = roleName }; + IsAuthorized = isAuthorized; + } + + public Permission(string permissionName, int userId, bool isAuthorized) + { + PermissionName = permissionName; + UserId = userId; + IsAuthorized = isAuthorized; + } } } diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs index bcb358df..700209b2 100644 --- a/Oqtane.Shared/Models/Site.cs +++ b/Oqtane.Shared/Models/Site.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations.Schema; namespace Oqtane.Models { @@ -12,7 +13,6 @@ namespace Oqtane.Models public string DefaultLayoutType { get; set; } public string DefaultContainerType { get; set; } - public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } public string ModifiedBy { get; set; } @@ -20,5 +20,8 @@ namespace Oqtane.Models public string DeletedBy { get; set; } public DateTime? DeletedOn { get; set; } public bool IsDeleted { get; set; } + + [NotMapped] + public string SiteTemplateType { get; set; } } } diff --git a/Oqtane.Shared/Models/PageTemplate.cs b/Oqtane.Shared/Models/SiteTemplate.cs similarity index 86% rename from Oqtane.Shared/Models/PageTemplate.cs rename to Oqtane.Shared/Models/SiteTemplate.cs index c87d2e58..e4e7a02d 100644 --- a/Oqtane.Shared/Models/PageTemplate.cs +++ b/Oqtane.Shared/Models/SiteTemplate.cs @@ -2,6 +2,12 @@ namespace Oqtane.Models { + public class SiteTemplate + { + public string Name { get; set; } + public string TypeName { get; set; } + } + public class PageTemplate { public string Name { get; set; } diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index c5b2926d..198db352 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -26,6 +26,8 @@ public const string ErrorModule = "Oqtane.Modules.Admin.Error.{Action}, Oqtane.Client"; public const string ModuleMessageComponent = "Oqtane.Modules.Controls.ModuleMessage, Oqtane.Client"; + public const string DefaultSiteTemplate = "Oqtane.SiteTemplates.DefaultSiteTemplate, Oqtane.Server"; + public const string ContentUrl = "/api/file/download/"; public const string HostUser = "host"; From 7da2824e503e7701abd931f8d863750f43868072 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 19 Mar 2020 15:03:11 -0400 Subject: [PATCH 078/265] fixed issues with client-side Blazor --- Oqtane.Client/Modules/Admin/Login/Index.razor | 4 ++-- Oqtane.Client/Oqtane.Client.csproj | 4 ++-- Oqtane.Client/Program.cs | 5 +++- .../Interfaces/IModuleDefinitionService.cs | 3 ++- .../Services/ModuleDefinitionService.cs | 11 ++++----- Oqtane.Client/Themes/Controls/Login.razor | 4 ++-- Oqtane.Client/UI/PageState.cs | 1 + Oqtane.Client/UI/Runtime.cs | 8 +++++++ Oqtane.Client/UI/SiteRouter.razor | 18 ++++++++++++-- Oqtane.Server/Oqtane.Server.csproj | 4 ++-- Oqtane.Server/Program.cs | 8 ++++--- Oqtane.Server/Repository/SiteRepository.cs | 8 +++++++ Oqtane.Server/Startup.cs | 4 +++- Oqtane.Shared/Oqtane.Shared.csproj | 4 ++-- Oqtane.sln | 24 +++++++++---------- 15 files changed, 73 insertions(+), 37 deletions(-) create mode 100644 Oqtane.Client/UI/Runtime.cs diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index 5310049d..41f36dc3 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -81,8 +81,7 @@ private async Task Login() { - var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); - if (authstateprovider == null) + if (PageState.Runtime == Runtime.Server) { // server-side Blazor User user = new User(); @@ -116,6 +115,7 @@ if (user.IsAuthenticated) { await logger.LogInformation("Login Successful For Username {Username}", _username); + var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); authstateprovider.NotifyAuthenticationChanged(); NavigationManager.NavigateTo(NavigateUrl(_returnUrl, "reload")); } diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index d66027f3..a95adf1b 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -10,7 +10,7 @@ 7.3 3.0 - Debug;Release;Wasm + Debug;Release;WebAssembly 0.0.9 Oqtane Shaun Walker @@ -24,7 +24,7 @@ Oqtane - + TRACE;WASM diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index ca04c0c4..b256ca9b 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -1,4 +1,7 @@ -namespace Oqtane +// DO NOT REMOVE - needed for client-side Blazor +using Microsoft.AspNetCore.Blazor.Hosting; + +namespace Oqtane.Client { public class Program { diff --git a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs index 04450500..e0bc3a24 100644 --- a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs +++ b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs @@ -1,4 +1,5 @@ using Oqtane.Models; +using Oqtane.UI; using System.Collections.Generic; using System.Threading.Tasks; @@ -11,6 +12,6 @@ namespace Oqtane.Services Task UpdateModuleDefinitionAsync(ModuleDefinition moduleDefinition); Task InstallModuleDefinitionsAsync(); Task DeleteModuleDefinitionAsync(int moduleDefinitionId, int siteId); - Task LoadModuleDefinitionsAsync(int siteId); + Task LoadModuleDefinitionsAsync(int siteId, Runtime runtime); } } diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index ea1e658f..0d3cc4aa 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Components; using System; using System.Reflection; using Oqtane.Shared; -using Oqtane.Providers; +using Oqtane.UI; namespace Oqtane.Services { @@ -16,14 +16,12 @@ namespace Oqtane.Services private readonly HttpClient _http; private readonly SiteState _siteState; private readonly NavigationManager _navigationManager; - private readonly IServiceProvider _serviceProvider; - public ModuleDefinitionService(HttpClient http, SiteState siteState, NavigationManager navigationManager, IServiceProvider serviceProvider) + public ModuleDefinitionService(HttpClient http, SiteState siteState, NavigationManager navigationManager) { _http = http; _siteState = siteState; _navigationManager = navigationManager; - _serviceProvider = serviceProvider; } private string Apiurl @@ -57,14 +55,13 @@ namespace Oqtane.Services await _http.DeleteAsync(Apiurl + "/" + moduleDefinitionId.ToString() + "?siteid=" + siteId.ToString()); } - public async Task LoadModuleDefinitionsAsync(int 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 - var authstateprovider = (IdentityAuthenticationStateProvider)_serviceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); - if (authstateprovider != null) + 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(); diff --git a/Oqtane.Client/Themes/Controls/Login.razor b/Oqtane.Client/Themes/Controls/Login.razor index f8dbddd3..5778af99 100644 --- a/Oqtane.Client/Themes/Controls/Login.razor +++ b/Oqtane.Client/Themes/Controls/Login.razor @@ -33,8 +33,7 @@ { await UserService.LogoutUserAsync(PageState.User); - var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); - if (authstateprovider == null) + if (PageState.Runtime == Runtime.Server) { // server-side Blazor var interop = new Interop(jsRuntime); @@ -45,6 +44,7 @@ else { // client-side Blazor + var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); authstateprovider.NotifyAuthenticationChanged(); NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload")); } diff --git a/Oqtane.Client/UI/PageState.cs b/Oqtane.Client/UI/PageState.cs index e801ad73..a2678802 100644 --- a/Oqtane.Client/UI/PageState.cs +++ b/Oqtane.Client/UI/PageState.cs @@ -18,5 +18,6 @@ namespace Oqtane.UI public string Action { get; set; } public bool EditMode { get; set; } public DateTime LastSyncDate { get; set; } + public Runtime Runtime { get; set; } } } diff --git a/Oqtane.Client/UI/Runtime.cs b/Oqtane.Client/UI/Runtime.cs new file mode 100644 index 00000000..dcc886b1 --- /dev/null +++ b/Oqtane.Client/UI/Runtime.cs @@ -0,0 +1,8 @@ +namespace Oqtane.UI +{ + public enum Runtime + { + Server, + WebAssembly + } +} diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 6bc85872..205c4ef4 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -13,6 +13,7 @@ @inject ILogService LogService @using System.Diagnostics.CodeAnalysis @using Oqtane.Enums +@using System.Runtime.InteropServices @implements IHandleAfterRender @DynamicComponent @@ -81,6 +82,7 @@ bool editmode = false; Reload reload = Reload.None; DateTime lastsyncdate = DateTime.UtcNow; + Runtime runtime = GetRuntime(); // get Url path and querystring ( and remove anchors ) string path = new Uri(_absoluteUri).PathAndQuery.Substring(1); @@ -162,7 +164,7 @@ if (PageState == null || reload >= Reload.Site) { - await ModuleDefinitionService.LoadModuleDefinitionsAsync(site.SiteId); + await ModuleDefinitionService.LoadModuleDefinitionsAsync(site.SiteId, runtime); pages = await PageService.GetPagesAsync(site.SiteId); } else @@ -248,7 +250,8 @@ Uri = new Uri(_absoluteUri, UriKind.Absolute), QueryString = querystring, ModuleId = moduleid, - Action = action + Action = action, + Runtime = runtime }; if (PageState != null && (PageState.ModuleId != _pagestate.ModuleId || PageState.Action != _pagestate.Action)) @@ -458,4 +461,15 @@ return modules; } + private Runtime GetRuntime() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("WEBASSEMBLY"))) + { + return Runtime.WebAssembly; + } + else + { + return Runtime.Server; + } + } } diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 0e0e0d95..32847187 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -8,7 +8,7 @@ https://dotnet.myget.org/F/blazor-dev/api/v3/index.json; true - Debug;Release;Wasm + Debug;Release;WebAssembly 0.0.9 Oqtane Shaun Walker @@ -22,7 +22,7 @@ Oqtane - + TRACE;WASM diff --git a/Oqtane.Server/Program.cs b/Oqtane.Server/Program.cs index 89bb47b3..b440e758 100644 --- a/Oqtane.Server/Program.cs +++ b/Oqtane.Server/Program.cs @@ -1,9 +1,11 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; +// DO NOT REMOVE - needed for client-side Blazor +using Microsoft.AspNetCore.Blazor.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.AspNetCore; -// used by client-side Blazor - -namespace Oqtane +namespace Oqtane.Server { public class Program { diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index d1d0d1d5..096871c7 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -243,12 +243,14 @@ namespace Oqtane.Repository _pageTemplates.Add(new PageTemplate { Name = "Login", Parent = "", Path = "login", Icon = "lock-locked", IsNavigation = false, IsPersonalizable = false, EditMode = false, PagePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Login, Oqtane.Client", Title = "User Login", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions( new List { new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), Content = "" } } @@ -256,12 +258,14 @@ namespace Oqtane.Repository _pageTemplates.Add(new PageTemplate { Name = "Register", Parent = "", Path = "register", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, PagePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Register, Oqtane.Client", Title = "User Registration", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions( new List { new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), Content = "" } } @@ -270,12 +274,14 @@ namespace Oqtane.Repository _pageTemplates.Add(new PageTemplate { Name = "Reset", Parent = "", Path = "reset", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, PagePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Reset, Oqtane.Client", Title = "Password Reset", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions( new List { new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), Content = "" } } @@ -283,12 +289,14 @@ namespace Oqtane.Repository _pageTemplates.Add(new PageTemplate { Name = "Profile", Parent = "", Path = "profile", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, PagePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.RegisteredRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.UserProfile, Oqtane.Client", Title = "User Profile", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions( new List { new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.RegisteredRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), Content = "" } } diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 92e3476f..b961fd06 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -19,7 +19,9 @@ using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; using Oqtane.Security; using Oqtane.Services; -using Oqtane.Shared; // needed for WASM +// DO NOT REMOVE - needed for client-side Blazor +using Oqtane.Shared; +using Microsoft.AspNetCore.ResponseCompression; namespace Oqtane { diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index d252b2aa..830bda51 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -3,7 +3,7 @@ netstandard2.1 7.3 - Debug;Release;Wasm + Debug;Release;WebAssembly 0.0.9 Oqtane Shaun Walker @@ -17,7 +17,7 @@ Oqtane - + TRACE;WASM diff --git a/Oqtane.sln b/Oqtane.sln index 4c51a92b..3832d333 100644 --- a/Oqtane.sln +++ b/Oqtane.sln @@ -11,45 +11,45 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Shared", "Oqtane.Sha EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Upgrade", "Oqtane.Upgrade\Oqtane.Upgrade.csproj", "{2E8C6889-37CF-4C8D-88B1-505547F25098}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Oqtane.Test", "Oqtane.Test\Oqtane.Test.csproj", "{823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Test", "Oqtane.Test\Oqtane.Test.csproj", "{823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU - Wasm|Any CPU = Wasm|Any CPU + WebAssembly|Any CPU = WebAssembly|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {083BB22D-DF24-43A2-95E5-8F385CCB3318}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {083BB22D-DF24-43A2-95E5-8F385CCB3318}.Debug|Any CPU.Build.0 = Debug|Any CPU {083BB22D-DF24-43A2-95E5-8F385CCB3318}.Release|Any CPU.ActiveCfg = Release|Any CPU {083BB22D-DF24-43A2-95E5-8F385CCB3318}.Release|Any CPU.Build.0 = Release|Any CPU - {083BB22D-DF24-43A2-95E5-8F385CCB3318}.Wasm|Any CPU.ActiveCfg = Wasm|Any CPU - {083BB22D-DF24-43A2-95E5-8F385CCB3318}.Wasm|Any CPU.Build.0 = Wasm|Any CPU + {083BB22D-DF24-43A2-95E5-8F385CCB3318}.WebAssembly|Any CPU.ActiveCfg = WebAssembly|Any CPU + {083BB22D-DF24-43A2-95E5-8F385CCB3318}.WebAssembly|Any CPU.Build.0 = WebAssembly|Any CPU {FD15B24A-7F6A-4830-9CA2-9C621771C330}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD15B24A-7F6A-4830-9CA2-9C621771C330}.Debug|Any CPU.Build.0 = Debug|Any CPU {FD15B24A-7F6A-4830-9CA2-9C621771C330}.Release|Any CPU.ActiveCfg = Release|Any CPU {FD15B24A-7F6A-4830-9CA2-9C621771C330}.Release|Any CPU.Build.0 = Release|Any CPU - {FD15B24A-7F6A-4830-9CA2-9C621771C330}.Wasm|Any CPU.ActiveCfg = Wasm|Any CPU - {FD15B24A-7F6A-4830-9CA2-9C621771C330}.Wasm|Any CPU.Build.0 = Wasm|Any CPU + {FD15B24A-7F6A-4830-9CA2-9C621771C330}.WebAssembly|Any CPU.ActiveCfg = WebAssembly|Any CPU + {FD15B24A-7F6A-4830-9CA2-9C621771C330}.WebAssembly|Any CPU.Build.0 = WebAssembly|Any CPU {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Release|Any CPU.Build.0 = Release|Any CPU - {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Wasm|Any CPU.ActiveCfg = Wasm|Any CPU - {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Wasm|Any CPU.Build.0 = Wasm|Any CPU + {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.WebAssembly|Any CPU.ActiveCfg = WebAssembly|Any CPU + {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.WebAssembly|Any CPU.Build.0 = WebAssembly|Any CPU {2E8C6889-37CF-4C8D-88B1-505547F25098}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2E8C6889-37CF-4C8D-88B1-505547F25098}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E8C6889-37CF-4C8D-88B1-505547F25098}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E8C6889-37CF-4C8D-88B1-505547F25098}.Release|Any CPU.Build.0 = Release|Any CPU - {2E8C6889-37CF-4C8D-88B1-505547F25098}.Wasm|Any CPU.ActiveCfg = Debug|Any CPU - {2E8C6889-37CF-4C8D-88B1-505547F25098}.Wasm|Any CPU.Build.0 = Debug|Any CPU + {2E8C6889-37CF-4C8D-88B1-505547F25098}.WebAssembly|Any CPU.ActiveCfg = Debug|Any CPU + {2E8C6889-37CF-4C8D-88B1-505547F25098}.WebAssembly|Any CPU.Build.0 = Debug|Any CPU {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.Debug|Any CPU.Build.0 = Debug|Any CPU {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.Release|Any CPU.ActiveCfg = Release|Any CPU {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.Release|Any CPU.Build.0 = Release|Any CPU - {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.Wasm|Any CPU.ActiveCfg = Debug|Any CPU - {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.Wasm|Any CPU.Build.0 = Debug|Any CPU + {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.WebAssembly|Any CPU.ActiveCfg = Debug|Any CPU + {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.WebAssembly|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 3077bb617518d9630a686b7386df51f4ccf660c1 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Fri, 20 Mar 2020 14:21:41 +0100 Subject: [PATCH 079/265] Site repository refactoring Not necessary to build default pages in constructor. --- Oqtane.Server/Repository/SiteRepository.cs | 596 ++++++++++++++------- 1 file changed, 397 insertions(+), 199 deletions(-) diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 096871c7..09ec53fc 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -25,11 +25,14 @@ namespace Oqtane.Repository private readonly IPageModuleRepository _pageModuleRepository; private readonly IModuleDefinitionRepository _moduleDefinitionRepository; private readonly IPermissionRepository _permissionRepository; + private readonly IServiceProvider _serviceProvider; - private readonly List _pageTemplates; + private readonly IConfigurationRoot _config; - public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IFileRepository fileRepository, IPageRepository pageRepository, IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IPermissionRepository permissionRepository, IServiceProvider serviceProvider, IConfigurationRoot config) + public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IFileRepository fileRepository, IPageRepository pageRepository, + IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IPermissionRepository permissionRepository, IServiceProvider serviceProvider, + IConfigurationRoot config) { _db = context; _roleRepository = roleRepository; @@ -43,264 +46,440 @@ namespace Oqtane.Repository _permissionRepository = permissionRepository; _serviceProvider = serviceProvider; _config = config; + } - // define the default site template ( admin pages ) - _pageTemplates = new List(); - _pageTemplates.Add(new PageTemplate { Name = "Admin", Parent = "", Path = "admin", Icon = "", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + private List CreateAdminPages(List pageTemplates = null) + { + if (pageTemplates == null) pageTemplates = new List(); + + pageTemplates.Add(new PageTemplate + { + Name = "Admin", Parent = "", Path = "admin", Icon = "", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Dashboard, Oqtane.Client", Title = "Admin Dashboard", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - }}); - _pageTemplates.Add(new PageTemplate { Name = "Site Management", Parent = "Admin", Path = "admin/sites", Icon = "globe", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Sites, Oqtane.Client", Title = "Site Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Dashboard, Oqtane.Client", Title = "Admin Dashboard", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Site Settings", Parent = "Admin", Path = "admin/site", Icon = "home", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Site Management", Parent = "Admin", Path = "admin/sites", Icon = "globe", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Site, Oqtane.Client", Title = "Site Settings", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Sites, Oqtane.Client", Title = "Site Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Page Management", Parent = "Admin", Path = "admin/pages", Icon = "layers", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Site Settings", Parent = "Admin", Path = "admin/site", Icon = "home", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Pages, Oqtane.Client", Title = "Page Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Site, Oqtane.Client", Title = "Site Settings", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "User Management", Parent = "Admin", Path = "admin/users", Icon = "people", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Page Management", Parent = "Admin", Path = "admin/pages", Icon = "layers", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Users, Oqtane.Client", Title = "User Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Pages, Oqtane.Client", Title = "Page Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Profile Management", Parent = "Admin", Path = "admin/profiles", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "User Management", Parent = "Admin", Path = "admin/users", Icon = "people", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Profiles, Oqtane.Client", Title = "Profile Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Users, Oqtane.Client", Title = "User Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Role Management", Parent = "Admin", Path = "admin/roles", Icon = "lock-locked", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Profile Management", Parent = "Admin", Path = "admin/profiles", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Roles, Oqtane.Client", Title = "Role Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Profiles, Oqtane.Client", Title = "Profile Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Event Log", Parent = "Admin", Path = "admin/log", Icon = "magnifying-glass", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Role Management", Parent = "Admin", Path = "admin/roles", Icon = "lock-locked", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Logs, Oqtane.Client", Title = "Event Log", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Roles, Oqtane.Client", Title = "Role Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "File Management", Parent = "Admin", Path = "admin/files", Icon = "file", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Event Log", Parent = "Admin", Path = "admin/log", Icon = "magnifying-glass", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Files, Oqtane.Client", Title = "File Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Logs, Oqtane.Client", Title = "Event Log", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Recycle Bin", Parent = "Admin", Path = "admin/recyclebin", Icon = "trash", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "File Management", Parent = "Admin", Path = "admin/files", Icon = "file", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.RecycleBin, Oqtane.Client", Title = "Recycle Bin", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Files, Oqtane.Client", Title = "File Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Tenant Management", Parent = "Admin", Path = "admin/tenants", Icon = "list", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Recycle Bin", Parent = "Admin", Path = "admin/recyclebin", Icon = "trash", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Tenants, Oqtane.Client", Title = "Tenant Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.RecycleBin, Oqtane.Client", Title = "Recycle Bin", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Module Management", Parent = "Admin", Path = "admin/modules", Icon = "browser", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Tenant Management", Parent = "Admin", Path = "admin/tenants", Icon = "list", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.ModuleDefinitions, Oqtane.Client", Title = "Module Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Tenants, Oqtane.Client", Title = "Tenant Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = "brush", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Module Management", Parent = "Admin", Path = "admin/modules", Icon = "browser", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Themes, Oqtane.Client", Title = "Theme Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.ModuleDefinitions, Oqtane.Client", Title = "Module Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = "timer", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = "brush", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Jobs, Oqtane.Client", Title = "Scheduled Jobs", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Themes, Oqtane.Client", Title = "Theme Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Upgrade Service", Parent = "Admin", Path = "admin/upgrade", Icon = "aperture", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = "timer", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Upgrade, Oqtane.Client", Title = "Upgrade Service", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Jobs, Oqtane.Client", Title = "Scheduled Jobs", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Login", Parent = "", Path = "login", Icon = "lock-locked", IsNavigation = false, IsPersonalizable = false, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Upgrade Service", Parent = "Admin", Path = "admin/upgrade", Icon = "aperture", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Upgrade, Oqtane.Client", Title = "Upgrade Service", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "Login", Parent = "", Path = "login", Icon = "lock-locked", IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Login, Oqtane.Client", Title = "User Login", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.View, Constants.AllUsersRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Login, Oqtane.Client", Title = "User Login", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.AllUsersRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Register", Parent = "", Path = "register", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Register", Parent = "", Path = "register", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Register, Oqtane.Client", Title = "User Registration", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.View, Constants.AllUsersRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Register, Oqtane.Client", Title = "User Registration", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.AllUsersRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Reset", Parent = "", Path = "reset", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Reset", Parent = "", Path = "reset", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Reset, Oqtane.Client", Title = "Password Reset", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.View, Constants.AllUsersRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Reset, Oqtane.Client", Title = "Password Reset", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.AllUsersRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); - _pageTemplates.Add(new PageTemplate { Name = "Profile", Parent = "", Path = "profile", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions(new List { + pageTemplates.Add(new PageTemplate + { + Name = "Profile", Parent = "", Path = "profile", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions(new List + { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.RegisteredRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), - PageTemplateModules = new List { - new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.UserProfile, Oqtane.Client", Title = "User Profile", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.View, Constants.RegisteredRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), Content = "" } - } + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.UserProfile, Oqtane.Client", Title = "User Profile", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.View, Constants.RegisteredRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } }); + return pageTemplates; } public IEnumerable GetSites() @@ -329,7 +508,7 @@ namespace Oqtane.Repository } public void DeleteSite(int siteId) - { + { var site = _db.Site.Find(siteId); _db.Site.Remove(site); _db.SaveChanges(); @@ -341,28 +520,46 @@ namespace Oqtane.Repository List roles = _roleRepository.GetRoles(site.SiteId, true).ToList(); if (!roles.Where(item => item.Name == Constants.AllUsersRole).Any()) { - _roleRepository.AddRole(new Role { SiteId = null, Name = Constants.AllUsersRole, Description = "All Users", IsAutoAssigned = false, IsSystem = true }); + _roleRepository.AddRole(new Role {SiteId = null, Name = Constants.AllUsersRole, Description = "All Users", IsAutoAssigned = false, IsSystem = true}); } + if (!roles.Where(item => item.Name == Constants.HostRole).Any()) { - _roleRepository.AddRole(new Role { SiteId = null, Name = Constants.HostRole, Description = "Application Administrators", IsAutoAssigned = false, IsSystem = true }); + _roleRepository.AddRole(new Role {SiteId = null, Name = Constants.HostRole, Description = "Application Administrators", IsAutoAssigned = false, IsSystem = true}); } - _roleRepository.AddRole(new Role { SiteId = site.SiteId, Name = Constants.RegisteredRole, Description = "Registered Users", IsAutoAssigned = true, IsSystem = true }); - _roleRepository.AddRole(new Role { SiteId = site.SiteId, Name = Constants.AdminRole, Description = "Site Administrators", IsAutoAssigned = false, IsSystem = true }); + _roleRepository.AddRole(new Role {SiteId = site.SiteId, Name = Constants.RegisteredRole, Description = "Registered Users", IsAutoAssigned = true, IsSystem = true}); + _roleRepository.AddRole(new Role {SiteId = site.SiteId, Name = Constants.AdminRole, Description = "Site Administrators", IsAutoAssigned = false, IsSystem = true}); - _profileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "FirstName", Title = "First Name", Description = "Your First Or Given Name", Category = "Name", ViewOrder = 1, MaxLength = 50, DefaultValue = "", IsRequired = true, IsPrivate = false }); - _profileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "LastName", Title = "Last Name", Description = "Your Last Or Family Name", Category = "Name", ViewOrder = 2, MaxLength = 50, DefaultValue = "", IsRequired = true, IsPrivate = false }); - _profileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "Street", Title = "Street", Description = "Street Or Building Address", Category = "Address", ViewOrder = 3, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false }); - _profileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "City", Title = "City", Description = "City", Category = "Address", ViewOrder = 4, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false }); - _profileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "Region", Title = "Region", Description = "State Or Province", Category = "Address", ViewOrder = 5, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false }); - _profileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "Country", Title = "Country", Description = "Country", Category = "Address", ViewOrder = 6, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false }); - _profileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "PostalCode", Title = "Postal Code", Description = "Postal Code Or Zip Code", Category = "Address", ViewOrder = 7, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false }); - _profileRepository.AddProfile(new Profile { SiteId = site.SiteId, Name = "Phone", Title = "Phone Number", Description = "Phone Number", Category = "Contact", ViewOrder = 8, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false }); + _profileRepository.AddProfile(new Profile + {SiteId = site.SiteId, Name = "FirstName", Title = "First Name", Description = "Your First Or Given Name", Category = "Name", ViewOrder = 1, MaxLength = 50, DefaultValue = "", IsRequired = true, IsPrivate = false}); + _profileRepository.AddProfile(new Profile + {SiteId = site.SiteId, Name = "LastName", Title = "Last Name", Description = "Your Last Or Family Name", Category = "Name", ViewOrder = 2, MaxLength = 50, DefaultValue = "", IsRequired = true, IsPrivate = false}); + _profileRepository.AddProfile(new Profile + {SiteId = site.SiteId, Name = "Street", Title = "Street", Description = "Street Or Building Address", Category = "Address", ViewOrder = 3, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false}); + _profileRepository.AddProfile( + new Profile {SiteId = site.SiteId, Name = "City", Title = "City", Description = "City", Category = "Address", ViewOrder = 4, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false}); + _profileRepository.AddProfile(new Profile + {SiteId = site.SiteId, Name = "Region", Title = "Region", Description = "State Or Province", Category = "Address", ViewOrder = 5, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false}); + _profileRepository.AddProfile(new Profile + {SiteId = site.SiteId, Name = "Country", Title = "Country", Description = "Country", Category = "Address", ViewOrder = 6, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false}); + _profileRepository.AddProfile(new Profile + {SiteId = site.SiteId, Name = "PostalCode", Title = "Postal Code", Description = "Postal Code Or Zip Code", Category = "Address", ViewOrder = 7, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false}); + _profileRepository.AddProfile(new Profile + {SiteId = site.SiteId, Name = "Phone", Title = "Phone Number", Description = "Phone Number", Category = "Contact", ViewOrder = 8, MaxLength = 50, DefaultValue = "", IsRequired = false, IsPrivate = false}); - Folder folder = _folderRepository.AddFolder(new Folder { SiteId = site.SiteId, ParentId = null, Name = "Root", Path = "", Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); - _folderRepository.AddFolder(new Folder { SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = "Users\\", Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); + Folder folder = _folderRepository.AddFolder(new Folder + { + SiteId = site.SiteId, ParentId = null, Name = "Root", Path = "", Order = 1, IsSystem = true, + Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" + }); + _folderRepository.AddFolder(new Folder + { + SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = "Users\\", Order = 1, IsSystem = true, + Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" + }); + var _pageTemplates = CreateAdminPages(); CreatePages(site, _pageTemplates); // process site template @@ -378,11 +575,12 @@ namespace Oqtane.Repository site.SiteTemplateType = Constants.DefaultSiteTemplate; } } + Type siteTemplateType = Type.GetType(site.SiteTemplateType); if (siteTemplateType != null) { var siteTemplateObject = ActivatorUtilities.CreateInstance(_serviceProvider, siteTemplateType); - List pageTemplates = ((ISiteTemplate)siteTemplateObject).CreateSite(site); + List pageTemplates = ((ISiteTemplate) siteTemplateObject).CreateSite(site); if (pageTemplates != null && pageTemplates.Count > 0) { CreatePages(site, pageTemplates); @@ -449,7 +647,7 @@ namespace Oqtane.Repository if (moduletype != null) { var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); - ((IPortable)moduleobject).ImportModule(module, pagetemplatemodule.Content, moduledefinition.Version); + ((IPortable) moduleobject).ImportModule(module, pagetemplatemodule.Content, moduledefinition.Version); } } } From c974b5b78c2c83d1bad20d7ecfb419425ad6175e Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 20 Mar 2020 12:55:00 -0400 Subject: [PATCH 080/265] JavaScript interop methods to manage html head elements for title, meta and link --- Oqtane.Client/Themes/ThemeBase.cs | 2 +- Oqtane.Client/UI/Interop.cs | 51 +++++++++++++++++++++++-- Oqtane.Client/wwwroot/js/interop.js | 58 +++++++++++++++++++++++------ Oqtane.Server/wwwroot/js/interop.js | 58 +++++++++++++++++++++++------ 4 files changed, 143 insertions(+), 26 deletions(-) diff --git a/Oqtane.Client/Themes/ThemeBase.cs b/Oqtane.Client/Themes/ThemeBase.cs index b443c584..2ee51b39 100644 --- a/Oqtane.Client/Themes/ThemeBase.cs +++ b/Oqtane.Client/Themes/ThemeBase.cs @@ -27,7 +27,7 @@ namespace Oqtane.Themes Url = ThemePath() + Url; } var interop = new Interop(JSRuntime); - await interop.IncludeCss("Theme", Url); + await interop.IncludeCSS("Theme", Url); } public string NavigateUrl() diff --git a/Oqtane.Client/UI/Interop.cs b/Oqtane.Client/UI/Interop.cs index 03d94ec4..5b2c6b7c 100644 --- a/Oqtane.Client/UI/Interop.cs +++ b/Oqtane.Client/UI/Interop.cs @@ -43,13 +43,58 @@ namespace Oqtane.UI } } - public Task IncludeCss(string id, string url) + public Task UpdateTitle(string title) { try { _jsRuntime.InvokeAsync( - "interop.includeCSS", - id, url); + "interop.updateTitle", + title); + return Task.CompletedTask; + } + catch + { + return Task.CompletedTask; + } + } + + public Task UpdateMeta(string id, string attribute, string name, string content) + { + try + { + _jsRuntime.InvokeAsync( + "interop.updateMeta", + id, attribute, name, content); + return Task.CompletedTask; + } + catch + { + return Task.CompletedTask; + } + } + + public Task UpdateLink(string id, string rel, string type, string url) + { + try + { + _jsRuntime.InvokeAsync( + "interop.updateLink", + id, rel, type, url); + return Task.CompletedTask; + } + catch + { + return Task.CompletedTask; + } + } + + public Task IncludeCSS(string id, string url) + { + try + { + _jsRuntime.InvokeAsync( + "interop.updateLink", + id, "stylesheet", "text/css", url); return Task.CompletedTask; } catch diff --git a/Oqtane.Client/wwwroot/js/interop.js b/Oqtane.Client/wwwroot/js/interop.js index f2a2421f..c9860f68 100644 --- a/Oqtane.Client/wwwroot/js/interop.js +++ b/Oqtane.Client/wwwroot/js/interop.js @@ -20,21 +20,49 @@ window.interop = { } return ""; }, - getElementByName: function (name) { - var elements = document.getElementsByName(name); - if (elements.length) { - return elements[0].value; - } else { - return ""; + updateTitle: function (title) { + if (document.title !== title) { + document.title = title; } }, - includeCSS: function (id, url) { - var link = document.getElementById(id); + updateMeta: function (id, attribute, name, content) { + var meta; + if (id !== "") { + meta = document.getElementById(id); + } + else { + meta = document.querySelector("meta[" + attribute + "=\"" + CSS.escape(name) + "\"]"); + } + if (meta === null) { + meta = document.createElement("meta"); + meta.setAttribute(attribute, name); + if (id !== "") { + meta.id = id; + } + meta.content = content; + document.head.appendChild(meta); + } + else { + if (meta.content !== content) { + meta.setAttribute("content", content); + } + } + }, + updateLink: function (id, rel, type, url) { + var link; + if (id !== "") { + link = document.getElementById(id); + } + else { + link = document.querySelector("link[href=\"" + CSS.escape(url) + "\"]"); + } if (link === null) { link = document.createElement("link"); - link.id = id; - link.type = "text/css"; - link.rel = "stylesheet"; + if (id !== "") { + link.id = id; + } + link.rel = rel; + link.type = type; link.href = url; document.head.appendChild(link); } @@ -44,6 +72,14 @@ window.interop = { } } }, + getElementByName: function (name) { + var elements = document.getElementsByName(name); + if (elements.length) { + return elements[0].value; + } else { + return ""; + } + }, submitForm: function (path, fields) { const form = document.createElement('form'); form.method = 'post'; diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index f2a2421f..c9860f68 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -20,21 +20,49 @@ window.interop = { } return ""; }, - getElementByName: function (name) { - var elements = document.getElementsByName(name); - if (elements.length) { - return elements[0].value; - } else { - return ""; + updateTitle: function (title) { + if (document.title !== title) { + document.title = title; } }, - includeCSS: function (id, url) { - var link = document.getElementById(id); + updateMeta: function (id, attribute, name, content) { + var meta; + if (id !== "") { + meta = document.getElementById(id); + } + else { + meta = document.querySelector("meta[" + attribute + "=\"" + CSS.escape(name) + "\"]"); + } + if (meta === null) { + meta = document.createElement("meta"); + meta.setAttribute(attribute, name); + if (id !== "") { + meta.id = id; + } + meta.content = content; + document.head.appendChild(meta); + } + else { + if (meta.content !== content) { + meta.setAttribute("content", content); + } + } + }, + updateLink: function (id, rel, type, url) { + var link; + if (id !== "") { + link = document.getElementById(id); + } + else { + link = document.querySelector("link[href=\"" + CSS.escape(url) + "\"]"); + } if (link === null) { link = document.createElement("link"); - link.id = id; - link.type = "text/css"; - link.rel = "stylesheet"; + if (id !== "") { + link.id = id; + } + link.rel = rel; + link.type = type; link.href = url; document.head.appendChild(link); } @@ -44,6 +72,14 @@ window.interop = { } } }, + getElementByName: function (name) { + var elements = document.getElementsByName(name); + if (elements.length) { + return elements[0].value; + } else { + return ""; + } + }, submitForm: function (path, fields) { const form = document.createElement('form'); form.method = 'post'; From 0988321b37d634ab31a896f1e3a2ca8ad78805aa Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sun, 22 Mar 2020 09:28:38 -0400 Subject: [PATCH 081/265] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 289a63d3..8bf45287 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ V1 (MVP) - [x] Auto-Upgrade Framework V.Next +- [ ] Use Migrations rather than SQL scripts for database installation/upgrade - [ ] Optional Encryption of Settings Values ( ie. via an IsSecure flag ) - [ ] Localization - [ ] Migrate to Code-Behind Pattern ( *.razor.cs ) From ad031cb375deb857f85da9499ebbfde2e67ae995 Mon Sep 17 00:00:00 2001 From: Mike Casas Date: Mon, 23 Mar 2020 10:24:54 -0400 Subject: [PATCH 082/265] Added functionality to add icons to the ActionLinks. --- Oqtane.Client/Modules/Controls/ActionLink.razor | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index 8f6459aa..5755c4c0 100644 --- a/Oqtane.Client/Modules/Controls/ActionLink.razor +++ b/Oqtane.Client/Modules/Controls/ActionLink.razor @@ -6,11 +6,11 @@ { if (Disabled) { - @_text + @((MarkupString)_iconSpan) @_text } else { - @_text + @((MarkupString)_iconSpan) @_text } } @@ -39,6 +39,9 @@ [Parameter] public string EditMode { get; set; } // optional - specifies if a user must be in edit mode to see the action - default is true + [Parameter] + public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon + string _text = ""; string _url = ""; string _parameters = ""; @@ -46,6 +49,7 @@ string _style = ""; bool _editmode = true; bool _authorized = false; + string _iconSpan = ""; protected override void OnParametersSet() { @@ -75,6 +79,11 @@ _editmode = bool.Parse(EditMode); } + if (!string.IsNullOrEmpty(IconName)) + { + _iconSpan = $" "; + } + _url = EditUrl(Action, _parameters); _authorized = IsAuthorized(); } From ca62560616925c27d2ce7055257857f2636393e2 Mon Sep 17 00:00:00 2001 From: Mike Casas Date: Mon, 23 Mar 2020 10:25:12 -0400 Subject: [PATCH 083/265] Code formatting. --- Oqtane.Client/Modules/Controls/ActionLink.razor | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index 5755c4c0..7585952f 100644 --- a/Oqtane.Client/Modules/Controls/ActionLink.razor +++ b/Oqtane.Client/Modules/Controls/ActionLink.razor @@ -33,9 +33,9 @@ [Parameter] public string Style { 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 @@ -119,10 +119,10 @@ authorized = true; break; case SecurityAccessLevel.View: - authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, ModuleState.Permissions); + authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, ModuleState.Permissions); break; case SecurityAccessLevel.Edit: - authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions); + authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions); break; case SecurityAccessLevel.Admin: authorized = UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole); From d9265e127e6c07bf24f4f17d5732405e70edda39 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 24 Mar 2020 14:08:29 -0400 Subject: [PATCH 084/265] SQL maanager, Module Creator, module settings enhancements --- .../Modules/Admin/ModuleCreator/Index.razor | 58 ++++++ .../Modules/Admin/ModuleCreator/Module.cs | 22 +++ .../Oqtane.Client/Modules/[Module]/Edit.razor | 92 ++++++++++ .../Modules/[Module]/Index.razor | 76 ++++++++ .../[Module]/Services/I[Module]Service.cs | 19 ++ .../[Module]/Services/[Module]Service.cs | 57 ++++++ .../Modules/[Module]/Settings.razor | 47 +++++ .../Modules/[Module]/[Module].cs | 22 +++ .../Controllers/[Module]Controller.cs | 75 ++++++++ .../[Module]/Manager/[Module]Manager.cs | 48 +++++ .../Repository/I[Module]Repository.cs | 14 ++ .../[Module]/Repository/[Module]Context.cs | 17 ++ .../[Module]/Repository/[Module]Repository.cs | 49 ++++++ .../Modules/[Module]/Scripts/00.00.01.sql | 26 +++ .../Modules/Models/[Module]/[Module].cs | 18 ++ .../Modules/Admin/Modules/Settings.razor | 165 +++++++++++------- Oqtane.Client/Modules/Admin/Sql/Index.razor | 104 +++++++++++ Oqtane.Client/Modules/Controls/Pager.razor | 10 +- Oqtane.Client/Oqtane.Client.csproj | 7 + .../Interfaces/IModuleDefinitionService.cs | 1 + .../Services/Interfaces/ISqlService.cs | 10 ++ .../Services/ModuleDefinitionService.cs | 4 + Oqtane.Client/Services/SqlService.cs | 34 ++++ Oqtane.Client/Startup.cs | 1 + .../Controllers/ModuleDefinitionController.cs | 73 +++++++- Oqtane.Server/Controllers/SqlController.cs | 65 +++++++ .../Repository/Interfaces/ISqlRepository.cs | 11 ++ Oqtane.Server/Repository/SiteRepository.cs | 28 +++ Oqtane.Server/Repository/SqlRepository.cs | 48 +++++ Oqtane.Server/Startup.cs | 3 + Oqtane.Shared/Models/SqlQuery.cs | 11 ++ .../Models/HtmlText}/HtmlTextInfo.cs | 0 32 files changed, 1146 insertions(+), 69 deletions(-) create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Module.cs create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Edit.razor create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Index.razor create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Settings.razor create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module].cs create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Scripts/00.00.01.sql create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Shared/Modules/Models/[Module]/[Module].cs create mode 100644 Oqtane.Client/Modules/Admin/Sql/Index.razor create mode 100644 Oqtane.Client/Services/Interfaces/ISqlService.cs create mode 100644 Oqtane.Client/Services/SqlService.cs create mode 100644 Oqtane.Server/Controllers/SqlController.cs create mode 100644 Oqtane.Server/Repository/Interfaces/ISqlRepository.cs create mode 100644 Oqtane.Server/Repository/SqlRepository.cs create mode 100644 Oqtane.Shared/Models/SqlQuery.cs rename Oqtane.Shared/{Models => Modules/Models/HtmlText}/HtmlTextInfo.cs (100%) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor new file mode 100644 index 00000000..087f52ab --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor @@ -0,0 +1,58 @@ +@namespace Oqtane.Modules.Admin.ModuleCreator +@inherits ModuleBase +@inject NavigationManager NavigationManager +@inject IModuleDefinitionService ModuleDefinitionService +@inject IModuleService ModuleService + + + + + + + + + + +
+ + + +
+ + + +
+ + + +@code { + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + + string _name = ""; + string _description = ""; + + protected override void OnInitialized() + { + AddModuleMessage("Please Note That Once You Select The Create Module Button The Application Must Restart In Order To Complete The Process.", MessageType.Info); + } + + private async Task CreateModule() + { + try + { + if (!string.IsNullOrEmpty(_name)) + { + ModuleDefinition moduleDefinition = new ModuleDefinition { Name = _name, Description = _description }; + await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition, ModuleState.ModuleId); + } + else + { + AddModuleMessage("You Must Provide A Name For The Module", MessageType.Warning); + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Creating Module"); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Module.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Module.cs new file mode 100644 index 00000000..0760879b --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Module.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace Oqtane.Modules.Admin.ModuleCreator +{ + public class Module : IModule + { + public Dictionary Properties + { + get + { + Dictionary properties = new Dictionary + { + { "Name", "Module Creator" }, + { "Description", "Enables software developers to quickly create modules by automating many of the initial module creation tasks" }, + { "Version", "1.0.0" }, + { "Categories", "Developer" } + }; + return properties; + } + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Edit.razor new file mode 100644 index 00000000..f0fed00e --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Edit.razor @@ -0,0 +1,92 @@ +@namespace Oqtane.Modules.[Module]s +@using Oqtane.Services.[Module]s +@using Oqtane.Models.[Module]s +@using Oqtane.Modules.Controls +@inherits ModuleBase +@inject NavigationManager NavigationManager +@inject I[Module]Service [Module]Service + + + + + + +
+ + + +
+ +Cancel +
+
+@if (PageState.Action == "Edit") +{ + +} + +@code { + + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Edit; } } + public override string Actions { get { return "Add,Edit"; } } + + int _id; + string _name; + string _createdby; + DateTime _createdon; + string _modifiedby; + DateTime _modifiedon; + + protected override async Task OnInitializedAsync() + { + if (PageState.Action == "Edit") + { + try + { + _id = Int32.Parse(PageState.QueryString["id"]); + [Module] [Module] = await [Module]Service.Get[Module]Async(_id); + if ([Module] != null) + { + _name = [Module].Name; + _createdby = [Module].CreatedBy; + _createdon = [Module].CreatedOn; + _modifiedby = [Module].ModifiedBy; + _modifiedon = [Module].ModifiedOn; + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading [Module] {[Module]Id} {Error}", _id, ex.Message); + AddModuleMessage("Error Loading [Module]", MessageType.Error); + } + } + } + + private async Task Save() + { + try + { + if (PageState.Action == "Add") + { + [Module] [Module] = new [Module](); + [Module].ModuleId = ModuleState.ModuleId; + [Module].Name = _name; + [Module] = await [Module]Service.Add[Module]Async([Module]); + await logger.LogInformation("[Module] Added {[Module]}", [Module]); + } + else + { + [Module] [Module] = await [Module]Service.Get[Module]Async(_id); + [Module].Name = _name; + await [Module]Service.Update[Module]Async([Module]); + await logger.LogInformation("[Module] Updated {[Module]}", [Module]); + } + NavigationManager.NavigateTo(NavigateUrl()); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Saving [Module] {Error}", ex.Message); + AddModuleMessage("Error Saving [Module]", MessageType.Error); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Index.razor new file mode 100644 index 00000000..a0f03fe2 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Index.razor @@ -0,0 +1,76 @@ +@namespace Oqtane.Modules.[Module]s +@using Oqtane.Services.[Module]s +@using Oqtane.Models.[Module]s +@inherits ModuleBase +@inject NavigationManager NavigationManager +@inject I[Module]Service [Module]Service + +@if (_[Module]s == null) +{ +

Loading...

+} +else +{ + +

+ + +
+ + + @context.Name +
+
+
+} + +
+[Module] Module Created Successfully. You Can Access The Files At The Following Locations:

+C:\Users\Shaun.Walker\Source\Repos\sbwalker\oqtane.framework\Oqtane.Client\Modules\[Module]\
+- Index.razor - main component for your module
+- Edit.razor - component for adding or editing content
+- Settings.razor - component for managing module settings
+- Module.cs - implements IModule interface to provide configuration settings for your module
+- Services\I[Module]Service.cs - interface for defining service API methods
+- Services\[Module]Service.cs - implements service API interface methods

+C:\Users\Shaun.Walker\Source\Repos\sbwalker\oqtane.framework\Oqtane.Server\Modules\[Module]\
+- Controllers\[Module]Controller.cs - API methods implemented using a REST pattern
+- Manager\[Module]Manager.cs - implements optional module interfaces for features such as import/export of content
+- Repository\I[Module]Repository.cs - interface for defining repository methods
+- Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core
+- Repository\[Module]Context.cs - provides a DB Context for data access
+- Scripts\01.00.00.sql - database schema definition

+C:\Users\Shaun.Walker\Source\Repos\sbwalker\oqtane.framework\Oqtane.Shared\Modules\Models\[Module]\
+- [Module].cs - model definition

+ +@code { + List<[Module]> _[Module]s; + + protected override async Task OnParametersSetAsync() + { + try + { + _[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading [Module] {Error}", ex.Message); + AddModuleMessage("Error Loading [Module]", MessageType.Error); + } + } + + private async Task Delete([Module] [Module]) + { + try + { + await [Module]Service.Delete[Module]Async([Module].[Module]Id); + await logger.LogInformation("[Module] Deleted {[Module]}", [Module]); + NavigationManager.NavigateTo(NavigateUrl()); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting [Module] {[Module]} {Error}", [Module], ex.Message); + AddModuleMessage("Error Deleting [Module]", MessageType.Error); + } + } +} \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs new file mode 100644 index 00000000..f481dc02 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Oqtane.Models.[Module]s; + +namespace Oqtane.Services.[Module]s +{ + public interface I[Module]Service + { + Task> Get[Module]sAsync(int ModuleId); + + Task<[Module]> Get[Module]Async(int [Module]Id); + + Task<[Module]> Add[Module]Async([Module] [Module]); + + Task<[Module]> Update[Module]Async([Module] [Module]); + + Task Delete[Module]Async(int [Module]Id); + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs new file mode 100644 index 00000000..7935c184 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using Oqtane.Models.[Module]s; +using Oqtane.Modules; +using Oqtane.Services; +using Oqtane.Shared; + +namespace Oqtane.Services.[Module]s +{ + public class [Module]Service : ServiceBase, I[Module]Service, IService + { + private readonly HttpClient _http; + private readonly NavigationManager _navigationManager; + private readonly SiteState _siteState; + + public [Module]Service(HttpClient http, SiteState siteState, NavigationManager navigationManager) + { + _http = http; + _siteState = siteState; + _navigationManager = navigationManager; + } + + private string Apiurl + { + get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "[Module]"); } + } + + public async Task> Get[Module]sAsync(int ModuleId) + { + List<[Module]> [Module]s = await _http.GetJsonAsync>(Apiurl + "?moduleid=" + ModuleId.ToString()); + return [Module]s.OrderBy(item => item.Name).ToList(); + } + + public async Task<[Module]> Get[Module]Async(int [Module]Id) + { + return await _http.GetJsonAsync<[Module]>(Apiurl + "/" + [Module]Id.ToString()); + } + + public async Task<[Module]> Add[Module]Async([Module] [Module]) + { + return await _http.PostJsonAsync<[Module]>(Apiurl + "?entityid=" + [Module].ModuleId, [Module]); + } + + public async Task<[Module]> Update[Module]Async([Module] [Module]) + { + return await _http.PutJsonAsync<[Module]>(Apiurl + "/" + [Module].[Module]Id + "?entityid=" + [Module].ModuleId, [Module]); + } + + public async Task Delete[Module]Async(int [Module]Id) + { + await _http.DeleteAsync(Apiurl + "/" + [Module]Id.ToString()); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Settings.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Settings.razor new file mode 100644 index 00000000..b657eb6a --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Settings.razor @@ -0,0 +1,47 @@ +@namespace Oqtane.Modules.[Module]s +@inherits ModuleBase +@inject ISettingService SettingService + + + + + + +
+ + + +
+ +@code { + public override string Title { get { return "[Module] Settings"; } } + + string _value; + + protected override async Task OnInitializedAsync() + { + try + { + Dictionary settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId); + _value = SettingService.GetSetting(settings, "SettingName", ""); + } + catch (Exception ex) + { + ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error); + } + } + + public async Task UpdateSettings() + { + try + { + Dictionary settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId); + SettingService.SetSetting(settings, "SettingName", _value); + await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId); + } + catch (Exception ex) + { + ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module].cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module].cs new file mode 100644 index 00000000..040fd244 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module].cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace Oqtane.Modules.[Module]s +{ + public class Module : IModule + { + public Dictionary Properties + { + get + { + Dictionary properties = new Dictionary + { + { "Name", "[Module]" }, + { "Description", "[Module]" }, + { "Version", "1.0.0" }, + { "ServerAssemblyName", "Oqtane.Server" } + }; + return properties; + } + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs new file mode 100644 index 00000000..023e42a4 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Oqtane.Models.[Module]s; +using Oqtane.Repository.[Module]s; +using Oqtane.Shared; +using System.Collections.Generic; +using Oqtane.Enums; +using Oqtane.Infrastructure.Interfaces; + +namespace Oqtane.Controllers.[Module]s +{ + [Route("{site}/api/[controller]")] + public class [Module]Controller : Controller + { + private readonly I[Module]Repository _[Module]s; + private readonly ILogManager _logger; + + public [Module]Controller(I[Module]Repository [Module]s, ILogManager logger) + { + _[Module]s = [Module]s; + _logger = logger; + } + + // GET: api/?moduleid=x + [HttpGet] + [Authorize(Roles = Constants.RegisteredRole)] + public IEnumerable<[Module]> Get(string moduleid) + { + return _[Module]s.Get[Module]s(int.Parse(moduleid)); + } + + // GET api//5 + [HttpGet("{id}")] + [Authorize(Roles = Constants.RegisteredRole)] + public [Module] Get(int id) + { + return _[Module]s.Get[Module](id); + } + + // POST api/ + [HttpPost] + [Authorize(Roles = Constants.AdminRole)] + public [Module] Post([FromBody] [Module] [Module]) + { + if (ModelState.IsValid) + { + [Module] = _[Module]s.Add[Module]([Module]); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]); + } + return [Module]; + } + + // PUT api//5 + [HttpPut("{id}")] + [Authorize(Roles = Constants.AdminRole)] + public [Module] Put(int id, [FromBody] [Module] [Module]) + { + if (ModelState.IsValid) + { + [Module] = _[Module]s.Update[Module]([Module]); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]); + } + return [Module]; + } + + // DELETE api//5 + [HttpDelete("{id}")] + [Authorize(Roles = Constants.AdminRole)] + public void Delete(int id) + { + _[Module]s.Delete[Module](id); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs new file mode 100644 index 00000000..8380c97d --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs @@ -0,0 +1,48 @@ +using Oqtane.Models.[Module]s; +using Oqtane.Repository.[Module]s; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +namespace Oqtane.Modules.[Module]s +{ + public class [Module]Manager : IPortable + { + private I[Module]Repository _[Module]s; + + public [Module]Manager(I[Module]Repository [Module]s) + { + _[Module]s = [Module]s; + } + + public string ExportModule(Models.Module module) + { + string content = ""; + List<[Module]> [Module]s = _[Module]s.Get[Module]s(module.ModuleId).ToList(); + if ([Module]s != null) + { + content = JsonSerializer.Serialize([Module]s); + } + return content; + } + + public void ImportModule(Models.Module module, string content, string version) + { + List<[Module]> [Module]s = null; + if (!string.IsNullOrEmpty(content)) + { + [Module]s = JsonSerializer.Deserialize>(content); + } + if ([Module]s != null) + { + foreach([Module] [Module] in [Module]s) + { + [Module] _[Module] = new [Module](); + _[Module].ModuleId = module.ModuleId; + _[Module].Name = [Module].Name; + _[Module]s.Add[Module](_[Module]); + } + } + } + } +} \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs new file mode 100644 index 00000000..7a607ad2 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Oqtane.Models.[Module]s; + +namespace Oqtane.Repository.[Module]s +{ + public interface I[Module]Repository + { + IEnumerable<[Module]> Get[Module]s(int ModuleId); + [Module] Get[Module](int [Module]Id); + [Module] Add[Module]([Module] [Module]); + [Module] Update[Module]([Module] [Module]); + void Delete[Module](int [Module]Id); + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs new file mode 100644 index 00000000..9a438a88 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.AspNetCore.Http; +using Oqtane.Models.[Module]s; +using Oqtane.Modules; + +namespace Oqtane.Repository.[Module]s +{ + public class [Module]Context : DBContextBase, IService + { + public virtual DbSet<[Module]> [Module] { get; set; } + + public [Module]Context(ITenantResolver tenantResolver, IHttpContextAccessor accessor) : base(tenantResolver, accessor) + { + // ContextBase handles multi-tenant database connections + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs new file mode 100644 index 00000000..c6383fce --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore; +using System.Linq; +using System.Collections.Generic; +using Oqtane.Models.[Module]s; +using Oqtane.Modules; + +namespace Oqtane.Repository.[Module]s +{ + public class [Module]Repository : I[Module]Repository, IService + { + private readonly [Module]Context _db; + + public [Module]Repository([Module]Context context) + { + _db = context; + } + + public IEnumerable<[Module]> Get[Module]s(int ModuleId) + { + return _db.[Module].Where(item => item.ModuleId == ModuleId); + } + + public [Module] Get[Module](int [Module]Id) + { + return _db.[Module].Find([Module]Id); + } + + public [Module] Add[Module]([Module] [Module]) + { + _db.[Module].Add([Module]); + _db.SaveChanges(); + return [Module]; + } + + public [Module] Update[Module]([Module] [Module]) + { + _db.Entry([Module]).State = EntityState.Modified; + _db.SaveChanges(); + return [Module]; + } + + public void Delete[Module](int [Module]Id) + { + [Module] [Module] = _db.[Module].Find([Module]Id); + _db.[Module].Remove([Module]); + _db.SaveChanges(); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Scripts/00.00.01.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Scripts/00.00.01.sql new file mode 100644 index 00000000..f97f4daf --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Scripts/00.00.01.sql @@ -0,0 +1,26 @@ +/* +Create [Module] table +*/ + +CREATE TABLE [dbo].[[Module]]( + [[Module]Id] [int] IDENTITY(1,1) NOT NULL, + [ModuleId] [int] NOT NULL, + [Name] [nvarchar](256) NOT NULL, + [CreatedBy] [nvarchar](256) NOT NULL, + [CreatedOn] [datetime] NOT NULL, + [ModifiedBy] [nvarchar](256) NOT NULL, + [ModifiedOn] [datetime] NOT NULL, + CONSTRAINT [PK_[Module]] PRIMARY KEY CLUSTERED + ( + [[Module]Id] ASC + ) +) +GO + +/* +Create foreign key relationships +*/ +ALTER TABLE [dbo].[[Module]] WITH CHECK ADD CONSTRAINT [FK_[Module]_Module] FOREIGN KEY([ModuleId]) +REFERENCES [dbo].Module ([ModuleId]) +ON DELETE CASCADE +GO \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Shared/Modules/Models/[Module]/[Module].cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Shared/Modules/Models/[Module]/[Module].cs new file mode 100644 index 00000000..6ebba399 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Shared/Modules/Models/[Module]/[Module].cs @@ -0,0 +1,18 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Oqtane.Models.[Module]s +{ + public class [Module] : IAuditable + { + [Key] + public int [Module]Id { get; set; } + public int ModuleId { get; set; } + public string Name { get; set; } + + public string CreatedBy { get; set; } + public DateTime CreatedOn { get; set; } + public string ModifiedBy { get; set; } + public DateTime ModifiedOn { get; set; } + } +} diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index 73bd79c7..560fa398 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -5,75 +5,108 @@ @inject IModuleService ModuleService @inject IPageModuleService PageModuleService - - - - - - - - - - - - - - - - - - - -
- - - -
- - - -
- - - -
- - - -
+@if (_containers != null) +{ +
+
-@DynamicComponent + - -Cancel +
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+
+ @if (_settingsModuleType != null) + { +
+
+ @DynamicComponent +
+ } +
+
+
+ + Cancel +} @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Edit; } } public override string Title { get { return "Module Settings"; } } - Dictionary _containers = new Dictionary(); + Dictionary _containers; string _title; string _containerType; string _permissionNames = ""; string _permissions; string _pageId; -#pragma warning disable 649 PermissionGrid _permissionGrid; -#pragma warning restore 649 + Type _settingsModuleType; + string _settingstitle = "Other Settings"; RenderFragment DynamicComponent { get; set; } object _settings; @@ -86,16 +119,19 @@ _permissionNames = ModuleState.ModuleDefinition.PermissionNames; _pageId = ModuleState.PageId.ToString(); - DynamicComponent = builder => + _settingsModuleType = Type.GetType(ModuleState.ModuleType); + if (_settingsModuleType != null) { - Type moduleType = Type.GetType(ModuleState.ModuleType); - if (moduleType != null) + var moduleobject = Activator.CreateInstance(_settingsModuleType); + _settingstitle = (string)_settingsModuleType.GetProperty("Title").GetValue(moduleobject, null); + + DynamicComponent = builder => { - builder.OpenComponent(0, moduleType); - builder.AddComponentReferenceCapture(1, inst => { _settings = Convert.ChangeType(inst, moduleType); }); + builder.OpenComponent(0, _settingsModuleType); + builder.AddComponentReferenceCapture(1, inst => { _settings = Convert.ChangeType(inst, _settingsModuleType); }); builder.CloseComponent(); - } - }; + }; + } } private async Task SaveModule() @@ -111,10 +147,13 @@ await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); - Type moduleType = Type.GetType(ModuleState.ModuleType); - if (moduleType != null) + if (_settingsModuleType != null) { - moduleType.GetMethod("UpdateSettings")?.Invoke(_settings, null); // method must be public in settings component + Type moduleType = Type.GetType(ModuleState.ModuleType); + if (moduleType != null) + { + moduleType.GetMethod("UpdateSettings")?.Invoke(_settings, null); // method must be public in settings component + } } NavigationManager.NavigateTo(NavigateUrl()); diff --git a/Oqtane.Client/Modules/Admin/Sql/Index.razor b/Oqtane.Client/Modules/Admin/Sql/Index.razor new file mode 100644 index 00000000..0f3705c9 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/Sql/Index.razor @@ -0,0 +1,104 @@ +@namespace Oqtane.Modules.Admin.Sql +@inherits ModuleBase +@inject NavigationManager NavigationManager +@inject ITenantService TenantService +@inject ISqlService SqlService + +@if (_tenants == null) +{ +

Loading...

+} +else +{ + + + + + + + + + +
+ + + +
+ + + +
+ +

+ @if (!string.IsNullOrEmpty(_results)) + { + @((MarkupString)_results) + } +} + +@code { + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + + List _tenants; + string _tenantid = "-1"; + string _sql = ""; + string _results = ""; + + protected override async Task OnInitializedAsync() + { + _tenants = await TenantService.GetTenantsAsync(); + } + + private async Task Execute() + { + if (_tenantid != "-1" && !string.IsNullOrEmpty(_sql)) + { + SqlQuery sqlquery = new SqlQuery { TenantId = int.Parse(_tenantid), Query = _sql }; + sqlquery = await SqlService.ExecuteQueryAsync(sqlquery); + _results = DisplayResults(sqlquery.Results); + } + else + { + AddModuleMessage("You Must Select A Tenant And Provide A SQL Query", MessageType.Warning); + } + } + + private string DisplayResults(List> results) + { + string table = ""; + foreach (Dictionary item in results) + { + if (table == "") + { + table = "
"; + table += ""; + foreach (KeyValuePair kvp in item) + { + table += ""; + } + table += ""; + } + table += ""; + foreach (KeyValuePair kvp in item) + { + table += ""; + } + table += ""; + } + if (table != "") + { + table += "
" + kvp.Key + "
" + kvp.Value + "
"; + } + else + { + table = "No Results Returned"; + } + return table; + } +} diff --git a/Oqtane.Client/Modules/Controls/Pager.razor b/Oqtane.Client/Modules/Controls/Pager.razor index 64ee715f..bfcc8cb3 100644 --- a/Oqtane.Client/Modules/Controls/Pager.razor +++ b/Oqtane.Client/Modules/Controls/Pager.razor @@ -1,6 +1,6 @@ @namespace Oqtane.Modules.Controls @inherits ModuleBase -@typeparam TAbleItem +@typeparam TableItem

@if(Format == "Table") @@ -78,13 +78,13 @@ public RenderFragment Header { get; set; } [Parameter] - public RenderFragment Row { get; set; } + public RenderFragment Row { get; set; } [Parameter] - public RenderFragment Detail { get; set; } + public RenderFragment Detail { get; set; } [Parameter] - public IEnumerable Items { get; set; } + public IEnumerable Items { get; set; } [Parameter] public string PageSize { get; set; } @@ -95,7 +95,7 @@ [Parameter] public string Class { get; set; } - IEnumerable ItemList { get; set; } + IEnumerable ItemList { get; set; } protected override void OnParametersSet() { diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index a95adf1b..66ba174f 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -27,6 +27,13 @@ TRACE;WASM + + + + + + + diff --git a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs index e0bc3a24..8560c661 100644 --- a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs +++ b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs @@ -13,5 +13,6 @@ namespace Oqtane.Services 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/Interfaces/ISqlService.cs b/Oqtane.Client/Services/Interfaces/ISqlService.cs new file mode 100644 index 00000000..f563a795 --- /dev/null +++ b/Oqtane.Client/Services/Interfaces/ISqlService.cs @@ -0,0 +1,10 @@ +using Oqtane.Models; +using System.Threading.Tasks; + +namespace Oqtane.Services +{ + public interface ISqlService + { + Task ExecuteQueryAsync(SqlQuery sqlquery); + } +} diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index 0d3cc4aa..cc537e86 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -92,5 +92,9 @@ namespace Oqtane.Services } } } + public async Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId) + { + await _http.PostJsonAsync(Apiurl + "?moduleid=" + moduleId.ToString(), moduleDefinition); + } } } diff --git a/Oqtane.Client/Services/SqlService.cs b/Oqtane.Client/Services/SqlService.cs new file mode 100644 index 00000000..52371df8 --- /dev/null +++ b/Oqtane.Client/Services/SqlService.cs @@ -0,0 +1,34 @@ +using Oqtane.Models; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using Oqtane.Shared; +using System.Collections.Generic; +using System.Linq; + +namespace Oqtane.Services +{ + public class SqlService : ServiceBase, ISqlService + { + private readonly HttpClient _http; + private readonly SiteState _siteState; + private readonly NavigationManager _navigationManager; + + public SqlService(HttpClient http, SiteState siteState, NavigationManager navigationManager) + { + _http = http; + _siteState = siteState; + _navigationManager = navigationManager; + } + + private string Apiurl + { + get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "Sql"); } + } + + public async Task ExecuteQueryAsync(SqlQuery sqlquery) + { + return await _http.PostJsonAsync(Apiurl, sqlquery); + } + } +} diff --git a/Oqtane.Client/Startup.cs b/Oqtane.Client/Startup.cs index b7991d90..8d4dfdeb 100644 --- a/Oqtane.Client/Startup.cs +++ b/Oqtane.Client/Startup.cs @@ -59,6 +59,7 @@ namespace Oqtane.Client services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // dynamically register module contexts and repository services Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 6e4ea17f..5e4659ce 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -11,6 +11,7 @@ using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; using Oqtane.Security; +using System; // ReSharper disable StringIndexOfIsCultureSpecific.1 namespace Oqtane.Controllers @@ -19,17 +20,23 @@ namespace Oqtane.Controllers public class ModuleDefinitionController : Controller { private readonly IModuleDefinitionRepository _moduleDefinitions; + private readonly IModuleRepository _modules; private readonly IUserPermissions _userPermissions; private readonly IInstallationManager _installationManager; private readonly IWebHostEnvironment _environment; + private readonly ITenantResolver _resolver; + private readonly ISqlRepository _sql; private readonly ILogManager _logger; - public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, ILogManager logger) + public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantResolver resolver, ISqlRepository sql, ILogManager logger) { _moduleDefinitions = moduleDefinitions; + _modules = modules; _userPermissions = userPermissions; _installationManager = installationManager; _environment = environment; + _resolver = resolver; + _sql = sql; _logger = logger; } @@ -124,5 +131,69 @@ namespace Oqtane.Controllers return File(file, "application/octet-stream", assemblyname); } + // POST api/?moduleid=x + [HttpPost] + [Authorize(Roles = Constants.HostRole)] + public void Post([FromBody] ModuleDefinition moduleDefinition, string moduleid) + { + if (ModelState.IsValid) + { + string rootPath = Directory.GetParent(_environment.ContentRootPath).FullName; + string templatePath = Path.Combine(rootPath, "Oqtane.Client\\Modules\\Admin\\ModuleCreator\\Templates\\"); + ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, moduleDefinition); + moduleDefinition.ModuleDefinitionName = "Oqtane.Modules." + moduleDefinition.Name + "s, Oqtane.Client"; + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Definition Created {ModuleDefinition}", moduleDefinition); + Models.Module module = _modules.GetModule(int.Parse(moduleid)); + module.ModuleDefinitionName = moduleDefinition.ModuleDefinitionName; + _modules.UpdateModule(module); + _installationManager.RestartApplication(); + } + } + + private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, ModuleDefinition moduleDefinition) + { + // process folder + string folderPath = current.FullName.Replace("Oqtane.Client\\Modules\\Admin\\ModuleCreator\\Templates\\", ""); + folderPath = folderPath.Replace("[Module]", moduleDefinition.Name); + if (!Directory.Exists(folderPath)) + { + Directory.CreateDirectory(folderPath); + } + + FileInfo[] files = current.GetFiles("*.*"); + if (files != null) + { + foreach (FileInfo file in files) + { + // process file + string filePath = Path.Combine(folderPath, file.Name); + filePath = filePath.Replace("[Module]", moduleDefinition.Name); + + string text = System.IO.File.ReadAllText(file.FullName); + text = text.Replace("[Module]", moduleDefinition.Name); + text = text.Replace("[Description]", moduleDefinition.Description); + text = text.Replace("[RootPath]", rootPath); + text = text.Replace("[Folder]", folderPath); + text = text.Replace("[File]", Path.GetFileName(filePath)); + System.IO.File.WriteAllText(filePath, text); + + if (Path.GetExtension(filePath) == ".sql") + { + // execute script + foreach (string query in text.Split("GO", StringSplitOptions.RemoveEmptyEntries)) + { + _sql.ExecuteNonQuery(_resolver.GetTenant(), query); + } + } + } + + DirectoryInfo[] folders = current.GetDirectories(); + + foreach (DirectoryInfo folder in folders.Reverse()) + { + ProcessTemplatesRecursively(folder, rootPath, moduleDefinition); + } + } + } } } diff --git a/Oqtane.Server/Controllers/SqlController.cs b/Oqtane.Server/Controllers/SqlController.cs new file mode 100644 index 00000000..1ec75d19 --- /dev/null +++ b/Oqtane.Server/Controllers/SqlController.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Oqtane.Models; +using System.Collections.Generic; +using Oqtane.Shared; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Repository; +using Oqtane.Enums; +using System.Data.SqlClient; +using System.Data; +using System.Dynamic; +using Newtonsoft.Json; +using System; + +namespace Oqtane.Controllers +{ + [Route("{site}/api/[controller]")] + public class SqlController : Controller + { + private readonly ITenantRepository _tenants; + private readonly ISqlRepository _sql; + private readonly ILogManager _logger; + + public SqlController(ITenantRepository tenants, ISqlRepository sql, ILogManager logger) + { + _tenants = tenants; + _sql = sql; + _logger = logger; + } + + // POST: api/ + [HttpPost] + [Authorize(Roles = Constants.HostRole)] + public SqlQuery Post([FromBody] SqlQuery sqlquery) + { + var results = new List>(); + Dictionary row; + Tenant tenant = _tenants.GetTenant(sqlquery.TenantId); + try + { + foreach (string query in sqlquery.Query.Split("GO", StringSplitOptions.RemoveEmptyEntries)) + { + SqlDataReader dr = _sql.ExecuteReader(tenant, query); + _logger.Log(LogLevel.Information, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId}", query, sqlquery.TenantId); + while (dr.Read()) + { + row = new Dictionary(); + for (var field = 0; field < dr.FieldCount; field++) + { + row[dr.GetName(field)] = dr.IsDBNull(field) ? "" : dr.GetValue(field).ToString(); + } + results.Add(row); + } + } + } + catch (Exception ex) + { + _logger.Log(LogLevel.Error, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId} Results In An Error {Error}", sqlquery.Query, sqlquery.TenantId, ex.Message); + } + sqlquery.Results = results; + return sqlquery; + } + + } +} diff --git a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs new file mode 100644 index 00000000..4bd04e91 --- /dev/null +++ b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs @@ -0,0 +1,11 @@ +using System.Data.SqlClient; +using Oqtane.Models; + +namespace Oqtane.Repository +{ + public interface ISqlRepository + { + int ExecuteNonQuery(Tenant tenant, string query); + SqlDataReader ExecuteReader(Tenant tenant, string query); + } +} diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 09ec53fc..dc10fb3b 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -361,6 +361,34 @@ namespace Oqtane.Repository } }); pageTemplates.Add(new PageTemplate + { + Name = "Sql Management", + Parent = "Admin", + Path = "admin/sql", + Icon = "spreadsheet", + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = "Oqtane.Modules.Admin.Sql, Oqtane.Client", Title = "Sql Management", Pane = "Content", + ModulePermissions = _permissionRepository.EncodePermissions(new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate { Name = "Upgrade Service", Parent = "Admin", Path = "admin/upgrade", Icon = "aperture", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = _permissionRepository.EncodePermissions(new List diff --git a/Oqtane.Server/Repository/SqlRepository.cs b/Oqtane.Server/Repository/SqlRepository.cs new file mode 100644 index 00000000..c9bdf734 --- /dev/null +++ b/Oqtane.Server/Repository/SqlRepository.cs @@ -0,0 +1,48 @@ +using System; +using System.Data; +using System.Data.SqlClient; +using Oqtane.Models; + +namespace Oqtane.Repository +{ + public class SqlRepository : ISqlRepository + { + + public int ExecuteNonQuery(Tenant tenant, string query) + { + SqlConnection conn = new SqlConnection(FormatConnectionString(tenant.DBConnectionString)); + SqlCommand cmd = conn.CreateCommand(); + using (conn) + { + PrepareCommand(conn, cmd, query); + int val = cmd.ExecuteNonQuery(); + return val; + } + } + + public SqlDataReader ExecuteReader(Tenant tenant, string query) + { + SqlConnection conn = new SqlConnection(FormatConnectionString(tenant.DBConnectionString)); + SqlCommand cmd = conn.CreateCommand(); + PrepareCommand(conn, cmd, query); + var dr = cmd.ExecuteReader(CommandBehavior.CloseConnection); + return dr; + } + + private void PrepareCommand(SqlConnection conn, SqlCommand cmd, string query) + { + if (conn.State != ConnectionState.Open) + { + conn.Open(); + } + cmd.Connection = conn; + cmd.CommandText = query; + cmd.CommandType = CommandType.Text; + } + + private string FormatConnectionString(string connectionString) + { + return connectionString.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString()); + } + } +} diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index b961fd06..07a47ee7 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -107,6 +107,7 @@ namespace Oqtane services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddSingleton(); @@ -185,6 +186,7 @@ namespace Oqtane services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddOqtaneModules(); services.AddOqtaneThemes(); @@ -338,6 +340,7 @@ namespace Oqtane services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddOqtaneModules(); services.AddOqtaneThemes(); diff --git a/Oqtane.Shared/Models/SqlQuery.cs b/Oqtane.Shared/Models/SqlQuery.cs new file mode 100644 index 00000000..035c1178 --- /dev/null +++ b/Oqtane.Shared/Models/SqlQuery.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Oqtane.Models +{ + public class SqlQuery + { + public int TenantId { get; set; } + public string Query { get; set; } + public List> Results { get; set; } + } +} diff --git a/Oqtane.Shared/Models/HtmlTextInfo.cs b/Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs similarity index 100% rename from Oqtane.Shared/Models/HtmlTextInfo.cs rename to Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs From 20e481af3dd1393b605290a514fff5970e66062e Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 24 Mar 2020 16:56:01 -0400 Subject: [PATCH 085/265] fixes to role management --- Oqtane.Client/Modules/Admin/Roles/Add.razor | 38 +++++++------------ Oqtane.Client/Modules/Admin/Roles/Edit.razor | 15 +------- Oqtane.Client/Modules/Admin/Roles/Index.razor | 4 +- .../Modules/Controls/ActionDialog.razor | 2 +- .../Modules/Controls/ActionLink.razor | 4 +- 5 files changed, 19 insertions(+), 44 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Roles/Add.razor b/Oqtane.Client/Modules/Admin/Roles/Add.razor index e908dd1c..65f346c5 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Add.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Add.razor @@ -6,37 +6,26 @@ - - - - - - - -
- + - +
- + - +
- + - -
- - - @@ -49,19 +38,18 @@ @code { public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - string name = ""; - string description = ""; - string isautoassigned = "False"; - string issystem = "False"; + string _name = ""; + string _description = ""; + string _isautoassigned = "False"; private async Task SaveRole() { Role role = new Role(); role.SiteId = PageState.Page.SiteId; - role.Name = name; - role.Description = description; - role.IsAutoAssigned = (isautoassigned == null ? false : Boolean.Parse(isautoassigned)); - role.IsSystem = (issystem == null ? false : Boolean.Parse(issystem)); + role.Name = _name; + role.Description = _description; + role.IsAutoAssigned = (_isautoassigned == null ? false : Boolean.Parse(_isautoassigned)); + role.IsSystem = false; try { diff --git a/Oqtane.Client/Modules/Admin/Roles/Edit.razor b/Oqtane.Client/Modules/Admin/Roles/Edit.razor index ebbd5907..acea8de2 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Edit.razor @@ -31,17 +31,6 @@
- - - -
Cancel @@ -53,7 +42,6 @@ string _name = ""; string _description = ""; string _isautoassigned = "False"; - string _issystem = "False"; protected override async Task OnInitializedAsync() { @@ -66,7 +54,6 @@ _name = role.Name; _description = role.Description; _isautoassigned = role.IsAutoAssigned.ToString(); - _issystem = role.IsSystem.ToString(); } } catch (Exception ex) @@ -82,7 +69,7 @@ role.Name = _name; role.Description = _description; role.IsAutoAssigned = (_isautoassigned != null && Boolean.Parse(_isautoassigned)); - role.IsSystem = (_issystem != null && Boolean.Parse(_issystem)); + role.IsSystem = false; try { diff --git a/Oqtane.Client/Modules/Admin/Roles/Index.razor b/Oqtane.Client/Modules/Admin/Roles/Index.razor index 29378752..01372e80 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Index.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Index.razor @@ -17,8 +17,8 @@ else

Name @context.Name
- - + +
- - + +
+ -
+ -
+ - - +
diff --git a/Oqtane.Client/Modules/Admin/Roles/Add.razor b/Oqtane.Client/Modules/Admin/Roles/Add.razor index 65f346c5..29c248cf 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Add.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Add.razor @@ -6,26 +6,26 @@ + + + + + + + + + + + + + + + +
- + - +
- + - +
- + - diff --git a/Oqtane.Client/Modules/Admin/Roles/Edit.razor b/Oqtane.Client/Modules/Admin/Roles/Edit.razor index acea8de2..9b341e7e 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Edit.razor @@ -6,26 +6,26 @@ - - - - +
- + - +
- + - +
- + - diff --git a/Oqtane.Client/Modules/Controls/PermissionGrid.razor b/Oqtane.Client/Modules/Controls/PermissionGrid.razor index 5e253a9d..974bd31c 100644 --- a/Oqtane.Client/Modules/Controls/PermissionGrid.razor +++ b/Oqtane.Client/Modules/Controls/PermissionGrid.razor @@ -6,7 +6,7 @@ @if (_permissions != null) {
- +
@@ -32,7 +32,7 @@
Role
@if (_users.Count != 0) { - +
@@ -60,7 +60,7 @@
User
} - +
diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index eb7aeaa8..257429e5 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -408,7 +408,7 @@ private void ShowControlPanel() { _message = ""; - _display = "width: 25%; minwidth:300px"; + _display = "width: 25%; min-width: 375px;"; StateHasChanged(); } diff --git a/Oqtane.Client/wwwroot/css/app.css b/Oqtane.Client/wwwroot/css/app.css index 2f2f61c8..c28214d4 100644 --- a/Oqtane.Client/wwwroot/css/app.css +++ b/Oqtane.Client/wwwroot/css/app.css @@ -59,7 +59,6 @@ app { .app-admin-modal .modal-content { margin: 5% auto; /* 5% from the top and centered */ - padding: 20px; width: 80%; /* Could be more or less, depending on screen size */ } diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index b524b6bd..45d61327 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -55,7 +55,7 @@ namespace Oqtane.SiteTemplates new Permission(PermissionNames.Edit, Constants.AdminRole, true) }), Content = "

Oqtane is an open source modular application framework built from the ground up using modern .NET Core technology. It leverages the revolutionary new Blazor component model to create a fully dynamic web development experience which can be executed on a client or server. Whether you are looking for a platform to accelerate your web development efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.

" + - "



Join Our Community  Clone Our Repo

" + + "

Join Our Community  Clone Our Repo

" + "

Blazor is a single-page app framework that lets you build interactive web applications using C# instead of JavaScript. Client-side Blazor relies on WebAssembly, an open web standard that does not require plugins or code transpilation in order to run natively in a web browser. Server-side Blazor uses SignalR to host your application on a web server and provide a responsive and robust debugging experience. Blazor applications works in all modern web browsers, including mobile browsers.

" + "

Blazor is a feature of ASP.NET Core 3, the popular cross platform web development framework from Microsoft that extends the .NET developer platform with tools and libraries for building web apps.

" }, diff --git a/Oqtane.Server/wwwroot/css/app.css b/Oqtane.Server/wwwroot/css/app.css index 99f6e73e..f1c79deb 100644 --- a/Oqtane.Server/wwwroot/css/app.css +++ b/Oqtane.Server/wwwroot/css/app.css @@ -59,7 +59,6 @@ app { .app-admin-modal .modal-content { margin: 5% auto; /* 5% from the top and centered */ - padding: 20px; width: 80%; /* Could be more or less, depending on screen size */ } From 9335b18d16e56ad38b604e0279e09f33c0b8d988 Mon Sep 17 00:00:00 2001 From: Aubrey Date: Fri, 27 Mar 2020 11:20:36 -0400 Subject: [PATCH 088/265] Check for a valid email. --- .../Modules/Admin/Register/Index.razor | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index 46fdca01..a3624623 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Register +@using System.Text.RegularExpressions; @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService @@ -42,13 +43,15 @@ string _confirm = ""; string _email = ""; string _displayName = ""; + bool _isEmailValid = false; private async Task Register() { try { _message = ""; - if (_username != "" && _password != "" && _confirm != "" && _email != "") + IsValidEmail(); + if (_username != "" && _password != "" && _confirm != "" && _isEmailValid) { if (_password == _confirm) { @@ -94,4 +97,16 @@ { NavigationManager.NavigateTo(NavigateUrl("")); } +} + private void IsValidEmail() + { + if (_email != ""){ + _isEmailValid = true; + } + + _isEmailValid = Regex.IsMatch(_email, + @"^(?("")("".+?(? Date: Fri, 27 Mar 2020 11:29:49 -0400 Subject: [PATCH 089/265] Fixed missing character. --- Oqtane.Client/Modules/Admin/Register/Index.razor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index a3624623..efef6e81 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -97,7 +97,7 @@ { NavigationManager.NavigateTo(NavigateUrl("")); } -} + private void IsValidEmail() { if (_email != ""){ @@ -109,4 +109,4 @@ @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250)); } -} +} \ No newline at end of file From f8d77320250540d99484f519aaace656066ed162 Mon Sep 17 00:00:00 2001 From: Aubrey Date: Fri, 27 Mar 2020 13:42:14 -0400 Subject: [PATCH 090/265] Moved logic to the Utilities class. --- .../Modules/Admin/Register/Index.razor | 15 +-------------- Oqtane.Shared/Shared/Utilities.cs | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index efef6e81..8401c1f0 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Register -@using System.Text.RegularExpressions; @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService @@ -50,7 +49,7 @@ try { _message = ""; - IsValidEmail(); + _isEmailValid = Utilities.IsValidEmail(_email); if (_username != "" && _password != "" && _confirm != "" && _isEmailValid) { if (_password == _confirm) @@ -97,16 +96,4 @@ { NavigationManager.NavigateTo(NavigateUrl("")); } - - private void IsValidEmail() - { - if (_email != ""){ - _isEmailValid = true; - } - - _isEmailValid = Regex.IsMatch(_email, - @"^(?("")("".+?(? Date: Sat, 28 Mar 2020 00:33:23 -0700 Subject: [PATCH 091/265] Rename template .sql file --- .../Modules/[Module]/Scripts/{00.00.01.sql => 01.00.00.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Scripts/{00.00.01.sql => 01.00.00.sql} (100%) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Scripts/00.00.01.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Scripts/01.00.00.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Scripts/00.00.01.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Scripts/01.00.00.sql From 940cdcb349f37f1129a4bb83ea778bc7a0220c00 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Wed, 25 Mar 2020 15:30:16 +0100 Subject: [PATCH 092/265] Database Manager done: + master.sql as resource + implemented incremental database changes also for Master + dbUp sql script variables implemented + improved database handling and creation code + simpified database creation + almost all Database and Tenant creation moved to DatabaseManager.cs (rest code marked with TODO) + Unattended install of master can be performed by settings in appsettings.json + Improved IsInstalled checking + Removed DBSchema field from Tenant + Default database and site creation moved to Program.Main --- Oqtane.Client/Modules/Admin/Sites/Add.razor | 9 +- Oqtane.Client/Modules/Admin/Tenants/Add.razor | 40 +- .../Modules/Admin/Tenants/Edit.razor | 12 +- Oqtane.Client/Services/InstallationService.cs | 13 +- .../Interfaces/IInstallationService.cs | 3 +- Oqtane.Client/UI/Installer.razor | 203 +++++----- .../Controllers/InstallationController.cs | 281 ++------------ Oqtane.Server/Controllers/UserController.cs | 156 ++++---- .../OqtaneServiceCollectionExtensions.cs | 2 +- .../Infrastructure/DatabaseManager.cs | 367 ++++++++++++++++++ Oqtane.Server/Oqtane.Server.csproj | 12 +- Oqtane.Server/Program.cs | 11 +- .../Repository/Context/DBContextBase.cs | 12 +- .../Repository/Context/InstallationContext.cs | 23 ++ .../Repository/SiteTemplateRepository.cs | 2 +- Oqtane.Server/Repository/TenantRepository.cs | 10 +- Oqtane.Server/Repository/TenantResolver.cs | 40 +- .../{Master.sql => Master.00.00.00.sql} | 4 +- Oqtane.Server/Scripts/Master.00.00.01.sql | 2 + .../{00.00.00.sql => Tenant.00.00.00.sql} | 12 +- .../{00.00.01.sql => Tenant.00.00.01.sql} | 0 Oqtane.Server/SilentInstall.json | 7 + Oqtane.Server/Startup.cs | 12 +- Oqtane.Server/appsettings.json | 4 + Oqtane.Shared/Models/Tenant.cs | 2 - Oqtane.Shared/Shared/InstallConfig.cs | 12 + 26 files changed, 726 insertions(+), 525 deletions(-) create mode 100644 Oqtane.Server/Infrastructure/DatabaseManager.cs create mode 100644 Oqtane.Server/Repository/Context/InstallationContext.cs rename Oqtane.Server/Scripts/{Master.sql => Master.00.00.00.sql} (96%) create mode 100644 Oqtane.Server/Scripts/Master.00.00.01.sql rename Oqtane.Server/Scripts/{00.00.00.sql => Tenant.00.00.00.sql} (96%) rename Oqtane.Server/Scripts/{00.00.01.sql => Tenant.00.00.01.sql} (100%) create mode 100644 Oqtane.Server/SilentInstall.json create mode 100644 Oqtane.Shared/Shared/InstallConfig.cs diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 7700a3b0..2afb5ed4 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -164,7 +164,7 @@ else _tenantid = (string)e.Value; if (_tenantid != "-1") { - Tenant tenant = _tenants.Where(item => item.TenantId == int.Parse(_tenantid)).FirstOrDefault(); + Tenant tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid)); if (tenant != null) { _isinitialized = tenant.IsInitialized; @@ -273,8 +273,11 @@ else if (user != null) { Tenant tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid)); - tenant.IsInitialized = true; - await TenantService.UpdateTenantAsync(tenant); + if (tenant != null) + { + tenant.IsInitialized = true; + await TenantService.UpdateTenantAsync(tenant); + } } } await Log(aliases[0], LogLevel.Information, "", null, "Site Created {Site}", site); diff --git a/Oqtane.Client/Modules/Admin/Tenants/Add.razor b/Oqtane.Client/Modules/Admin/Tenants/Add.razor index 4348bda7..9fe32192 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Add.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Add.razor @@ -67,14 +67,6 @@ - - - -
- - - -
Cancel @@ -88,7 +80,6 @@ string database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm"); string username = ""; string password = ""; - string schema = ""; string integratedsecurity = "display: none;"; private void SetIntegratedSecurity(ChangeEventArgs e) @@ -109,32 +100,41 @@ { ShowProgressIndicator(); - string connectionstring = ""; + string connectionString = ""; if (type == "LocalDB") { - connectionstring = "Data Source=" + server + ";AttachDbFilename=|DataDirectory|\\" + database + ".mdf;Initial Catalog=" + database + ";Integrated Security=SSPI;"; + connectionString = "Data Source=" + server + ";AttachDbFilename=|DataDirectory|\\" + database + ".mdf;Initial Catalog=" + database + ";Integrated Security=SSPI;"; } else { - connectionstring = "Data Source=" + server + ";Initial Catalog=" + database + ";"; + connectionString = "Data Source=" + server + ";Initial Catalog=" + database + ";"; if (integratedsecurity == "display: none;") { - connectionstring += "Integrated Security=SSPI;"; + connectionString += "Integrated Security=SSPI;"; } else { - connectionstring += "User ID=" + username + ";Password=" + password; + connectionString += "User ID=" + username + ";Password=" + password; } } - Installation installation = await InstallationService.Install(connectionstring); + + var config = new InstallConfig + { + IsMaster = false, + ConnectionString = connectionString, + }; + + Installation installation = await InstallationService.Install(config); if (installation.Success) { - Tenant tenant = new Tenant(); - tenant.Name = name; - tenant.DBConnectionString = connectionstring; - tenant.DBSchema = schema; - tenant.IsInitialized = false; + //TODO : Move to Database Manager + Tenant tenant = new Tenant + { + Name = name, + DBConnectionString = connectionString, + IsInitialized = false + }; await TenantService.AddTenantAsync(tenant); await logger.LogInformation("Tenant Created {Tenant}", tenant); diff --git a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor index 0224cd1e..afba20e7 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor @@ -20,14 +20,7 @@
- - - -
Cancel @@ -38,7 +31,6 @@ int tenantid; string name = ""; string connectionstring = ""; - string schema = ""; protected override async Task OnInitializedAsync() { @@ -50,7 +42,6 @@ { name = tenant.Name; connectionstring = tenant.DBConnectionString; - schema = tenant.DBSchema; } } catch (Exception ex) @@ -70,7 +61,6 @@ { tenant.Name = name; tenant.DBConnectionString = connectionstring; - tenant.DBSchema = schema; await TenantService.UpdateTenantAsync(tenant); await logger.LogInformation("Tenant Saved {TenantId}", tenantid); diff --git a/Oqtane.Client/Services/InstallationService.cs b/Oqtane.Client/Services/InstallationService.cs index 31a1e3dd..0a11650b 100644 --- a/Oqtane.Client/Services/InstallationService.cs +++ b/Oqtane.Client/Services/InstallationService.cs @@ -19,24 +19,21 @@ namespace Oqtane.Services _navigationManager = navigationManager; } - private string Apiurl - { - get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "Installation"); } - } + private string ApiUrl => CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "Installation"); public async Task IsInstalled() { - return await _http.GetJsonAsync(Apiurl + "/installed"); + return await _http.GetJsonAsync(ApiUrl + "/installed"); } - public async Task Install(string connectionstring) + public async Task Install(InstallConfig config) { - return await _http.PostJsonAsync(Apiurl, connectionstring); + return await _http.PostJsonAsync(ApiUrl, config); } public async Task Upgrade() { - return await _http.GetJsonAsync(Apiurl + "/upgrade"); + return await _http.GetJsonAsync(ApiUrl + "/upgrade"); } } } diff --git a/Oqtane.Client/Services/Interfaces/IInstallationService.cs b/Oqtane.Client/Services/Interfaces/IInstallationService.cs index 580fd225..c9eeaca8 100644 --- a/Oqtane.Client/Services/Interfaces/IInstallationService.cs +++ b/Oqtane.Client/Services/Interfaces/IInstallationService.cs @@ -1,12 +1,13 @@ using Oqtane.Models; using System.Threading.Tasks; +using Oqtane.Shared; namespace Oqtane.Services { public interface IInstallationService { Task IsInstalled(); - Task Install(string connectionstring); + Task Install(InstallConfig config); Task Upgrade(); } } diff --git a/Oqtane.Client/UI/Installer.razor b/Oqtane.Client/UI/Installer.razor index 071a97bb..f2985b1c 100644 --- a/Oqtane.Client/UI/Installer.razor +++ b/Oqtane.Client/UI/Installer.razor @@ -7,117 +7,117 @@
- +
-
+
-

Database Configuration


+

Database Configuration


- - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + +
- - - -
- - - -
- - - -
- - - -
- - - -
- - - -
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
-

Application Administrator


+

Application Administrator


- - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
- - - -
- - - -
- - - -
- - - -
+ + + +
+ + + +
+ + + +
+ + + +
-
+
-

- @((MarkupString)_message) +

+ @((MarkupString) _message)
@@ -140,7 +140,7 @@ private void SetIntegratedSecurity(ChangeEventArgs e) { - if (Convert.ToBoolean((string)e.Value)) + if (Convert.ToBoolean((string) e.Value)) { _integratedSecurityDisplay = "display: none;"; } @@ -172,10 +172,20 @@ else { connectionstring += "User ID=" + _username + ";Password=" + _password; - } } - Installation installation = await InstallationService.Install(connectionstring); + + var config = new InstallConfig + { + ConnectionString = connectionstring, + HostUser = _hostUsername, + HostEmail = _hostEmail, + Password = _hostPassword, + IsMaster = true, + }; + + Installation installation = await InstallationService.Install(config); + //TODO: Should be moved to Database manager if (installation.Success) { Site site = new Site(); @@ -208,4 +218,5 @@ _message = "
Please Enter All Fields And Ensure Passwords Match And Are Greater Than 5 Characters In Length
"; } } + } diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index 763a570d..bb99f23f 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -1,16 +1,9 @@ -using DbUp; -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Oqtane.Models; using Oqtane.Shared; -using System; -using System.Data.SqlClient; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading; +using Oqtane.Infrastructure; using Oqtane.Infrastructure.Interfaces; // ReSharper disable StringIndexOfIsCultureSpecific.1 @@ -22,155 +15,45 @@ namespace Oqtane.Controllers { private readonly IConfigurationRoot _config; private readonly IInstallationManager _installationManager; + private readonly DatabaseManager _databaseManager; - public InstallationController(IConfigurationRoot config, IInstallationManager installationManager) + public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, DatabaseManager databaseManager) { _config = config; _installationManager = installationManager; + _databaseManager = databaseManager; } // POST api/ [HttpPost] - public Installation Post([FromBody] string connectionString) + public Installation Post([FromBody] InstallConfig config) { - var installation = new Installation { Success = false, Message = "" }; + //TODO Security ???? + var installation = new Installation {Success = false, Message = ""}; - if (ModelState.IsValid) + if (ModelState.IsValid && (!_databaseManager.IsInstalled || !config.IsMaster)) { - bool master = false; - string defaultconnectionstring = _config.GetConnectionString("DefaultConnection"); - if (string.IsNullOrEmpty(defaultconnectionstring) || connectionString == defaultconnectionstring) - { - master = true; - } + bool master = config.IsMaster; - bool exists = false; - if (master) - { - exists = IsInstalled().Success; - } + config.Alias = config.Alias ?? HttpContext.Request.Host.Value; + var result = DatabaseManager.InstallDatabase(config); - if (!exists) + if (result.Success) { - string datadirectory = AppDomain.CurrentDomain.GetData("DataDirectory").ToString(); - connectionString = connectionString.Replace("|DataDirectory|", datadirectory); - - SqlConnection connection = new SqlConnection(connectionString); - try + if (master) { - using (connection) - { - connection.Open(); - } - exists = true; - } - catch - { - // database does not exist + _config.Reload(); } - // try to create database if it does not exist - if (!exists) - { - string masterConnectionString = ""; - string databaseName = ""; - string[] fragments = connectionString.Split(';', StringSplitOptions.RemoveEmptyEntries); - foreach (string fragment in fragments) - { - if (fragment.ToLower().Contains("initial catalog=") || fragment.ToLower().Contains("database=")) - { - databaseName = fragment.Substring(fragment.IndexOf("=") + 1); - } - else - { - if (!fragment.ToLower().Contains("attachdbfilename=")) - { - masterConnectionString += fragment + ";"; - } - } - } - connection = new SqlConnection(masterConnectionString); - try - { - using (connection) - { - connection.Open(); - SqlCommand command; - if (connectionString.ToLower().Contains("attachdbfilename=")) // LocalDB - { - command = new SqlCommand("CREATE DATABASE [" + databaseName + "] ON ( NAME = '" + databaseName + "', FILENAME = '" + datadirectory + "\\" + databaseName + ".mdf')", connection); - } - else - { - command = new SqlCommand("CREATE DATABASE [" + databaseName + "]", connection); - } - command.ExecuteNonQuery(); - exists = true; - } - } - catch (Exception ex) - { - installation.Message = "Can Not Create Database - " + ex.Message; - } - - // sleep to allow SQL server to attach new database - Thread.Sleep(5000); - } - - if (exists) - { - // get master initialization script and update connectionstring and alias in seed data - string initializationScript = ""; - if (master) - { - using (StreamReader reader = new StreamReader(Directory.GetCurrentDirectory() + "\\Scripts\\Master.sql")) - { - initializationScript = reader.ReadToEnd(); - } - initializationScript = initializationScript.Replace("{ConnectionString}", connectionString.Replace(datadirectory, "|DataDirectory|")); - initializationScript = initializationScript.Replace("{Alias}", HttpContext.Request.Host.Value); - } - - var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) - .WithScript(new DbUp.Engine.SqlScript("Master.sql", initializationScript)) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly()); // tenant scripts should be added to /Scripts folder as Embedded Resources - var dbUpgrade = dbUpgradeConfig.Build(); - if (dbUpgrade.IsUpgradeRequired()) - { - var result = dbUpgrade.PerformUpgrade(); - if (!result.Successful) - { - installation.Message = result.Error.Message; - } - else - { - // update appsettings - if (master) - { - string config = ""; - using (StreamReader reader = new StreamReader(Directory.GetCurrentDirectory() + "\\appsettings.json")) - { - config = reader.ReadToEnd(); - } - connectionString = connectionString.Replace(datadirectory, "|DataDirectory|"); - connectionString = connectionString.Replace(@"\", @"\\"); - config = config.Replace("DefaultConnection\": \"", "DefaultConnection\": \"" + connectionString); - using (StreamWriter writer = new StreamWriter(Directory.GetCurrentDirectory() + "\\appsettings.json")) - { - writer.WriteLine(config); - } - _config.Reload(); - } - installation.Success = true; - } - } - } - } - else - { - installation.Message = "Application Is Already Installed"; + installation.Success = true; + return installation; } + + installation.Message = result.Message; + return installation; } + + installation.Message = "Application Is Already Installed"; return installation; } @@ -178,133 +61,21 @@ namespace Oqtane.Controllers [HttpGet("installed")] public Installation IsInstalled() { - var installation = new Installation { Success = false, Message = "" }; + var installation = new Installation {Success = false, Message = ""}; - string datadirectory = AppDomain.CurrentDomain.GetData("DataDirectory").ToString(); - string connectionString = _config.GetConnectionString("DefaultConnection"); - connectionString = connectionString.Replace("|DataDirectory|", datadirectory); - - if (!string.IsNullOrEmpty(connectionString)) - { - SqlConnection connection = new SqlConnection(connectionString); - try - { - using (connection) - { - connection.Open(); - } - installation.Success = true; - } - catch - { - // database does not exist - installation.Message = "Database Does Not Exist"; - } - } - else - { - installation.Message = "Connection String Has Not Been Specified In Oqtane.Server\\appsettings.json"; - } - - if (installation.Success) - { - var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) - .WithScript(new DbUp.Engine.SqlScript("Master.sql", "")); - var dbUpgrade = dbUpgradeConfig.Build(); - installation.Success = !dbUpgrade.IsUpgradeRequired(); - if (!installation.Success) - { - installation.Message = "Master Installation Scripts Have Not Been Executed"; - } - else - { - using (var db = new InstallationContext(connectionString)) - { - ApplicationVersion version = db.ApplicationVersion.ToList().LastOrDefault(); - if (version == null || version.Version != Constants.Version) - { - version = new ApplicationVersion(); - version.Version = Constants.Version; - version.CreatedOn = DateTime.UtcNow; - db.ApplicationVersion.Add(version); - db.SaveChanges(); - } - } - - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() - .Where(item => item.FullName.Contains(".Module.")).ToArray(); - - // get tenants - using (var db = new InstallationContext(connectionString)) - { - foreach (Tenant tenant in db.Tenant.ToList()) - { - connectionString = tenant.DBConnectionString; - connectionString = connectionString.Replace("|DataDirectory|", datadirectory); - - // upgrade framework - dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly()); - dbUpgrade = dbUpgradeConfig.Build(); - if (dbUpgrade.IsUpgradeRequired()) - { - var result = dbUpgrade.PerformUpgrade(); - if (!result.Successful) - { - // TODO: log result.Error.Message - problem is logger is not available here - } - } - // iterate through Oqtane module assemblies and execute any database scripts - foreach (Assembly assembly in assemblies) - { - InstallModule(assembly, connectionString); - } - } - } - } - } + installation.Success = _databaseManager.IsInstalled; + installation.Message = _databaseManager.Message; return installation; } - private void InstallModule(Assembly assembly, string connectionstring) - { - var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionstring) - .WithScriptsEmbeddedInAssembly(assembly); // scripts must be included as Embedded Resources - var dbUpgrade = dbUpgradeConfig.Build(); - if (dbUpgrade.IsUpgradeRequired()) - { - var result = dbUpgrade.PerformUpgrade(); - if (!result.Successful) - { - // TODO: log result.Error.Message - problem is logger is not available here - } - } - } - [HttpGet("upgrade")] [Authorize(Roles = Constants.HostRole)] public Installation Upgrade() { - var installation = new Installation { Success = true, Message = "" }; + var installation = new Installation {Success = true, Message = ""}; _installationManager.UpgradeFramework(); return installation; } } - - public class InstallationContext : DbContext - { - private readonly string _connectionString; - - public InstallationContext(string connectionString) - { - _connectionString = connectionString; - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => optionsBuilder.UseSqlServer(_connectionString); - - public virtual DbSet ApplicationVersion { get; set; } - public virtual DbSet Tenant { get; set; } - } } diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 19d0aaf9..d2bc94db 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -75,95 +75,107 @@ namespace Oqtane.Controllers [HttpPost] public async Task Post([FromBody] User user) { - User newUser = null; - if (ModelState.IsValid) { - // users created by non-administrators must be verified - bool verified = !(!User.IsInRole(Constants.AdminRole) && user.Username != Constants.HostUser); + var newUser = await CreateUser(user); + return newUser; + } - IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); - if (identityuser == null) + return null; + } + + //TODO shoud be moved to another layer + private async Task CreateUser(User user) + { + User newUser = null; + // users created by non-administrators must be verified + bool verified = !(!User.IsInRole(Constants.AdminRole) && user.Username != Constants.HostUser); + + IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); + if (identityuser == null) + { + identityuser = new IdentityUser(); + identityuser.UserName = user.Username; + identityuser.Email = user.Email; + identityuser.EmailConfirmed = verified; + var result = await _identityUserManager.CreateAsync(identityuser, user.Password); + if (result.Succeeded) { - identityuser = new IdentityUser(); - identityuser.UserName = user.Username; - identityuser.Email = user.Email; - identityuser.EmailConfirmed = verified; - var result = await _identityUserManager.CreateAsync(identityuser, user.Password); - if (result.Succeeded) + user.LastLoginOn = null; + user.LastIPAddress = ""; + newUser = _users.AddUser(user); + if (!verified) { - user.LastLoginOn = null; - user.LastIPAddress = ""; - newUser = _users.AddUser(user); - if (!verified) - { - Notification notification = new Notification(); - notification.SiteId = user.SiteId; - notification.FromUserId = null; - notification.ToUserId = newUser.UserId; - notification.ToEmail = ""; - notification.Subject = "User Account Verification"; - string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser); - string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token); - notification.Body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!"; - notification.ParentId = null; - notification.CreatedOn = DateTime.UtcNow; - notification.IsDelivered = false; - notification.DeliveredOn = null; - _notifications.AddNotification(notification); - } - - // assign to host role if this is the host user ( initial installation ) - if (user.Username == Constants.HostUser) - { - int hostroleid = _roles.GetRoles(user.SiteId, true).Where(item => item.Name == Constants.HostRole).FirstOrDefault().RoleId; - UserRole userrole = new UserRole(); - userrole.UserId = newUser.UserId; - userrole.RoleId = hostroleid; - userrole.EffectiveDate = null; - userrole.ExpiryDate = null; - _userRoles.AddUserRole(userrole); - } - - // add folder for user - Folder folder = _folders.GetFolder(user.SiteId, "Users\\"); - if (folder != null) - { - _folders.AddFolder(new Folder { SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", Path = folder.Path + newUser.UserId.ToString() + "\\", Order = 1, IsSystem = true, - Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"}]" }); - } + Notification notification = new Notification(); + notification.SiteId = user.SiteId; + notification.FromUserId = null; + notification.ToUserId = newUser.UserId; + notification.ToEmail = ""; + notification.Subject = "User Account Verification"; + string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser); + string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token); + notification.Body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!"; + notification.ParentId = null; + notification.CreatedOn = DateTime.UtcNow; + notification.IsDelivered = false; + notification.DeliveredOn = null; + _notifications.AddNotification(notification); } - } - else - { - var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false); - if (result.Succeeded) - { - newUser = _users.GetUser(user.Username); - } - } - if (newUser != null && user.Username != Constants.HostUser) - { - // add auto assigned roles to user for site - List roles = _roles.GetRoles(user.SiteId).Where(item => item.IsAutoAssigned).ToList(); - foreach (Role role in roles) + // assign to host role if this is the host user ( initial installation ) + if (user.Username == Constants.HostUser) { + int hostroleid = _roles.GetRoles(user.SiteId, true).Where(item => item.Name == Constants.HostRole).FirstOrDefault().RoleId; UserRole userrole = new UserRole(); userrole.UserId = newUser.UserId; - userrole.RoleId = role.RoleId; + userrole.RoleId = hostroleid; userrole.EffectiveDate = null; userrole.ExpiryDate = null; _userRoles.AddUserRole(userrole); } - } - if (newUser != null) - { - newUser.Password = ""; // remove sensitive information - _logger.Log(user.SiteId, LogLevel.Information, this, LogFunction.Create, "User Added {User}", newUser); + // add folder for user + Folder folder = _folders.GetFolder(user.SiteId, "Users\\"); + if (folder != null) + { + _folders.AddFolder(new Folder + { + SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", Path = folder.Path + newUser.UserId.ToString() + "\\", Order = 1, IsSystem = true, + Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + + newUser.UserId.ToString() + "]\"}]" + }); + } } } + else + { + var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false); + if (result.Succeeded) + { + newUser = _users.GetUser(user.Username); + } + } + + if (newUser != null && user.Username != Constants.HostUser) + { + // add auto assigned roles to user for site + List roles = _roles.GetRoles(user.SiteId).Where(item => item.IsAutoAssigned).ToList(); + foreach (Role role in roles) + { + UserRole userrole = new UserRole(); + userrole.UserId = newUser.UserId; + userrole.RoleId = role.RoleId; + userrole.EffectiveDate = null; + userrole.ExpiryDate = null; + _userRoles.AddUserRole(userrole); + } + } + + if (newUser != null) + { + newUser.Password = ""; // remove sensitive information + _logger.Log(user.SiteId, LogLevel.Information, this, LogFunction.Create, "User Added {User}", newUser); + } return newUser; } diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index e2bf622e..aef47901 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -115,7 +115,7 @@ namespace Microsoft.Extensions.DependencyInjection foreach (var file in assembliesFolder.EnumerateFiles($"*.{pattern}.*.dll")) { // check if assembly is already loaded - var assembly = Assemblies.FirstOrDefault(a => a.Location == file.FullName); + var assembly = Assemblies.FirstOrDefault(a =>!a.IsDynamic && a.Location == file.FullName); if (assembly == null) { // load assembly from stream to prevent locking file ( as long as dependencies are in /bin they will load as well ) diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs new file mode 100644 index 00000000..bccbf5b8 --- /dev/null +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -0,0 +1,367 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Reflection; +using DbUp; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Oqtane.Controllers; +using Oqtane.Models; +using Oqtane.Repository; +using Oqtane.Shared; +using File = System.IO.File; + +namespace Oqtane.Infrastructure +{ + public class DatabaseManager + { + private readonly IConfigurationRoot _config; + private readonly IServiceScopeFactory _serviceScopeFactory; + private bool _isInstalled; + + public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory) + { + _config = config; + _serviceScopeFactory = serviceScopeFactory; + } + + public string Message { get; set; } + + public bool IsInstalled + { + get + { + if (!_isInstalled) _isInstalled = CheckInstallState(); + + return _isInstalled; + } + set => _isInstalled = value; + } + + private bool CheckInstallState() + { + var defaultConnectionString = _config.GetConnectionString("DefaultConnection"); + var result = !string.IsNullOrEmpty(defaultConnectionString); + if (result) + { + using (var scope = _serviceScopeFactory.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + result = dbContext.Database.CanConnect(); + } + if (result) + { + //I think this is obsolete now and not accurate, maybe check presence of some table, Version ??? + var dbUpgradeConfig = DeployChanges + .To + .SqlDatabase(defaultConnectionString) + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master")); + + result = !dbUpgradeConfig.Build().IsUpgradeRequired(); + if (!result) Message = "Master Installation Scripts Have Not Been Executed"; + } + else + { + Message = "Database is not avaiable"; + } + } + else + { + Message = "Connection string is empty"; + } + + return result; + } + + + public static string NormalizeConnectionString(string connectionString, string dataDirectory) + { + connectionString = connectionString + .Replace("|DataDirectory|", dataDirectory); + //.Replace(@"\", @"\\"); + return connectionString; + } + + public static Installation InstallDatabase([NotNull] InstallConfig installConfig) + { + var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); + var result = new Installation {Success = false, Message = ""}; + + var alias = installConfig.Alias; + var connectionString = NormalizeConnectionString(installConfig.ConnectionString, dataDirectory); + + if (string.IsNullOrEmpty(connectionString) || string.IsNullOrEmpty(alias)) + { + result = new Installation + { + Success = false, + Message = "Connection string is empty", + }; + return result; + } + + result = MasterMigration(connectionString, alias, result, installConfig.IsMaster); + if (installConfig.IsMaster && result.Success) + { + WriteVersionInfo(connectionString); + TenantMigration(connectionString, dataDirectory); + UpdateOqtaneSettings(connectionString); + AddOrUpdateAppSetting("Oqtane:DefaultAlias", alias); + } + + return result; + } + + private static Installation MasterMigration(string connectionString, string alias, Installation result, bool master) + { + if (result == null) result = new Installation {Success = false, Message = string.Empty}; + + try + { + // create empty database if does not exists + // dbup database creation does not work correctly on localdb databases + using (var dbc = new DbContext(new DbContextOptionsBuilder().UseSqlServer(connectionString).Options)) + { + dbc.Database.EnsureCreated(); + } + } + catch (Exception e) + { + result = new Installation + { + Success = false, + Message = e.Message, + }; + Console.WriteLine(e); + return result; + } + + var dbUpgradeConfig = DeployChanges + .To + .SqlDatabase(connectionString) + .WithVariable("ConnectionString", connectionString) + .WithVariable("Alias", alias) + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master") && master || s.Contains("Tenant")) + ; + + var dbUpgrade = dbUpgradeConfig.Build(); + if (!dbUpgrade.IsUpgradeRequired()) + { + result.Success = true; + result.Message = string.Empty; + return result; + } + + var upgradeResult = dbUpgrade.PerformUpgrade(); + if (!upgradeResult.Successful) + { + Console.WriteLine(upgradeResult.Error.Message); + result.Message = upgradeResult.Error.Message; + } + else + { + result.Success = true; + } + + return result; + } + + private static void ModuleMigration(Assembly assembly, string connectionString) + { + var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) + .WithScriptsEmbeddedInAssembly(assembly); // scripts must be included as Embedded Resources + var dbUpgrade = dbUpgradeConfig.Build(); + if (dbUpgrade.IsUpgradeRequired()) + { + var result = dbUpgrade.PerformUpgrade(); + if (!result.Successful) + { + // TODO: log result.Error.Message - problem is logger is not available here + } + } + } + + private static void WriteVersionInfo(string connectionString) + { + using (var db = new InstallationContext(connectionString)) + { + var version = db.ApplicationVersion.ToList().LastOrDefault(); + if (version == null || version.Version != Constants.Version) + { + version = new ApplicationVersion {Version = Constants.Version, CreatedOn = DateTime.UtcNow}; + db.ApplicationVersion.Add(version); + db.SaveChanges(); + } + } + } + + private static void TenantMigration(string connectionString, string dataDirectory) + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies() + .Where(item => item.FullName != null && item.FullName.Contains(".Module.")).ToArray(); + + // get tenants + using (var db = new InstallationContext(connectionString)) + { + foreach (var tenant in db.Tenant.ToList()) + { + connectionString = NormalizeConnectionString(tenant.DBConnectionString, dataDirectory); + // upgrade framework + var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant")); + var dbUpgrade = dbUpgradeConfig.Build(); + if (dbUpgrade.IsUpgradeRequired()) + { + var result = dbUpgrade.PerformUpgrade(); + if (!result.Successful) + { + // TODO: log result.Error.Message - problem is logger is not available here + } + } + + // iterate through Oqtane module assemblies and execute any database scripts + foreach (var assembly in assemblies) ModuleMigration(assembly, connectionString); + } + } + } + + public static void UpdateOqtaneSettings(string connectionString) + { + AddOrUpdateAppSetting("ConnectionStrings:DefaultConnection", connectionString); + //AddOrUpdateAppSetting("Oqtane:DefaultAlias", connectionString); + } + + + public static void AddOrUpdateAppSetting(string sectionPathKey, T value) + { + try + { + var filePath = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json"); + var json = File.ReadAllText(filePath); + dynamic jsonObj = JsonConvert.DeserializeObject(json); + + SetValueRecursively(sectionPathKey, jsonObj, value); + + string output = JsonConvert.SerializeObject(jsonObj, Formatting.Indented); + File.WriteAllText(filePath, output); + } + catch (Exception ex) + { + Console.WriteLine("Error writing app settings | {0}", ex); + } + } + + private static void SetValueRecursively(string sectionPathKey, dynamic jsonObj, T value) + { + // split the string at the first ':' character + var remainingSections = sectionPathKey.Split(":", 2); + + var currentSection = remainingSections[0]; + if (remainingSections.Length > 1) + { + // continue with the procress, moving down the tree + var nextSection = remainingSections[1]; + SetValueRecursively(nextSection, jsonObj[currentSection], value); + } + else + { + // we've got to the end of the tree, set the value + jsonObj[currentSection] = value; + } + } + + public void StartupMigration() + { + var defaultConnectionString = _config.GetConnectionString("DefaultConnection"); + var defaultAlias = _config.GetSection("Oqtane").GetValue("DefaultAlias", string.Empty); + + // if no values specified, fallback to IDE installer + if (string.IsNullOrEmpty(defaultConnectionString) || string.IsNullOrEmpty(defaultAlias)) + { + IsInstalled = false; + return; + } + + var result = MasterMigration(defaultConnectionString, defaultAlias, null, true); + IsInstalled = result.Success; + if (_isInstalled) + BuildDefaultSite(); + } + + public void BuildDefaultSite() + { + using (var scope = _serviceScopeFactory.CreateScope()) + { + //Gather required services + var siteRepository = scope.ServiceProvider.GetRequiredService(); + + // Build default site only if no site present + if (siteRepository.GetSites().Any()) return; + + var users = scope.ServiceProvider.GetRequiredService(); + var roles = scope.ServiceProvider.GetRequiredService(); + var userRoles = scope.ServiceProvider.GetRequiredService(); + var folders = scope.ServiceProvider.GetRequiredService(); + var identityUserManager = scope.ServiceProvider.GetRequiredService>(); + + var site = new Site + { + TenantId = -1, + Name = "Default Site", + LogoFileId = null, + DefaultThemeType = Constants.DefaultTheme, + DefaultLayoutType = Constants.DefaultLayout, + DefaultContainerType = Constants.DefaultContainer, + }; + site = siteRepository.AddSite(site); + + var user = new User + { + SiteId = site.SiteId, + Username = Constants.HostUser, + //TODO Decide default password or throw exception ?? + Password = _config.GetSection("Oqtane").GetValue("DefaultPassword", "oQtane123"), + Email = _config.GetSection("Oqtane").GetValue("DefaultEmail", "nobody@cortonso.com"), + DisplayName = Constants.HostUser, + }; + CreateHostUser(folders, userRoles, roles, users, identityUserManager, user); + } + } + + + private static void CreateHostUser(IFolderRepository folderRepository, IUserRoleRepository userRoleRepository, IRoleRepository roleRepository, IUserRepository userRepository, UserManager identityUserManager, User user) + { + var identityUser = new IdentityUser {UserName = user.Username, Email = user.Email, EmailConfirmed = true}; + var result = identityUserManager.CreateAsync(identityUser, user.Password).GetAwaiter().GetResult(); + + if (result.Succeeded) + { + user.LastLoginOn = null; + user.LastIPAddress = ""; + var newUser = userRepository.AddUser(user); + + // assign to host role if this is the host user ( initial installation ) + if (user.Username == Constants.HostUser) + { + var hostRoleId = roleRepository.GetRoles(user.SiteId, true).FirstOrDefault(item => item.Name == Constants.HostRole)?.RoleId ?? 0; + var userRole = new UserRole {UserId = newUser.UserId, RoleId = hostRoleId, EffectiveDate = null, ExpiryDate = null}; + userRoleRepository.AddUserRole(userRole); + } + + // add folder for user + var folder = folderRepository.GetFolder(user.SiteId, "Users\\"); + if (folder != null) + folderRepository.AddFolder(new Folder + { + SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", Path = folder.Path + newUser.UserId + "\\", Order = 1, IsSystem = true, + Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + + newUser.UserId + "]\"}]", + }); + } + } + } +} diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 32847187..7471c460 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -27,14 +27,10 @@ - - - - - - - - + + + + diff --git a/Oqtane.Server/Program.cs b/Oqtane.Server/Program.cs index b440e758..6402cf5e 100644 --- a/Oqtane.Server/Program.cs +++ b/Oqtane.Server/Program.cs @@ -4,6 +4,8 @@ using Microsoft.Extensions.Hosting; using Microsoft.AspNetCore.Blazor.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.AspNetCore; +using Microsoft.Extensions.DependencyInjection; +using Oqtane.Infrastructure; namespace Oqtane.Server { @@ -12,7 +14,14 @@ namespace Oqtane.Server #if DEBUG || RELEASE public static void Main(string[] args) { - CreateHostBuilder(args).Build().Run(); + var host = CreateHostBuilder(args).Build(); + using (var serviceScope = host.Services.GetRequiredService().CreateScope()) + { + var manager = serviceScope.ServiceProvider.GetService(); + manager.StartupMigration(); + } + //DatabaseManager.StartupMigration(); + host.Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => diff --git a/Oqtane.Server/Repository/Context/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs index e6aceed9..247006bd 100644 --- a/Oqtane.Server/Repository/Context/DBContextBase.cs +++ b/Oqtane.Server/Repository/Context/DBContextBase.cs @@ -22,21 +22,11 @@ namespace Oqtane.Repository protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(_tenant.DBConnectionString - .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString()) + .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()) ); base.OnConfiguring(optionsBuilder); } - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - if (_tenant.DBSchema != "") - { - modelBuilder.HasDefaultSchema(_tenant.DBSchema); - } - } - public override int SaveChanges() { ChangeTracker.DetectChanges(); diff --git a/Oqtane.Server/Repository/Context/InstallationContext.cs b/Oqtane.Server/Repository/Context/InstallationContext.cs new file mode 100644 index 00000000..7b971a16 --- /dev/null +++ b/Oqtane.Server/Repository/Context/InstallationContext.cs @@ -0,0 +1,23 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.EntityFrameworkCore; +using Oqtane.Models; + +namespace Oqtane.Repository +{ + + public class InstallationContext : DbContext + { + private readonly string _connectionString; + + public InstallationContext(string connectionString) + { + _connectionString = connectionString; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseSqlServer(_connectionString); + + public virtual DbSet ApplicationVersion { get; set; } + public virtual DbSet Tenant { get; set; } + } +} diff --git a/Oqtane.Server/Repository/SiteTemplateRepository.cs b/Oqtane.Server/Repository/SiteTemplateRepository.cs index 4d3b5f7b..c88934fd 100644 --- a/Oqtane.Server/Repository/SiteTemplateRepository.cs +++ b/Oqtane.Server/Repository/SiteTemplateRepository.cs @@ -41,7 +41,7 @@ namespace Oqtane.Repository var siteTemplateObject = ActivatorUtilities.CreateInstance(_serviceProvider, siteTemplateType); siteTemplate = new SiteTemplate { - Name = (string)siteTemplateType.GetProperty("Name").GetValue(siteTemplateObject), + Name = (string)siteTemplateType.GetProperty("Name")?.GetValue(siteTemplateObject), TypeName = siteTemplateType.AssemblyQualifiedName }; siteTemplates.Add(siteTemplate); diff --git a/Oqtane.Server/Repository/TenantRepository.cs b/Oqtane.Server/Repository/TenantRepository.cs index 19acf9fa..4af682d6 100644 --- a/Oqtane.Server/Repository/TenantRepository.cs +++ b/Oqtane.Server/Repository/TenantRepository.cs @@ -49,10 +49,14 @@ namespace Oqtane.Repository } public void DeleteTenant(int tenantId) - { + { Tenant tenant = _db.Tenant.Find(tenantId); - _db.Tenant.Remove(tenant); - _db.SaveChanges(); + if (tenant != null) + { + _db.Tenant.Remove(tenant); + _db.SaveChanges(); + } + _cache.Remove("tenants"); } } diff --git a/Oqtane.Server/Repository/TenantResolver.cs b/Oqtane.Server/Repository/TenantResolver.cs index 9760c4e6..8a5b85c4 100644 --- a/Oqtane.Server/Repository/TenantResolver.cs +++ b/Oqtane.Server/Repository/TenantResolver.cs @@ -29,43 +29,41 @@ namespace Oqtane.Repository { aliasName = accessor.HttpContext.Request.Host.Value; string path = accessor.HttpContext.Request.Path.Value; - string[] segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + string[] segments = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries); if (segments.Length > 1 && segments[1] == "api" && segments[0] != "~") { aliasName += "/" + segments[0]; } + if (aliasName.EndsWith("/")) { aliasName = aliasName.Substring(0, aliasName.Length - 1); } } } - else // background processes can pass in an alias using the SiteState service + else // background processes can pass in an alias using the SiteState service { - if (siteState != null) - { - aliasId = siteState.Alias.AliasId; - } + aliasId = siteState?.Alias?.AliasId ?? -1; } // get the alias and tenant - if (aliasId != -1 || aliasName != "") + IEnumerable aliases = aliasRepository.GetAliases().ToList(); // cached + if (aliasId != -1) { - IEnumerable aliases = aliasRepository.GetAliases(); // cached - IEnumerable tenants = tenantRepository.GetTenants(); // cached + _alias = aliases.FirstOrDefault(item => item.AliasId == aliasId); + } + else + { + + _alias = aliases.FirstOrDefault(item => item.Name == aliasName + //if here is only one alias and other methods fail, take it (case of startup install) + || aliases.Count() == 1); + } - if (aliasId != -1) - { - _alias = aliases.FirstOrDefault(item => item.AliasId == aliasId); - } - else - { - _alias = aliases.FirstOrDefault(item => item.Name == aliasName); - } - if (_alias != null) - { - _tenant = tenants.FirstOrDefault(item => item.TenantId == _alias.TenantId); - } + if (_alias != null) + { + IEnumerable tenants = tenantRepository.GetTenants(); // cached + _tenant = tenants.FirstOrDefault(item => item.TenantId == _alias.TenantId); } } diff --git a/Oqtane.Server/Scripts/Master.sql b/Oqtane.Server/Scripts/Master.00.00.00.sql similarity index 96% rename from Oqtane.Server/Scripts/Master.sql rename to Oqtane.Server/Scripts/Master.00.00.00.sql index 45e474c7..e1b64042 100644 --- a/Oqtane.Server/Scripts/Master.sql +++ b/Oqtane.Server/Scripts/Master.00.00.00.sql @@ -123,7 +123,7 @@ Create seed data SET IDENTITY_INSERT [dbo].[Tenant] ON GO INSERT [dbo].[Tenant] ([TenantId], [Name], [DBConnectionString], [DBSchema], [IsInitialized], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (1, N'Master', N'{ConnectionString}', N'', 1, '', getdate(), '', getdate()) +VALUES (1, N'Master', N'$ConnectionString$', N'', 1, '', getdate(), '', getdate()) GO SET IDENTITY_INSERT [dbo].[Tenant] OFF GO @@ -131,7 +131,7 @@ GO SET IDENTITY_INSERT [dbo].[Alias] ON GO INSERT [dbo].[Alias] ([AliasId], [Name], [TenantId], [SiteId], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (1, N'{Alias}', 1, 1, '', getdate(), '', getdate()) +VALUES (1, N'$Alias$', 1, 1, '', getdate(), '', getdate()) GO SET IDENTITY_INSERT [dbo].[Alias] OFF GO diff --git a/Oqtane.Server/Scripts/Master.00.00.01.sql b/Oqtane.Server/Scripts/Master.00.00.01.sql new file mode 100644 index 00000000..02943e96 --- /dev/null +++ b/Oqtane.Server/Scripts/Master.00.00.01.sql @@ -0,0 +1,2 @@ +alter table Tenant drop column DBSchema +go diff --git a/Oqtane.Server/Scripts/00.00.00.sql b/Oqtane.Server/Scripts/Tenant.00.00.00.sql similarity index 96% rename from Oqtane.Server/Scripts/00.00.00.sql rename to Oqtane.Server/Scripts/Tenant.00.00.00.sql index 666fe978..5157b401 100644 --- a/Oqtane.Server/Scripts/00.00.00.sql +++ b/Oqtane.Server/Scripts/Tenant.00.00.00.sql @@ -404,7 +404,7 @@ Create indexes */ -CREATE UNIQUE NONCLUSTERED INDEX IX_Setting ON dbo.Setting +CREATE UNIQUE NONCLUSTERED INDEX IX_Setting ON [dbo].Setting ( EntityName, EntityId, @@ -412,13 +412,13 @@ CREATE UNIQUE NONCLUSTERED INDEX IX_Setting ON dbo.Setting ) ON [PRIMARY] GO -CREATE UNIQUE NONCLUSTERED INDEX IX_User ON dbo.[User] +CREATE UNIQUE NONCLUSTERED INDEX IX_User ON [dbo].[User] ( Username ) ON [PRIMARY] GO -CREATE UNIQUE NONCLUSTERED INDEX IX_Permission ON dbo.Permission +CREATE UNIQUE NONCLUSTERED INDEX IX_Permission ON [dbo].Permission ( SiteId, EntityName, @@ -429,7 +429,7 @@ CREATE UNIQUE NONCLUSTERED INDEX IX_Permission ON dbo.Permission ) ON [PRIMARY] GO -CREATE UNIQUE NONCLUSTERED INDEX IX_Page ON dbo.Page +CREATE UNIQUE NONCLUSTERED INDEX IX_Page ON [dbo].Page ( SiteId, [Path], @@ -437,14 +437,14 @@ CREATE UNIQUE NONCLUSTERED INDEX IX_Page ON dbo.Page ) ON [PRIMARY] GO -CREATE UNIQUE NONCLUSTERED INDEX IX_UserRole ON dbo.UserRole +CREATE UNIQUE NONCLUSTERED INDEX IX_UserRole ON [dbo].UserRole ( RoleId, UserId ) ON [PRIMARY] GO -CREATE UNIQUE NONCLUSTERED INDEX IX_Folder ON dbo.Folder +CREATE UNIQUE NONCLUSTERED INDEX IX_Folder ON [dbo].Folder ( SiteId, [Path] diff --git a/Oqtane.Server/Scripts/00.00.01.sql b/Oqtane.Server/Scripts/Tenant.00.00.01.sql similarity index 100% rename from Oqtane.Server/Scripts/00.00.01.sql rename to Oqtane.Server/Scripts/Tenant.00.00.01.sql diff --git a/Oqtane.Server/SilentInstall.json b/Oqtane.Server/SilentInstall.json new file mode 100644 index 00000000..cf3e04b4 --- /dev/null +++ b/Oqtane.Server/SilentInstall.json @@ -0,0 +1,7 @@ +{ + "Alias" : "", + "DefaultConnection" : "", + "HostUser" : "host", + "Password" : "", + "HostEmail" : "" +} diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 07a47ee7..190e4adb 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -19,9 +19,12 @@ using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; using Oqtane.Security; using Oqtane.Services; -// DO NOT REMOVE - needed for client-side Blazor using Oqtane.Shared; + +#if WASM +// DO NOT REMOVE - needed for client-side Blazor using Microsoft.AspNetCore.ResponseCompression; +#endif namespace Oqtane { @@ -113,7 +116,7 @@ namespace Oqtane services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection") - .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString()) + .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()) )); services.AddDbContext(options => { }); @@ -160,7 +163,8 @@ namespace Oqtane services.AddSingleton(Configuration); services.AddSingleton(); services.AddSingleton(); - + services.AddSingleton(); + // register transient scoped core services services.AddTransient(); services.AddTransient(); @@ -203,6 +207,8 @@ namespace Oqtane { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Oqtane", Version = "v1" }); }); + + } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/Oqtane.Server/appsettings.json b/Oqtane.Server/appsettings.json index 7f07c90a..9e226a4d 100644 --- a/Oqtane.Server/appsettings.json +++ b/Oqtane.Server/appsettings.json @@ -1,5 +1,9 @@ { "ConnectionStrings": { "DefaultConnection": "" + }, + "Oqtane": { + "DefaultAlias": "", + "DefaultPassword": "" } } diff --git a/Oqtane.Shared/Models/Tenant.cs b/Oqtane.Shared/Models/Tenant.cs index bf6c4627..3f0a518a 100644 --- a/Oqtane.Shared/Models/Tenant.cs +++ b/Oqtane.Shared/Models/Tenant.cs @@ -7,9 +7,7 @@ namespace Oqtane.Models public int TenantId { get; set; } public string Name { get; set; } public string DBConnectionString { get; set; } - public string DBSchema { get; set; } public bool IsInitialized { get; set; } - public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } public string ModifiedBy { get; set; } diff --git a/Oqtane.Shared/Shared/InstallConfig.cs b/Oqtane.Shared/Shared/InstallConfig.cs new file mode 100644 index 00000000..f7f065b4 --- /dev/null +++ b/Oqtane.Shared/Shared/InstallConfig.cs @@ -0,0 +1,12 @@ +namespace Oqtane.Shared +{ + public class InstallConfig + { + public string Alias { get; set; } + public string ConnectionString { get; set; } + public string HostUser { get; set; } + public string Password { get; set; } + public string HostEmail { get; set; } + public bool IsMaster { get; set; } + } +} From 52cb3cb980f2869157db384994afc94353f4fe53 Mon Sep 17 00:00:00 2001 From: Aubrey Date: Sun, 29 Mar 2020 09:00:26 -0400 Subject: [PATCH 093/265] Modified null and empty string check. --- Oqtane.Shared/Shared/Utilities.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index f54f80c4..1e2a568c 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -222,10 +222,7 @@ namespace Oqtane.Shared public static bool IsValidEmail(string email) { - if (email != "") - { - return true; - } + if (string.IsNullOrEmpty(email)) return false; return Regex.IsMatch(email, @"^(?("")("".+?(? Date: Sun, 29 Mar 2020 17:37:00 +0200 Subject: [PATCH 094/265] Delete SilentInstall.json --- Oqtane.Server/SilentInstall.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 Oqtane.Server/SilentInstall.json diff --git a/Oqtane.Server/SilentInstall.json b/Oqtane.Server/SilentInstall.json deleted file mode 100644 index cf3e04b4..00000000 --- a/Oqtane.Server/SilentInstall.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Alias" : "", - "DefaultConnection" : "", - "HostUser" : "host", - "Password" : "", - "HostEmail" : "" -} From 07d17da92ad852799af995449ac46a905f6f8988 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Mon, 30 Mar 2020 17:25:14 +0200 Subject: [PATCH 095/265] Better script selection criteria --- Oqtane.Server/Infrastructure/DatabaseManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index bccbf5b8..26419bf4 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -145,8 +145,7 @@ namespace Oqtane.Infrastructure .SqlDatabase(connectionString) .WithVariable("ConnectionString", connectionString) .WithVariable("Alias", alias) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master") && master || s.Contains("Tenant")) - ; + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => master || !s.Contains("Master.")); var dbUpgrade = dbUpgradeConfig.Build(); if (!dbUpgrade.IsUpgradeRequired()) From 35f87d25be70b2c12b111a26e0b8f0200be68cd1 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 30 Mar 2020 20:42:43 -0400 Subject: [PATCH 096/265] Added Favicon support, Progressive Web App support, page title and url support, and private/public user registration options --- Oqtane.Client/Modules/Admin/Pages/Add.razor | 22 ++- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 28 +++- Oqtane.Client/Modules/Admin/Site/Index.razor | 130 +++++++++++++++--- Oqtane.Client/Modules/Admin/Sites/Add.razor | 5 + Oqtane.Client/Modules/ModuleBase.cs | 4 +- Oqtane.Client/Themes/Controls/Menu.razor | 39 +++++- .../Themes/Controls/UserProfile.razor | 5 +- Oqtane.Client/Themes/ThemeControlBase.cs | 4 +- Oqtane.Client/UI/Installer.razor | 7 +- Oqtane.Client/UI/Interop.cs | 29 +++- Oqtane.Client/UI/ThemeBuilder.razor | 61 +++++++- Oqtane.Client/wwwroot/index.html | 3 + Oqtane.Client/wwwroot/js/interop.js | 50 ++++++- Oqtane.Client/wwwroot/service-worker.js | 2 + Oqtane.Server/Controllers/PageController.cs | 2 + Oqtane.Server/Pages/_Host.cshtml | 3 + Oqtane.Server/Repository/SiteRepository.cs | 2 + Oqtane.Server/Scripts/Tenant.00.00.00.sql | 7 + Oqtane.Server/wwwroot/js/interop.js | 50 ++++++- Oqtane.Server/wwwroot/service-worker.js | 2 + Oqtane.Shared/Models/Page.cs | 2 + Oqtane.Shared/Models/Site.cs | 5 + Oqtane.Shared/Shared/Utilities.cs | 7 + 23 files changed, 422 insertions(+), 47 deletions(-) create mode 100644 Oqtane.Client/wwwroot/service-worker.js create mode 100644 Oqtane.Server/wwwroot/service-worker.js diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index fc0644a3..77af0407 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -15,6 +15,14 @@
+ + + +
@@ -74,6 +82,14 @@
+ + + +
@@ -151,12 +167,14 @@ List _themeList; List _pageList; - string _name; + string _name = ""; + string _title = ""; string _path = ""; string _parentid; string _insert = ">>"; List _children; int _childid = -1; + string _url = ""; string _isnavigation = "True"; string _ispersonalizable = "False"; string _mode = "view"; @@ -244,6 +262,7 @@ page = new Page(); page.SiteId = PageState.Page.SiteId; page.Name = _name; + page.Title = _title; if (_path == "") { _path = _name; @@ -289,6 +308,7 @@ break; } page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation)); + page.Url = _url; page.EditMode = (_mode == "edit" ? true : false); page.ThemeType = _themetype; page.LayoutType = (_layouttype == null ? "" : _layouttype); diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 91a57188..7dc91d72 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -15,6 +15,14 @@
+ + + +
@@ -74,6 +82,14 @@ }
+ + + +
@@ -153,7 +169,7 @@
- +
@@ -173,14 +189,16 @@ List _themeList; List _pageList; int _pageId; - string _name; - string _path; + string _name = ""; + string _title = ""; + string _path = ""; string _currentparentid; string _parentid; string _insert = "="; List _children; int _childid = -1; string _isnavigation; + string _url = ""; string _ispersonalizable; string _mode; string _themetype; @@ -213,6 +231,7 @@ if (page != null) { _name = page.Name; + _title = page.Title; _path = page.Path; if (_path.Contains("/")) { @@ -228,6 +247,7 @@ } _currentparentid = _parentid; _isnavigation = page.IsNavigation.ToString(); + _url = page.Url; _ispersonalizable = page.IsPersonalizable.ToString(); _mode = (page.EditMode) ? "edit" : "view"; _themetype = page.ThemeType; @@ -313,6 +333,7 @@ string currentPath = page.Path; page.Name = _name; + page.Title = _title; if (_path == "" && _name.ToLower() != "home") { _path = _name; @@ -361,6 +382,7 @@ } } page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation)); + page.Url = _url; page.EditMode = (_mode == "edit"); page.ThemeType = _themetype; page.LayoutType = _layouttype ?? ""; diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index a6bdcac0..9e36c6ba 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -12,7 +12,7 @@ + + + + + + + + - @if (_pageName != "") + @if (_pageName != string.Empty) { } - @if (_moduleTitle != "") + @if (_moduleTitle != string.Empty) { } - @if (_username != "") + @if (_username != string.Empty) {
- + @@ -20,7 +20,7 @@
- + @@ -28,7 +28,7 @@
- + @@ -36,15 +36,23 @@
- + - +
- + + + +
+
- +
- +
- + + + +
+
- + @@ -119,7 +138,7 @@
- + @@ -127,7 +146,7 @@
- + @@ -135,7 +154,7 @@
- + @@ -143,7 +162,7 @@
- + @@ -151,6 +170,43 @@
+ + +
+ + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+
+
Cancel @@ -173,10 +229,13 @@ List _aliasList; string _urls = ""; int _logofileid = -1; - FileManager _filemanager; + FileManager _logofilemanager; + int _faviconfileid = -1; + FileManager _faviconfilemanager; string _themetype; string _layouttype; string _containertype; + string _allowregistration; string _smtphost = ""; string _smtpport = ""; @@ -184,6 +243,12 @@ string _smtpusername = ""; string _smtppassword = ""; + string _pwaisenabled; + int _pwaappiconfileid = -1; + FileManager _pwaappiconfilemanager; + int _pwasplashiconfileid = -1; + FileManager _pwasplashiconfilemanager; + string _createdby; DateTime _createdon; string _modifiedby; @@ -212,10 +277,15 @@ { _logofileid = site.LogoFileId.Value; } + if (site.FaviconFileId != null) + { + _faviconfileid = site.FaviconFileId.Value; + } _themetype = site.DefaultThemeType; _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); _layouttype = site.DefaultLayoutType; _containertype = site.DefaultContainerType; + _allowregistration = site.AllowRegistration.ToString(); Dictionary settings = await SettingService.GetSiteSettingsAsync(site.SiteId); _smtphost = SettingService.GetSetting(settings, "SMTPHost", ""); @@ -224,6 +294,16 @@ _smtpusername = SettingService.GetSetting(settings, "SMTPUsername", ""); _smtppassword = SettingService.GetSetting(settings, "SMTPPassword", ""); + _pwaisenabled = site.PwaIsEnabled.ToString(); + if (site.PwaAppIconFileId != null) + { + _pwaappiconfileid = site.PwaAppIconFileId.Value; + } + if (site.PwaSplashIconFileId != null) + { + _pwasplashiconfileid = site.PwaSplashIconFileId.Value; + } + _createdby = site.CreatedBy; _createdon = site.CreatedOn; _modifiedby = site.ModifiedBy; @@ -286,16 +366,34 @@ { site.Name = _name; site.LogoFileId = null; - int logofileid = _filemanager.GetFileId(); + int logofileid = _logofilemanager.GetFileId(); if (logofileid != -1) { site.LogoFileId = logofileid; } + int faviconfileid = _faviconfilemanager.GetFileId(); + if (faviconfileid != -1) + { + site.FaviconFileId = faviconfileid; + } site.DefaultThemeType = _themetype; site.DefaultLayoutType = (_layouttype == null ? "" : _layouttype); site.DefaultContainerType = _containertype; + site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration)); site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted)); + site.PwaIsEnabled = (_pwaisenabled == null ? true : Boolean.Parse(_pwaisenabled)); + int pwaappiconfileid = _pwaappiconfilemanager.GetFileId(); + if (pwaappiconfileid != -1) + { + site.PwaAppIconFileId = pwaappiconfileid; + } + int pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId(); + if (pwasplashiconfileid != -1) + { + site.PwaSplashIconFileId = pwasplashiconfileid; + } + site = await SiteService.UpdateSiteAsync(site, PageState.Alias); _urls = _urls.Replace("\n", ","); diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 2afb5ed4..6bfc7cfe 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -248,9 +248,14 @@ else site.TenantId = int.Parse(_tenantid); site.Name = _name; site.LogoFileId = null; + site.FaviconFileId = null; site.DefaultThemeType = _themetype; site.DefaultLayoutType = (_layouttype == null ? "" : _layouttype); site.DefaultContainerType = _containertype; + site.PwaIsEnabled = false; + site.PwaAppIconFileId = null; + site.PwaSplashIconFileId = null; + site.AllowRegistration = false; site.SiteTemplateType = _sitetemplatetype; site = await SiteService.AddSiteAsync(site, aliases[0]); diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 4e0b54a7..1fa56c65 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -87,9 +87,7 @@ namespace Oqtane.Modules public string ContentUrl(int fileid) { - string url = (PageState.Alias.Path == "") ? "/~" : PageState.Alias.Path; - url += Constants.ContentUrl + fileid.ToString(); - return url; + return Utilities.ContentUrl(PageState.Alias.Path, fileid); } // user feedback methods diff --git a/Oqtane.Client/Themes/Controls/Menu.razor b/Oqtane.Client/Themes/Controls/Menu.razor index 07e09d34..fc7c8bed 100644 --- a/Oqtane.Client/Themes/Controls/Menu.razor +++ b/Oqtane.Client/Themes/Controls/Menu.razor @@ -41,7 +41,19 @@ securitylevel = int.MaxValue; menu += "
  • "; - menu += ""; + if (string.IsNullOrEmpty(p.Url)) + { + menu += ""; + } + else + { + string target = ""; + if (p.Url.StartsWith("http")) + { + target = " target=\"_new\""; + } + menu += ""; + } if (p.HasChildren) { menu += ""; @@ -69,6 +81,9 @@ private void CreateHorizontalMenu() { + string url = ""; + string target = ""; + menu = ""; menu += "
    "; menu += "
      "; @@ -76,18 +91,32 @@ { if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, p.Permissions) && p.ParentId == PageState.Page.ParentId && p.Level == PageState.Page.Level) { + if (string.IsNullOrEmpty(p.Url)) + { + url = NavigateUrl(p.Path); + target = ""; + } + else + { + url = p.Url; + if (p.Url.StartsWith("http")) + { + target = " target=\"_new\""; + } + } + if (p.PageId == PageState.Page.PageId) { menu += "
    • " + - "" + - ((p.Icon != "") ? " " : "") + + "" + + ((p.Icon != "") ? " " : "") + p.Name + " (current)
    • "; } else { menu += "
    • " + - "" + - ((p.Icon != "") ? " " : "") + + "" + + ((p.Icon != "") ? " " : "") + p.Name + "
    • "; } } diff --git a/Oqtane.Client/Themes/Controls/UserProfile.razor b/Oqtane.Client/Themes/Controls/UserProfile.razor index f43f3747..1f101caf 100644 --- a/Oqtane.Client/Themes/Controls/UserProfile.razor +++ b/Oqtane.Client/Themes/Controls/UserProfile.razor @@ -10,7 +10,10 @@ - + @if (PageState.Site.AllowRegistration) + { + + } diff --git a/Oqtane.Client/Themes/ThemeControlBase.cs b/Oqtane.Client/Themes/ThemeControlBase.cs index 13cc1923..91b2a7cf 100644 --- a/Oqtane.Client/Themes/ThemeControlBase.cs +++ b/Oqtane.Client/Themes/ThemeControlBase.cs @@ -41,9 +41,7 @@ namespace Oqtane.Themes public string ContentUrl(int fileid) { - string url = (PageState.Alias.Path == "") ? "/~" : PageState.Alias.Path; - url += Constants.ContentUrl + fileid.ToString(); - return url; + return Utilities.ContentUrl(PageState.Alias.Path, fileid); } } } diff --git a/Oqtane.Client/UI/Installer.razor b/Oqtane.Client/UI/Installer.razor index f2985b1c..5c4d0312 100644 --- a/Oqtane.Client/UI/Installer.razor +++ b/Oqtane.Client/UI/Installer.razor @@ -185,16 +185,21 @@ }; Installation installation = await InstallationService.Install(config); - //TODO: Should be moved to Database manager + //TODO: Should be moved to Database manager if (installation.Success) { Site site = new Site(); site.TenantId = -1; // will be populated on server site.Name = "Default Site"; site.LogoFileId = null; + site.FaviconFileId = null; site.DefaultThemeType = Constants.DefaultTheme; site.DefaultLayoutType = Constants.DefaultLayout; site.DefaultContainerType = Constants.DefaultContainer; + site.PwaIsEnabled = false; + site.PwaAppIconFileId = null; + site.PwaSplashIconFileId = null; + site.AllowRegistration = false; site = await SiteService.AddSiteAsync(site, null); User user = new User(); diff --git a/Oqtane.Client/UI/Interop.cs b/Oqtane.Client/UI/Interop.cs index 5b2c6b7c..b57d1444 100644 --- a/Oqtane.Client/UI/Interop.cs +++ b/Oqtane.Client/UI/Interop.cs @@ -58,12 +58,12 @@ namespace Oqtane.UI } } - public Task UpdateMeta(string id, string attribute, string name, string content) + public Task IncludeMeta(string id, string attribute, string name, string content) { try { _jsRuntime.InvokeAsync( - "interop.updateMeta", + "interop.includeMeta", id, attribute, name, content); return Task.CompletedTask; } @@ -73,13 +73,28 @@ namespace Oqtane.UI } } - public Task UpdateLink(string id, string rel, string type, string url) + public Task IncludeLink(string id, string rel, string url, string type) { try { _jsRuntime.InvokeAsync( - "interop.updateLink", - id, rel, type, url); + "interop.includeLink", + id, rel, url, type); + return Task.CompletedTask; + } + catch + { + return Task.CompletedTask; + } + } + + public Task IncludeScript(string id, string src, string content, string location) + { + try + { + _jsRuntime.InvokeAsync( + "interop.includeScript", + id, src, content, location); return Task.CompletedTask; } catch @@ -93,8 +108,8 @@ namespace Oqtane.UI try { _jsRuntime.InvokeAsync( - "interop.updateLink", - id, "stylesheet", "text/css", url); + "interop.includeLink", + id, "stylesheet", url, "text/css"); return Task.CompletedTask; } catch diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index 2be44081..b16368b2 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -1,5 +1,6 @@ @namespace Oqtane.UI @inject IJSRuntime JsRuntime +@inject NavigationManager NavigationManager @DynamicComponent @@ -8,8 +9,26 @@ RenderFragment DynamicComponent { get; set; } - protected override void OnParametersSet() + protected override async Task OnParametersSetAsync() { + var interop = new Interop(JsRuntime); + if (!string.IsNullOrEmpty(PageState.Page.Title)) + { + await interop.UpdateTitle(PageState.Page.Title); + } + else + { + await interop.UpdateTitle(PageState.Site.Name + " - " + PageState.Page.Name); + } + if (PageState.Site.FaviconFileId != null) + { + await interop.IncludeLink("fav-icon", "shortcut icon", Utilities.ContentUrl(PageState.Alias.Path, PageState.Site.FaviconFileId.Value), "image/x-icon"); + } + if (PageState.Site.PwaIsEnabled) + { + await InitializePwa(interop); + } + DynamicComponent = builder => { Type themeType = Type.GetType(PageState.Page.ThemeType); @@ -27,4 +46,44 @@ } }; } + + private async Task InitializePwa(Interop interop) + { + // dynamically create manifest.json and add to page + string manifest = "setTimeout(() => { " + + "var manifest = { " + + "\"name\": \"" + PageState.Site.Name + "\", " + + "\"short_name\": \"" + PageState.Site.Name + "\", " + + "\"start_url\": \"/\", " + + "\"display\": \"standalone\", " + + "\"background_color\": \"#fff\", " + + "\"description\": \"" + PageState.Site.Name + "\", " + + "\"icons\": [{ " + + "\"src\": \"" + Utilities.ContentUrl(PageState.Alias.Path, PageState.Site.PwaAppIconFileId.Value) + "\", " + + "\"sizes\": \"192x192\", " + + "\"type\": \"image/png\" " + + "}, { " + + "\"src\": \"" + Utilities.ContentUrl(PageState.Alias.Path, PageState.Site.PwaSplashIconFileId.Value) + "\", " + + "\"sizes\": \"512x512\", " + + "\"type\": \"image/png\" " + + "}] " + + "} " + + "const serialized = JSON.stringify(manifest); " + + "const blob = new Blob([serialized], {type: 'application/javascript'}); " + + "const url = URL.createObjectURL(blob); " + + "document.getElementById('pwa-manifest').setAttribute('href', url); " + + "} " + + ", 1000);"; + await interop.IncludeScript("pwa-manifestscript", "", manifest, "body"); + + // service worker must be in root of site + string serviceworker = "if ('serviceWorker' in navigator) { " + + "navigator.serviceWorker.register('/service-worker.js').then(function(registration) { " + + "console.log('ServiceWorker Registration Successful'); " + + "}).catch (function(err) { " + + "console.log('ServiceWorker Registration Failed ', err); " + + "}); " + + "}"; + await interop.IncludeScript("pwa-serviceworker", "", serviceworker, "body"); + } } diff --git a/Oqtane.Client/wwwroot/index.html b/Oqtane.Client/wwwroot/index.html index 16cb5285..4244d8d4 100644 --- a/Oqtane.Client/wwwroot/index.html +++ b/Oqtane.Client/wwwroot/index.html @@ -5,6 +5,9 @@ Oqtane + + + diff --git a/Oqtane.Client/wwwroot/js/interop.js b/Oqtane.Client/wwwroot/js/interop.js index c9860f68..ec289bbe 100644 --- a/Oqtane.Client/wwwroot/js/interop.js +++ b/Oqtane.Client/wwwroot/js/interop.js @@ -25,7 +25,7 @@ window.interop = { document.title = title; } }, - updateMeta: function (id, attribute, name, content) { + includeMeta: function (id, attribute, name, content) { var meta; if (id !== "") { meta = document.getElementById(id); @@ -48,7 +48,7 @@ window.interop = { } } }, - updateLink: function (id, rel, type, url) { + includeLink: function (id, rel, url, type) { var link; if (id !== "") { link = document.getElementById(id); @@ -62,14 +62,58 @@ window.interop = { link.id = id; } link.rel = rel; - link.type = type; link.href = url; + if (type !== "") { + link.type = type; + } document.head.appendChild(link); } else { + if (link.rel !== rel) { + link.setAttribute('rel', rel); + } if (link.href !== url) { link.setAttribute('href', url); } + if (type !== "" && link.type !== type) { + link.setAttribute('type', type); + } + } + }, + includeScript: function (id, src, content, location) { + var script; + if (id !== "") { + script = document.getElementById(id); + } + if (script === null) { + script = document.createElement("script"); + if (id !== "") { + script.id = id; + } + if (src !== "") { + script.src = src; + } + else { + script.innerHTML = content; + } + if (location === 'head') { + document.head.appendChild(script); + } + if (location === 'body') { + document.body.appendChild(script); + } + } + else { + if (src !== "") { + if (script.src !== src) { + script.src = src; + } + } + else { + if (script.innerHTML !== content) { + script.innerHTML = content; + } + } } }, getElementByName: function (name) { diff --git a/Oqtane.Client/wwwroot/service-worker.js b/Oqtane.Client/wwwroot/service-worker.js new file mode 100644 index 00000000..0c55c1b1 --- /dev/null +++ b/Oqtane.Client/wwwroot/service-worker.js @@ -0,0 +1,2 @@ +// always fetch from the network and do not enable offline support +self.addEventListener('fetch', () => { }); \ No newline at end of file diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 3a65407d..607ceaef 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -146,10 +146,12 @@ namespace Oqtane.Controllers page = new Page(); page.SiteId = parent.SiteId; page.Name = parent.Name; + page.Title = parent.Title; page.Path = parent.Path; page.ParentId = parent.PageId; page.Order = 0; page.IsNavigation = false; + page.Url = ""; page.EditMode = false; page.ThemeType = parent.ThemeType; page.LayoutType = parent.LayoutType; diff --git a/Oqtane.Server/Pages/_Host.cshtml b/Oqtane.Server/Pages/_Host.cshtml index c5a90e79..ac372241 100644 --- a/Oqtane.Server/Pages/_Host.cshtml +++ b/Oqtane.Server/Pages/_Host.cshtml @@ -9,6 +9,9 @@ Oqtane + + + diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index dc10fb3b..dc40978b 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -634,8 +634,10 @@ namespace Oqtane.Repository SiteId = site.SiteId, ParentId = parentid, Name = pagetemplate.Name, + Title = "", Path = pagetemplate.Path, Order = 1, + Url = "", IsNavigation = pagetemplate.IsNavigation, EditMode = pagetemplate.EditMode, ThemeType = "", diff --git a/Oqtane.Server/Scripts/Tenant.00.00.00.sql b/Oqtane.Server/Scripts/Tenant.00.00.00.sql index 5157b401..1d0e540f 100644 --- a/Oqtane.Server/Scripts/Tenant.00.00.00.sql +++ b/Oqtane.Server/Scripts/Tenant.00.00.00.sql @@ -9,9 +9,14 @@ CREATE TABLE [dbo].[Site]( [TenantId] [int] NOT NULL, [Name] [nvarchar](200) NOT NULL, [LogoFileId] [int] NULL, + [FaviconFileId] [int] NULL, [DefaultThemeType] [nvarchar](200) NOT NULL, [DefaultLayoutType] [nvarchar](200) NOT NULL, [DefaultContainerType] [nvarchar](200) NOT NULL, + [PwaIsEnabled] [bit] NOT NULL, + [PwaAppIconFileId] [int] NULL, + [PwaSplashIconFileId] [int] NULL, + [AllowRegistration] [bit] NOT NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, @@ -31,11 +36,13 @@ CREATE TABLE [dbo].[Page]( [SiteId] [int] NOT NULL, [Path] [nvarchar](50) NOT NULL, [Name] [nvarchar](50) NOT NULL, + [Title] [nvarchar](200) NULL, [ThemeType] [nvarchar](200) NULL, [Icon] [nvarchar](50) NOT NULL, [ParentId] [int] NULL, [Order] [int] NOT NULL, [IsNavigation] [bit] NOT NULL, + [Url] [nvarchar](500) NULL, [LayoutType] [nvarchar](200) NOT NULL, [EditMode] [bit] NOT NULL, [UserId] [int] NULL, diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index c9860f68..ec289bbe 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -25,7 +25,7 @@ window.interop = { document.title = title; } }, - updateMeta: function (id, attribute, name, content) { + includeMeta: function (id, attribute, name, content) { var meta; if (id !== "") { meta = document.getElementById(id); @@ -48,7 +48,7 @@ window.interop = { } } }, - updateLink: function (id, rel, type, url) { + includeLink: function (id, rel, url, type) { var link; if (id !== "") { link = document.getElementById(id); @@ -62,14 +62,58 @@ window.interop = { link.id = id; } link.rel = rel; - link.type = type; link.href = url; + if (type !== "") { + link.type = type; + } document.head.appendChild(link); } else { + if (link.rel !== rel) { + link.setAttribute('rel', rel); + } if (link.href !== url) { link.setAttribute('href', url); } + if (type !== "" && link.type !== type) { + link.setAttribute('type', type); + } + } + }, + includeScript: function (id, src, content, location) { + var script; + if (id !== "") { + script = document.getElementById(id); + } + if (script === null) { + script = document.createElement("script"); + if (id !== "") { + script.id = id; + } + if (src !== "") { + script.src = src; + } + else { + script.innerHTML = content; + } + if (location === 'head') { + document.head.appendChild(script); + } + if (location === 'body') { + document.body.appendChild(script); + } + } + else { + if (src !== "") { + if (script.src !== src) { + script.src = src; + } + } + else { + if (script.innerHTML !== content) { + script.innerHTML = content; + } + } } }, getElementByName: function (name) { diff --git a/Oqtane.Server/wwwroot/service-worker.js b/Oqtane.Server/wwwroot/service-worker.js new file mode 100644 index 00000000..0c55c1b1 --- /dev/null +++ b/Oqtane.Server/wwwroot/service-worker.js @@ -0,0 +1,2 @@ +// always fetch from the network and do not enable offline support +self.addEventListener('fetch', () => { }); \ No newline at end of file diff --git a/Oqtane.Shared/Models/Page.cs b/Oqtane.Shared/Models/Page.cs index d9c40e36..f1268875 100644 --- a/Oqtane.Shared/Models/Page.cs +++ b/Oqtane.Shared/Models/Page.cs @@ -9,8 +9,10 @@ namespace Oqtane.Models public int SiteId { get; set; } public int? ParentId { get; set; } public string Name { get; set; } + public string Title { get; set; } public string Path { get; set; } public int Order { get; set; } + public string Url { get; set; } public string ThemeType { get; set; } public string LayoutType { get; set; } public string Icon { get; set; } diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs index 700209b2..4b6b7f86 100644 --- a/Oqtane.Shared/Models/Site.cs +++ b/Oqtane.Shared/Models/Site.cs @@ -9,9 +9,14 @@ namespace Oqtane.Models public int TenantId { get; set; } public string Name { get; set; } public int? LogoFileId { get; set; } + public int? FaviconFileId { get; set; } public string DefaultThemeType { get; set; } public string DefaultLayoutType { get; set; } public string DefaultContainerType { get; set; } + public bool PwaIsEnabled { get; set; } + public int? PwaAppIconFileId { get; set; } + public int? PwaSplashIconFileId { get; set; } + public bool AllowRegistration { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 1e2a568c..46c02fa7 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -56,6 +56,13 @@ namespace Oqtane.Shared return url; } + public static string ContentUrl(string alias, int fileid) + { + string url = (alias == "") ? "/~" : alias; + url += Constants.ContentUrl + fileid.ToString(); + return url; + } + public static string GetTypeName(string fullyqualifiedtypename) { if (fullyqualifiedtypename.Contains(",")) From 66ad089088ff4fad2ad601efe6377802514a867a Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Tue, 31 Mar 2020 17:21:05 +0300 Subject: [PATCH 097/265] Refactoring (#314) * Refactoring * Refactoring * Check for a valid email. * Fixed missing character. * Moved logic to the Utilities class. * Rename template .sql file * Modified null and empty string check. * Check for a valid email. * Fixed missing character. * Moved logic to the Utilities class. * Added Favicon support, Progressive Web App support, page title and url support, and private/public user registration options * Refactoring * Refactoring * Check for a valid email. * Moved logic to the Utilities class. Co-authored-by: Aubrey Co-authored-by: MIchael Atwood Co-authored-by: Shaun Walker --- Oqtane.Client/App.razor | 1 + .../Modules/Admin/Dashboard/Index.razor | 8 +- Oqtane.Client/Modules/Admin/Error/Index.razor | 3 +- Oqtane.Client/Modules/Admin/Files/Add.razor | 10 +- Oqtane.Client/Modules/Admin/Files/Edit.razor | 36 ++--- Oqtane.Client/Modules/Admin/Files/Index.razor | 9 +- Oqtane.Client/Modules/Admin/Jobs/Add.razor | 29 ++-- Oqtane.Client/Modules/Admin/Jobs/Edit.razor | 35 ++--- Oqtane.Client/Modules/Admin/Jobs/Index.razor | 10 +- Oqtane.Client/Modules/Admin/Jobs/Log.razor | 11 +- Oqtane.Client/Modules/Admin/Login/Index.razor | 32 +++-- Oqtane.Client/Modules/Admin/Logs/Detail.razor | 50 +++---- Oqtane.Client/Modules/Admin/Logs/Index.razor | 16 +-- .../Modules/Admin/ModuleCreator/Index.razor | 10 +- .../Modules/Admin/ModuleDefinitions/Add.razor | 7 +- .../Admin/ModuleDefinitions/Edit.razor | 24 ++-- .../Admin/ModuleDefinitions/Index.razor | 11 +- .../Modules/Admin/Modules/Export.razor | 7 +- .../Modules/Admin/Modules/Import.razor | 9 +- .../Modules/Admin/Modules/Settings.razor | 34 ++--- Oqtane.Client/Modules/Admin/Pages/Add.razor | 71 +++++----- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 87 ++++++------ Oqtane.Client/Modules/Admin/Pages/Index.razor | 3 +- .../Modules/Admin/Profiles/Edit.razor | 29 ++-- .../Modules/Admin/Profiles/Index.razor | 4 +- .../Modules/Admin/RecycleBin/Index.razor | 10 +- .../Modules/Admin/Register/Index.razor | 28 ++-- Oqtane.Client/Modules/Admin/Reset/Index.razor | 16 +-- Oqtane.Client/Modules/Admin/Roles/Add.razor | 12 +- Oqtane.Client/Modules/Admin/Roles/Edit.razor | 14 +- Oqtane.Client/Modules/Admin/Roles/Index.razor | 4 +- Oqtane.Client/Modules/Admin/Site/Index.razor | 128 +++++++++--------- Oqtane.Client/Modules/Admin/Sites/Add.razor | 76 ++++++----- Oqtane.Client/Modules/Admin/Sites/Edit.razor | 75 +++++----- Oqtane.Client/Modules/Admin/Sites/Index.razor | 14 +- Oqtane.Client/Modules/Admin/Sql/Index.razor | 25 ++-- Oqtane.Client/Modules/Admin/Tenants/Add.razor | 26 ++-- .../Modules/Admin/Tenants/Edit.razor | 15 +- .../Modules/Admin/Tenants/Index.razor | 4 +- Oqtane.Client/Modules/Admin/Themes/Add.razor | 7 +- .../Modules/Admin/Themes/Index.razor | 11 +- .../Modules/Admin/Upgrade/Index.razor | 8 +- .../Modules/Admin/UserProfile/Add.razor | 15 +- .../Modules/Admin/UserProfile/Index.razor | 47 ++++--- .../Modules/Admin/UserProfile/View.razor | 27 ++-- Oqtane.Client/Modules/Admin/Users/Add.razor | 24 ++-- Oqtane.Client/Modules/Admin/Users/Edit.razor | 54 ++++---- Oqtane.Client/Modules/Admin/Users/Index.razor | 6 +- Oqtane.Client/Modules/Admin/Users/Roles.razor | 21 +-- .../Modules/Controls/ActionDialog.razor | 10 +- .../Modules/Controls/ActionLink.razor | 28 ++-- .../Modules/Controls/AuditInfo.razor | 24 +++- .../Modules/Controls/FileManager.razor | 73 +++++----- Oqtane.Client/Modules/Controls/Label.razor | 10 +- .../Modules/Controls/ModuleMessage.razor | 9 +- Oqtane.Client/Modules/Controls/Pager.razor | 20 ++- .../Modules/Controls/PermissionGrid.razor | 43 +++--- .../Modules/Controls/RichTextEditor.razor | 22 +-- .../Modules/Controls/TabControl.razor | 13 +- .../Modules/Controls/TriStateCheckBox.razor | 12 +- Oqtane.Client/Modules/Counter/Index.razor | 4 +- Oqtane.Client/Modules/HtmlText/Edit.razor | 47 +++---- Oqtane.Client/Modules/HtmlText/Index.razor | 6 +- Oqtane.Client/Modules/Weather/Index.razor | 2 +- .../Themes/BlazorTheme/Default.razor | 2 +- .../Themes/Controls/Breadcrumbs.razor | 13 +- .../Themes/Controls/ControlPanel.razor | 60 +++++--- Oqtane.Client/Themes/Controls/Login.razor | 3 +- Oqtane.Client/Themes/Controls/Logo.razor | 2 +- Oqtane.Client/Themes/Controls/Menu.razor | 35 +++-- .../Themes/Controls/ModuleActions.razor | 9 +- .../Themes/Controls/ModuleTitle.razor | 2 +- .../Themes/OqtaneTheme/Default.razor | 2 +- .../Themes/OqtaneTheme/MultiPane.razor | 2 +- .../Themes/OqtaneTheme/SinglePane.razor | 2 +- Oqtane.Client/UI/ContainerBuilder.razor | 4 +- Oqtane.Client/UI/Installer.razor | 16 +-- Oqtane.Client/UI/ModuleInstance.razor | 8 +- Oqtane.Client/UI/Pane.razor | 22 +-- Oqtane.Client/UI/PaneLayout.razor | 2 +- Oqtane.Client/UI/SiteRouter.razor | 65 +++++---- Oqtane.Client/UI/ThemeBuilder.razor | 2 +- 82 files changed, 958 insertions(+), 829 deletions(-) diff --git a/Oqtane.Client/App.razor b/Oqtane.Client/App.razor index ffc2a160..3d13adff 100644 --- a/Oqtane.Client/App.razor +++ b/Oqtane.Client/App.razor @@ -19,6 +19,7 @@ @code { private bool _initialized; private bool _installed; + private PageState PageState { get; set; } protected override async Task OnParametersSetAsync() diff --git a/Oqtane.Client/Modules/Admin/Dashboard/Index.razor b/Oqtane.Client/Modules/Admin/Dashboard/Index.razor index c1776600..38202300 100644 --- a/Oqtane.Client/Modules/Admin/Dashboard/Index.razor +++ b/Oqtane.Client/Modules/Admin/Dashboard/Index.razor @@ -19,13 +19,13 @@
    @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - - List _pages; + var List _pages; + + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override void OnInitialized() { - Page admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin"); + var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin"); _pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId).ToList(); } } diff --git a/Oqtane.Client/Modules/Admin/Error/Index.razor b/Oqtane.Client/Modules/Admin/Error/Index.razor index 8ecd898c..9e0df574 100644 --- a/Oqtane.Client/Modules/Admin/Error/Index.razor +++ b/Oqtane.Client/Modules/Admin/Error/Index.razor @@ -3,7 +3,7 @@ @inject IModuleService ModuleService @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Anonymous; } } + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous; protected override async Task OnInitializedAsync() { @@ -13,6 +13,7 @@ string message = "A Problem Was Encountered Loading Module " + module.ModuleDefinitionName; AddModuleMessage(message, MessageType.Error); } + await logger.LogCritical("Error Loading Module {Module}", module); } } diff --git a/Oqtane.Client/Modules/Admin/Files/Add.razor b/Oqtane.Client/Modules/Admin/Files/Add.razor index 65935a72..182f08f7 100644 --- a/Oqtane.Client/Modules/Admin/Files/Add.razor +++ b/Oqtane.Client/Modules/Admin/Files/Add.razor @@ -70,11 +70,11 @@ } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } + private string url = string.Empty; + private List _folders; + private int _folderId = -1; - string url = ""; - List _folders; - int _folderId = -1; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override async Task OnInitializedAsync() { @@ -90,7 +90,7 @@ { try { - if (url != "" && _folderId != -1) + if (url != string.Empty && _folderId != -1) { await FileService.UploadFileAsync(url, _folderId); await logger.LogInformation("File Downloaded Successfully From Url {Url}", url); diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index d7bc822c..167c0b3c 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -56,24 +56,25 @@ } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - public override string Title { get { return "Folder Management"; } } - - List _folders; - int _folderId = -1; - string _name; - int _parentId = -1; - bool _isSystem; - string _permissions = ""; - string _createdBy; - DateTime _createdOn; - string _modifiedBy; - DateTime _modifiedOn; + private List _folders; + private int _folderId = -1; + private string _name; + private int _parentId = -1; + private bool _isSystem; + private string _permissions = string.Empty; + private string _createdBy; + private DateTime _createdOn; + private string _modifiedBy; + private DateTime _modifiedOn; #pragma warning disable 649 - PermissionGrid _permissionGrid; + private PermissionGrid _permissionGrid; #pragma warning restore 649 + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; + + public override string Title => "Folder Management"; + protected override async Task OnInitializedAsync() { try @@ -99,7 +100,7 @@ else { _parentId = _folders[0].FolderId; - _permissions = ""; + _permissions = string.Empty; } } catch (Exception ex) @@ -113,7 +114,7 @@ { try { - if (_name != "" && _parentId != -1) + if (_name != string.Empty && _parentId != -1) { Folder folder; if (_folderId != -1) @@ -126,6 +127,7 @@ } folder.SiteId = PageState.Site.SiteId; + if (_parentId == -1) { folder.ParentId = null; @@ -134,6 +136,7 @@ { folder.ParentId = _parentId; } + folder.Name = _name; folder.IsSystem = _isSystem; folder.Permissions = _permissionGrid.GetPermissions(); @@ -146,6 +149,7 @@ { folder = await FolderService.AddFolderAsync(folder); } + await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); await logger.LogInformation("Folder Saved {Folder}", folder); NavigationManager.NavigateTo(NavigateUrl()); diff --git a/Oqtane.Client/Modules/Admin/Files/Index.razor b/Oqtane.Client/Modules/Admin/Files/Index.razor index efe9c33a..d92e0994 100644 --- a/Oqtane.Client/Modules/Admin/Files/Index.razor +++ b/Oqtane.Client/Modules/Admin/Files/Index.razor @@ -49,17 +49,18 @@ } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } + private List _folders; + private int _folderId = -1; + private List _files; - List _folders; - int _folderId = -1; - List _files; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override async Task OnParametersSetAsync() { try { _folders = await FolderService.GetFoldersAsync(PageState.Site.SiteId); + if (_folderId == -1 && _folders.Count > 0) { _folderId = _folders[0].FolderId; diff --git a/Oqtane.Client/Modules/Admin/Jobs/Add.razor b/Oqtane.Client/Modules/Admin/Jobs/Add.razor index a333d0d5..4775f8a0 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Add.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Add.razor @@ -74,28 +74,29 @@ Cancel @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + private string _name = string.Empty; + private string _jobType = string.Empty; + private string _isEnabled = "True"; + private string _interval = string.Empty; + private string _frequency = string.Empty; + private string _startDate = string.Empty; + private string _endDate = string.Empty; + private string _retentionHistory = "10"; - string _name = ""; - string _jobType = ""; - string _isEnabled = "True"; - string _interval = ""; - string _frequency = ""; - string _startDate = ""; - string _endDate = ""; - string _retentionHistory = "10"; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; private async Task SaveJob() { - if (_name != "" && !string.IsNullOrEmpty(_jobType) && _frequency != "" && _interval != "" && _retentionHistory != "") + if (_name != string.Empty && !string.IsNullOrEmpty(_jobType) && _frequency != string.Empty && _interval != string.Empty && _retentionHistory != string.Empty) { - Job job = new Job(); + var job = new Job(); job.Name = _name; job.JobType = _jobType; job.IsEnabled = Boolean.Parse(_isEnabled); job.Frequency = _frequency; job.Interval = int.Parse(_interval); - if (_startDate == "") + + if (_startDate == string.Empty) { job.StartDate = null; } @@ -103,7 +104,8 @@ { job.StartDate = DateTime.Parse(_startDate); } - if (_endDate == "") + + if (_endDate == string.Empty) { job.EndDate = null; } @@ -111,6 +113,7 @@ { job.EndDate = DateTime.Parse(_endDate); } + job.RetentionHistory = int.Parse(_retentionHistory); job.IsStarted = false; job.IsExecuting = false; diff --git a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor index 205dda81..5339a226 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor @@ -74,17 +74,17 @@ Cancel @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + private int _jobId; + private string _name = string.Empty; + private string _jobType = string.Empty; + private string _isEnabled = "True"; + private string _interval = string.Empty; + private string _frequency = string.Empty; + private string _startDate = string.Empty; + private string _endDate = string.Empty; + private string _retentionHistory = string.Empty; - int _jobId; - string _name = ""; - string _jobType = ""; - string _isEnabled = "True"; - string _interval = ""; - string _frequency = ""; - string _startDate = ""; - string _endDate = ""; - string _retentionHistory = ""; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnInitializedAsync() { @@ -99,8 +99,8 @@ _isEnabled = job.IsEnabled.ToString(); _interval = job.Interval.ToString(); _frequency = job.Frequency; - _startDate = (job.StartDate != null) ? job.StartDate.ToString() : ""; - _endDate = (job.EndDate != null) ? job.EndDate.ToString() : ""; + _startDate = (job.StartDate != null) ? job.StartDate.ToString() : string.Empty; + _endDate = (job.EndDate != null) ? job.EndDate.ToString() : string.Empty; _retentionHistory = job.RetentionHistory.ToString(); } } @@ -113,15 +113,16 @@ private async Task SaveJob() { - if (_name != "" && !string.IsNullOrEmpty(_jobType) && _frequency != "" && _interval != "" && _retentionHistory != "") + if (_name != string.Empty && !string.IsNullOrEmpty(_jobType) && _frequency != string.Empty && _interval != string.Empty && _retentionHistory != string.Empty) { - Job job = await JobService.GetJobAsync(_jobId); + var job = await JobService.GetJobAsync(_jobId); job.Name = _name; job.JobType = _jobType; job.IsEnabled = Boolean.Parse(_isEnabled); job.Frequency = _frequency; job.Interval = int.Parse(_interval); - if (_startDate == "") + + if (_startDate == string.Empty) { job.StartDate = null; } @@ -129,7 +130,8 @@ { job.StartDate = DateTime.Parse(_startDate); } - if (_endDate == "") + + if (_endDate == string.Empty) { job.EndDate = null; } @@ -137,6 +139,7 @@ { job.EndDate = DateTime.Parse(_endDate); } + job.RetentionHistory = int.Parse(_retentionHistory); try diff --git a/Oqtane.Client/Modules/Admin/Jobs/Index.razor b/Oqtane.Client/Modules/Admin/Jobs/Index.razor index 602708c9..f15c50df 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Index.razor @@ -47,10 +47,10 @@ else } @code { + private List _jobs; + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - List _jobs; - protected override async Task OnParametersSetAsync() { _jobs = await JobService.GetJobsAsync(); @@ -58,7 +58,7 @@ else private string DisplayStatus(bool isEnabled, bool isExecuting) { - string status = ""; + var status = string.Empty; if (!isEnabled) { status = "Disabled"; @@ -81,7 +81,7 @@ else private string DisplayFrequency(int interval, string frequency) { - string result = "Every " + interval.ToString() + " "; + var result = "Every " + interval.ToString() + " "; switch (frequency) { case "m": @@ -97,10 +97,12 @@ else result += "Month"; break; } + if (interval > 1) { result += "s"; } + return result; } diff --git a/Oqtane.Client/Modules/Admin/Jobs/Log.razor b/Oqtane.Client/Modules/Admin/Jobs/Log.razor index af0d5d5e..9efc1486 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Log.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Log.razor @@ -28,23 +28,25 @@ else } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } - - List _jobLogs; + private List _jobLogs; + + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnParametersSetAsync() { _jobLogs = await JobLogService.GetJobLogsAsync(); + if (PageState.QueryString.ContainsKey("id")) { _jobLogs = _jobLogs.Where(item => item.JobId == Int32.Parse(PageState.QueryString["id"])).ToList(); } + _jobLogs = _jobLogs.OrderByDescending(item => item.JobLogId).ToList(); } private string DisplayStatus(bool isExecuting, bool? succeeded) { - string status = ""; + var status = string.Empty; if (isExecuting) { status = "Executing"; @@ -60,6 +62,7 @@ else status = "Failed"; } } + return status; } } diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index 41f36dc3..098ebcb4 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -5,7 +5,7 @@ @inject IUserService UserService @inject IServiceProvider ServiceProvider -@if (_message != "") +@if (_message != string.Empty) { } @@ -41,14 +41,14 @@ @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Anonymous; } } + private string _returnUrl = string.Empty; + private string _message = string.Empty; + private MessageType _type = MessageType.Info; + private string _username = string.Empty; + private string _password = string.Empty; + private bool _remember = false; - string _returnUrl = ""; - string _message = ""; - MessageType _type = MessageType.Info; - string _username = ""; - string _password = ""; - bool _remember = false; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous; protected override async Task OnInitializedAsync() { @@ -56,13 +56,15 @@ { _returnUrl = PageState.QueryString["returnurl"]; } + if (PageState.QueryString.ContainsKey("name")) { _username = PageState.QueryString["name"]; } + if (PageState.QueryString.ContainsKey("token")) { - User user = new User(); + var user = new User(); user.SiteId = PageState.Site.SiteId; user.Username = _username; user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]); @@ -84,11 +86,12 @@ if (PageState.Runtime == Runtime.Server) { // server-side Blazor - User user = new User(); + var user = new User(); user.SiteId = PageState.Site.SiteId; user.Username = _username; user.Password = _password; user = await UserService.LoginUserAsync(user, false, false); + if (user.IsAuthenticated) { await logger.LogInformation("Login Successful For Username {Username}", _username); @@ -107,7 +110,7 @@ else { // client-side Blazor - User user = new User(); + var user = new User(); user.SiteId = PageState.Site.SiteId; user.Username = _username; user.Password = _password; @@ -134,9 +137,9 @@ private async Task Forgot() { - if (_username != "") + if (_username != string.Empty) { - User user = await UserService.GetUserAsync(_username, PageState.Site.SiteId); + var user = await UserService.GetUserAsync(_username, PageState.Site.SiteId); if (user != null) { await UserService.ForgotPasswordAsync(user); @@ -152,6 +155,7 @@ { _message = "Please Enter The Username Related To Your Account And Then Select The Forgot Password Option Again"; } + StateHasChanged(); } - } +} diff --git a/Oqtane.Client/Modules/Admin/Logs/Detail.razor b/Oqtane.Client/Modules/Admin/Logs/Detail.razor index 0fd6333a..2f9c054d 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Detail.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Detail.razor @@ -48,7 +48,7 @@
  • @@ -59,7 +59,7 @@
    @@ -70,7 +70,7 @@
    @@ -136,30 +136,30 @@ Cancel @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } + private int _logId; + private string _logDate = string.Empty; + private string _level = string.Empty; + private string _feature = string.Empty; + private string _function = string.Empty; + private string _category = string.Empty; + private string _pageName = string.Empty; + private string _moduleTitle = string.Empty; + private string _username = string.Empty; + private string _url = string.Empty; + private string _template = string.Empty; + private string _message = string.Empty; + private string _exception = string.Empty; + private string _properties = string.Empty; + private string _server = string.Empty; - int _logId; - string _logDate = ""; - string _level = ""; - string _feature = ""; - string _function = ""; - string _category = ""; - string _pageName = ""; - string _moduleTitle = ""; - string _username = ""; - string _url = ""; - string _template = ""; - string _message = ""; - string _exception = ""; - string _properties = ""; - string _server = ""; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override async Task OnInitializedAsync() { try { _logId = Int32.Parse(PageState.QueryString["id"]); - Log log = await LogService.GetLogAsync(_logId); + var log = await LogService.GetLogAsync(_logId); if (log != null) { _logDate = log.LogDate.ToString(CultureInfo.CurrentCulture); @@ -167,30 +167,34 @@ _feature = log.Feature; _function = log.Function; _category = log.Category; + if (log.PageId != null) { - Page page = await PageService.GetPageAsync(log.PageId.Value); + var page = await PageService.GetPageAsync(log.PageId.Value); if (page != null) { _pageName = page.Name; } } + if (log.PageId != null && log.ModuleId != null) { - PageModule pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value); + var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value); if (pagemodule != null) { _moduleTitle = pagemodule.Title; } } + if (log.UserId != null) { - User user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId); + var user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId); if (user != null) { _username = user.Username; } } + _url = log.Url; _template = log.MessageTemplate; _message = log.Message; diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor index 77fd5d4c..e87450bd 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor @@ -71,12 +71,12 @@ else } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } + private string _level = "-"; + private string _function = "-"; + private string _rows = "10"; + private List _logs; - string _level = "-"; - string _function = "-"; - string _rows = "10"; - List _logs; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override async Task OnInitializedAsync() { @@ -139,12 +139,12 @@ else private async Task GetLogs() { - _logs = await LogService.GetLogsAsync(PageState.Site.SiteId, ((_level == "-") ? "" : _level), ((_function == "-") ? "" : _function), int.Parse(_rows)); + _logs = await LogService.GetLogsAsync(PageState.Site.SiteId, ((_level == "-") ? string.Empty : _level), ((_function == "-") ? string.Empty : _function), int.Parse(_rows)); } private string GetClass(string function) { - string classname = ""; + string classname = string.Empty; switch (function) { case "Create": @@ -163,7 +163,7 @@ else classname = "table-secondary"; break; default: - classname = ""; + classname = string.Empty; break; } return classname; diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor index 087f52ab..b1d72ce0 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor @@ -25,11 +25,11 @@ -@code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } +@code { + private string _name = string.Empty; + private string _description = string.Empty; - string _name = ""; - string _description = ""; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override void OnInitialized() { @@ -42,7 +42,7 @@ { if (!string.IsNullOrEmpty(_name)) { - ModuleDefinition moduleDefinition = new ModuleDefinition { Name = _name, Description = _description }; + var moduleDefinition = new ModuleDefinition { Name = _name, Description = _description }; await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition, ModuleState.ModuleId); } else diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index bce9e1eb..a342297b 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -42,16 +42,17 @@ @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + private List _packages; - List _packages; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnInitializedAsync() { try { - List moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); + var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); _packages = await PackageService.GetPackagesAsync("module"); + foreach(Package package in _packages.ToArray()) { if (moduledefinitions.Exists(item => Utilities.GetTypeName(item.ModuleDefinitionName) == package.PackageId)) diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index fb6c723e..732d7d5c 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -26,26 +26,26 @@ @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - - int _moduleDefinitionId; - string _name; - string _permissions; - string _createdby; - DateTime _createdon; - string _modifiedby; - DateTime _modifiedon; + private int _moduleDefinitionId; + private string _name; + private string _permissions; + private string _createdby; + private DateTime _createdon; + private string _modifiedby; + private DateTime _modifiedon; #pragma warning disable 649 - PermissionGrid _permissionGrid; + private PermissionGrid _permissionGrid; #pragma warning restore 649 + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; + protected override async Task OnInitializedAsync() { try { _moduleDefinitionId = Int32.Parse(PageState.QueryString["id"]); - ModuleDefinition moduleDefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId); + var moduleDefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId); if (moduleDefinition != null) { _name = moduleDefinition.Name; @@ -67,7 +67,7 @@ { try { - ModuleDefinition moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId); + var moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId); moduledefinition.Permissions = _permissionGrid.GetPermissions(); await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition); await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition); diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor index 3efd4ff8..d348ddb4 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor @@ -41,10 +41,10 @@ else } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + private List _moduleDefinitions; + private List _packages; - List _moduleDefinitions; - List _packages; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnInitializedAsync() { @@ -62,12 +62,13 @@ else private bool UpgradeAvailable(string moduledefinitionname, string version) { - bool upgradeavailable = false; - Package package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault(); + var upgradeavailable = false; + var package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault(); if (package != null) { upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0); } + return upgradeavailable; } diff --git a/Oqtane.Client/Modules/Admin/Modules/Export.razor b/Oqtane.Client/Modules/Admin/Modules/Export.razor index 5ac95386..77c0ba53 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Export.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Export.razor @@ -20,10 +20,11 @@ @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - public override string Title { get { return "Export Module"; } } + private string _content = string.Empty; + + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; + public override string Title => "Export Module"; - string _content = ""; private async Task ExportModule() { diff --git a/Oqtane.Client/Modules/Admin/Modules/Import.razor b/Oqtane.Client/Modules/Admin/Modules/Import.razor index 5fe730fd..2a4d3aa0 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Import.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Import.razor @@ -20,15 +20,14 @@ @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } - public override string Title { get { return "Import Module"; } } - - string _content = ""; + private string _content = string.Empty; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; + public override string Title => "Import Module"; private async Task ImportModule() { - if (_content != "") + if (_content != string.Empty) { try { diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index 3f27530c..a98d015e 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -91,22 +91,21 @@ } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Edit; } } - public override string Title { get { return "Module Settings"; } } + private Dictionary _containers; + private string _title; + private string _containerType; + private string _permissionNames = ""; + private string _permissions; + private string _pageId; + private PermissionGrid _permissionGrid; + private Type _settingsModuleType; + private string _settingstitle = "Other Settings"; + private object _settings; - Dictionary _containers; - string _title; - string _containerType; - string _permissionNames = ""; - string _permissions; - string _pageId; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; + public override string Title => "Module Settings"; - PermissionGrid _permissionGrid; - - Type _settingsModuleType; - string _settingstitle = "Other Settings"; - RenderFragment DynamicComponent { get; set; } - object _settings; + private RenderFragment DynamicComponent { get; set; } protected override async Task OnInitializedAsync() { @@ -134,20 +133,21 @@ private async Task SaveModule() { - Module module = ModuleState; + var module = ModuleState; module.Permissions = _permissionGrid.GetPermissions(); await ModuleService.UpdateModuleAsync(module); - PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); + var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); pagemodule.PageId = int.Parse(_pageId); pagemodule.Title = _title; pagemodule.ContainerType = _containerType; + await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); if (_settingsModuleType != null) { - Type moduleType = Type.GetType(ModuleState.ModuleType); + var moduleType = Type.GetType(ModuleState.ModuleType); if (moduleType != null) { moduleType.GetMethod("UpdateSettings")?.Invoke(_settings, null); // method must be public in settings component diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 77af0407..671ce22e 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -118,7 +118,7 @@ "; + foreach (KeyValuePair kvp in item) { table += ""; } + table += ""; } + table += ""; + foreach (KeyValuePair kvp in item) { table += ""; } + table += ""; } - if (table != "") + + if (table != string.Empty) { table += "
    " + kvp.Key + "
    " + kvp.Value + "
    "; } @@ -99,6 +105,7 @@ else { table = "No Results Returned"; } + return table; } } diff --git a/Oqtane.Client/Modules/Admin/Tenants/Add.razor b/Oqtane.Client/Modules/Admin/Tenants/Add.razor index 9fe32192..5b6492bd 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Add.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Add.razor @@ -72,15 +72,16 @@ Cancel @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + private string name = string.Empty; + private string type = "LocalDB"; + private string server = "(LocalDb)\\MSSQLLocalDB"; + private string database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm"); + private string username = string.Empty; + private string password = string.Empty; + private string schema = string.Empty; + private string integratedsecurity = "display: none;"; - string name = ""; - string type = "LocalDB"; - string server = "(LocalDb)\\MSSQLLocalDB"; - string database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm"); - string username = ""; - string password = ""; - string integratedsecurity = "display: none;"; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; private void SetIntegratedSecurity(ChangeEventArgs e) { @@ -90,7 +91,7 @@ } else { - integratedsecurity = ""; + integratedsecurity = string.Empty; } } @@ -100,7 +101,7 @@ { ShowProgressIndicator(); - string connectionString = ""; + var connectionstring = string.Empty; if (type == "LocalDB") { connectionString = "Data Source=" + server + ";AttachDbFilename=|DataDirectory|\\" + database + ".mdf;Initial Catalog=" + database + ";Integrated Security=SSPI;"; @@ -108,6 +109,7 @@ else { connectionString = "Data Source=" + server + ";Initial Catalog=" + database + ";"; + if (integratedsecurity == "display: none;") { connectionString += "Integrated Security=SSPI;"; @@ -125,11 +127,11 @@ ConnectionString = connectionString, }; - Installation installation = await InstallationService.Install(config); + var installation = await InstallationService.Install(config); if (installation.Success) { //TODO : Move to Database Manager - Tenant tenant = new Tenant + var tenant = new Tenant { Name = name, DBConnectionString = connectionString, diff --git a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor index afba20e7..aabb0412 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor @@ -26,18 +26,19 @@ Cancel @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + private int tenantid; + private string name = string.Empty; + private string connectionstring = string.Empty; + private string schema = string.Empty; - int tenantid; - string name = ""; - string connectionstring = ""; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnInitializedAsync() { try { tenantid = Int32.Parse(PageState.QueryString["id"]); - Tenant tenant = await TenantService.GetTenantAsync(tenantid); + var tenant = await TenantService.GetTenantAsync(tenantid); if (tenant != null) { name = tenant.Name; @@ -56,11 +57,13 @@ try { connectionstring = connectionstring.Replace("\\\\", "\\"); - Tenant tenant = await TenantService.GetTenantAsync(tenantid); + var tenant = await TenantService.GetTenantAsync(tenantid); if (tenant != null) { tenant.Name = name; tenant.DBConnectionString = connectionstring; + tenant.DBSchema = schema; + await TenantService.UpdateTenantAsync(tenant); await logger.LogInformation("Tenant Saved {TenantId}", tenantid); diff --git a/Oqtane.Client/Modules/Admin/Tenants/Index.razor b/Oqtane.Client/Modules/Admin/Tenants/Index.razor index 5c22fcbf..07c2f8ad 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Index.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Index.razor @@ -26,9 +26,9 @@ else } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + private List tenants; - List tenants; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnParametersSetAsync() { diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor index a802b838..d78ab5e5 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Add.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -41,14 +41,15 @@ Cancel @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + private List packages; - List packages; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnInitializedAsync() { - List themes = await ThemeService.GetThemesAsync(); + var themes = await ThemeService.GetThemesAsync(); packages = await PackageService.GetPackagesAsync("theme"); + foreach(Package package in packages.ToArray()) { if (themes.Exists(item => Utilities.GetTypeName(item.ThemeName) == package.PackageId)) diff --git a/Oqtane.Client/Modules/Admin/Themes/Index.razor b/Oqtane.Client/Modules/Admin/Themes/Index.razor index 09ec9823..4a3113b6 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Index.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Index.razor @@ -39,10 +39,10 @@ else } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + private List themes; + private List packages; - List themes; - List packages; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnInitializedAsync() { @@ -52,12 +52,13 @@ else private bool UpgradeAvailable(string themename, string version) { - bool upgradeavailable = false; - Package package = packages.Where(item => item.PackageId == Utilities.GetTypeName(themename)).FirstOrDefault(); + var upgradeavailable = false; + var package = packages.Where(item => item.PackageId == Utilities.GetTypeName(themename)).FirstOrDefault(); if (package != null) { upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0); } + return upgradeavailable; } diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index 36dad205..5d55b6aa 100644 --- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor +++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor @@ -26,14 +26,14 @@ } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } + private bool upgradeavailable = false; - bool upgradeavailable = false; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnInitializedAsync() { - List packages = await PackageService.GetPackagesAsync("framework"); - Package package = packages.FirstOrDefault(); + var packages = await PackageService.GetPackagesAsync("framework"); + var package = packages.FirstOrDefault(); if (package != null) { upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) > 0); diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor index 5da04ebd..6cf41e3f 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor @@ -46,13 +46,14 @@ } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } } - public override string Title { get { return "Send Notification"; } } + private List userroles; + private string userid = "-1"; + private string subject = ""; + private string body = ""; - List userroles; - string userid = "-1"; - string subject = ""; - string body = ""; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View; + + public override string Title => "Send Notification"; protected override async Task OnInitializedAsync() { @@ -71,7 +72,7 @@ private async Task Send() { - Notification notification = new Notification(); + var notification = new Notification(); try { notification.SiteId = PageState.Site.SiteId; diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index 00c22dbd..6711617a 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -172,20 +172,20 @@ } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } } + private string username = string.Empty; + private string password = string.Empty; + private string confirm = string.Empty; + private string email = string.Empty; + private string displayname = string.Empty; + private FileManager filemanager; + private int photofileid = -1; + private List profiles; + private Dictionary settings; + private string category = string.Empty; + private string filter = "to"; + private List notifications; - string username = ""; - string password = ""; - string confirm = ""; - string email = ""; - string displayname = ""; - FileManager filemanager; - int photofileid = -1; - List profiles; - Dictionary settings; - string category = ""; - string filter = "to"; - List notifications; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View; protected override async Task OnInitializedAsync() { @@ -196,12 +196,15 @@ username = PageState.User.Username; email = PageState.User.Email; displayname = PageState.User.DisplayName; + if (PageState.User.PhotoFileId != null) { photofileid = PageState.User.PhotoFileId.Value; } + profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); + await LoadNotificationsAsync(); } else @@ -223,29 +226,29 @@ } private string GetProfileValue(string SettingName, string DefaultValue) - { - return SettingService.GetSetting(settings, SettingName, DefaultValue); - } + => SettingService.GetSetting(settings, SettingName, DefaultValue); private async Task Save() { try { - if (username != "" && email != "") + if (username != string.Empty && email != string.Empty) { if (password == confirm) { - User user = PageState.User; + var user = PageState.User; user.Username = username; user.Password = password; user.Email = email; - user.DisplayName = (displayname == "" ? username : displayname); + user.DisplayName = (displayname == string.Empty ? username : displayname); user.PhotoFileId = null; photofileid = filemanager.GetFileId(); + if (photofileid != -1) { user.PhotoFileId = photofileid; } + await UserService.UpdateUserAsync(user); await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); await logger.LogInformation("User Profile Saved"); @@ -269,12 +272,12 @@ private void Cancel() { - NavigationManager.NavigateTo(NavigateUrl("")); + NavigationManager.NavigateTo(NavigateUrl(string.Empty)); } private void ProfileChanged(ChangeEventArgs e, string SettingName) { - string value = (string)e.Value; + var value = (string)e.Value; settings = SettingService.SetSetting(settings, SettingName, value); } @@ -291,6 +294,7 @@ { await NotificationService.DeleteNotificationAsync(Notification.NotificationId); } + await logger.LogInformation("Notification Deleted {Notification}", Notification); await LoadNotificationsAsync(); StateHasChanged(); @@ -305,6 +309,7 @@ private async void FilterChanged(ChangeEventArgs e) { filter = (string)e.Value; + await LoadNotificationsAsync(); StateHasChanged(); } diff --git a/Oqtane.Client/Modules/Admin/UserProfile/View.razor b/Oqtane.Client/Modules/Admin/UserProfile/View.razor index bc018124..07140f63 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/View.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/View.razor @@ -52,7 +52,7 @@
    - @if (reply != "") + @if (reply != string.Empty) { } @@ -70,17 +70,17 @@ } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } } - public override string Title { get { return "View Notification"; } } + private int notificationid; + private string title = string.Empty; + private List userroles; + private string userid = "-1"; + private string subject = string.Empty; + private string createdon = string.Empty; + private string body = string.Empty; + private string reply = string.Empty; - int notificationid; - string title = ""; - List userroles; - string userid = "-1"; - string subject = ""; - string createdon = ""; - string body = ""; - string reply = ""; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View; + public override string Title => "View Notification"; protected override async Task OnInitializedAsync() { @@ -110,6 +110,7 @@ userid = notification.ToUserId.ToString(); } } + subject = notification.Subject; createdon = notification.CreatedOn.ToString(); body = notification.Body; @@ -132,11 +133,11 @@ private async Task Send() { - Notification notification = new Notification(); + var notification = new Notification(); notification.SiteId = PageState.Site.SiteId; notification.FromUserId = PageState.User.UserId; notification.ToUserId = int.Parse(userid); - notification.ToEmail = ""; + notification.ToEmail = string.Empty; notification.Subject = subject; notification.Body = body; notification.ParentId = notificationid; diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor index 66ad5c12..561185f6 100644 --- a/Oqtane.Client/Modules/Admin/Users/Add.razor +++ b/Oqtane.Client/Modules/Admin/Users/Add.razor @@ -76,16 +76,16 @@ } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } + private string username = string.Empty; + private string password = string.Empty; + private string confirm = string.Empty; + private string email = string.Empty; + private string displayname = string.Empty; + private List profiles; + private Dictionary settings; + private string category = string.Empty; - string username = ""; - string password = ""; - string confirm = ""; - string email = ""; - string displayname = ""; - List profiles; - Dictionary settings; - string category = ""; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override async Task OnInitializedAsync() { @@ -105,11 +105,11 @@ { try { - if (username != "" && password != "" && confirm != "" && email != "") + if (username != string.Empty && password != string.Empty && confirm != string.Empty && email != string.Empty) { if (password == confirm) { - User user = new User(); + var user = new User(); user.SiteId = PageState.Site.SiteId; user.Username = username; user.Password = password; @@ -150,7 +150,7 @@ private void ProfileChanged(ChangeEventArgs e, string SettingName) { - string value = (string)e.Value; + var value = (string)e.Value; settings = SettingService.SetSetting(settings, SettingName, value); } diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index 6c072d8c..a0b8370b 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -106,26 +106,26 @@ } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } + private int userid; + private string username = string.Empty; + private string password = string.Empty; + private string confirm = string.Empty; + private string email = string.Empty; + private string displayname = string.Empty; + private FileManager filemanager; + private int photofileid = -1; + private List profiles; + private Dictionary settings; + private string category = string.Empty; + private string createdby; + private DateTime createdon; + private string modifiedby; + private DateTime modifiedon; + private string deletedby; + private DateTime? deletedon; + private string isdeleted; - int userid; - string username = ""; - string password = ""; - string confirm = ""; - string email = ""; - string displayname = ""; - FileManager filemanager; - int photofileid = -1; - List profiles; - Dictionary settings; - string category = ""; - string createdby; - DateTime createdon; - string modifiedby; - DateTime modifiedon; - string deletedby; - DateTime? deletedon; - string isdeleted; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override async Task OnInitializedAsync() { @@ -134,16 +134,18 @@ profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId); userid = Int32.Parse(PageState.QueryString["id"]); - User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); + var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); if (user != null) { username = user.Username; email = user.Email; displayname = user.DisplayName; + if (user.PhotoFileId != null) { photofileid = user.PhotoFileId.Value; } + settings = await SettingService.GetUserSettingsAsync(user.UserId); createdby = user.CreatedBy; createdon = user.CreatedOn; @@ -162,19 +164,17 @@ } private string GetProfileValue(string SettingName, string DefaultValue) - { - return SettingService.GetSetting(settings, SettingName, DefaultValue); - } + => SettingService.GetSetting(settings, SettingName, DefaultValue); private async Task SaveUser() { try { - if (username != "" && password != "" && confirm != "" && email != "") + if (username != string.Empty && password != string.Empty && confirm != string.Empty && email != string.Empty) { if (password == confirm) { - User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); + var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); user.SiteId = PageState.Site.SiteId; user.Username = username; user.Password = password; @@ -182,10 +182,12 @@ user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; user.PhotoFileId = null; photofileid = filemanager.GetFileId(); + if (photofileid != -1) { user.PhotoFileId = photofileid; } + user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted)); user = await UserService.UpdateUserAsync(user); @@ -213,7 +215,7 @@ private void ProfileChanged(ChangeEventArgs e, string SettingName) { - string value = (string)e.Value; + var value = (string)e.Value; settings = SettingService.SetSetting(settings, SettingName, value); } diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index c6e52c6e..a9d4366f 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -28,9 +28,9 @@ else } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } + private List userroles; - List userroles; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override async Task OnInitializedAsync() { @@ -42,7 +42,7 @@ else { try { - User user = await UserService.GetUserAsync(UserRole.UserId, PageState.Site.SiteId); + var user = await UserService.GetUserAsync(UserRole.UserId, PageState.Site.SiteId); if (user != null) { await UserService.DeleteUserAsync(user.UserId); diff --git a/Oqtane.Client/Modules/Admin/Users/Roles.razor b/Oqtane.Client/Modules/Admin/Users/Roles.razor index 9477bbbd..fa84b46d 100644 --- a/Oqtane.Client/Modules/Admin/Users/Roles.razor +++ b/Oqtane.Client/Modules/Admin/Users/Roles.razor @@ -65,14 +65,14 @@ else } @code { - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } + private int userid; + private List roles; + private int roleid = -1; + private string effectivedate = string.Empty; + private string expirydate = string.Empty; + private List userroles; - int userid; - List roles; - int roleid = -1; - string effectivedate = ""; - string expirydate = ""; - List userroles; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override async Task OnInitializedAsync() { @@ -109,7 +109,7 @@ else { if (roleid != -1) { - UserRole userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault(); + var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault(); if (userrole != null) { if (string.IsNullOrEmpty(effectivedate)) @@ -120,6 +120,7 @@ else { userrole.EffectiveDate = DateTime.Parse(effectivedate); } + if (string.IsNullOrEmpty(expirydate)) { userrole.ExpiryDate = null; @@ -135,6 +136,7 @@ else userrole = new UserRole(); userrole.UserId = userid; userrole.RoleId = roleid; + if (string.IsNullOrEmpty(effectivedate)) { userrole.EffectiveDate = null; @@ -143,6 +145,7 @@ else { userrole.EffectiveDate = DateTime.Parse(effectivedate); } + if (string.IsNullOrEmpty(expirydate)) { userrole.ExpiryDate = null; @@ -151,8 +154,10 @@ else { userrole.ExpiryDate = DateTime.Parse(expirydate); } + await UserRoleService.AddUserRoleAsync(userrole); } + await GetUserRoles(); await logger.LogInformation("User Assigned To Role {UserRole}", userrole); AddModuleMessage("User Assigned To Role", MessageType.Success); diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor index bd3d5e2c..08b2d77d 100644 --- a/Oqtane.Client/Modules/Controls/ActionDialog.razor +++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor @@ -39,6 +39,11 @@ } @code { + private bool _visible = false; + private bool _editmode = true; + private bool _authorized = false; + private string _iconSpan = string.Empty; + [Parameter] public string Header { get; set; } // required @@ -69,11 +74,6 @@ [Parameter] public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon - bool _visible = false; - bool _editmode = true; - bool _authorized = false; - string _iconSpan = ""; - protected override void OnParametersSet() { if (string.IsNullOrEmpty(Text)) diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index 48b8116e..cf588579 100644 --- a/Oqtane.Client/Modules/Controls/ActionLink.razor +++ b/Oqtane.Client/Modules/Controls/ActionLink.razor @@ -15,6 +15,15 @@ } @code { + private string _text = string.Empty; + private string _url = string.Empty; + private string _parameters = string.Empty; + private string _classname = "btn btn-primary"; + private string _style = string.Empty; + private bool _editmode = true; + private bool _authorized = false; + private string _iconSpan = string.Empty; + [Parameter] public string Action { get; set; } // required @@ -42,15 +51,6 @@ [Parameter] public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon - string _text = ""; - string _url = ""; - string _parameters = ""; - string _classname = "btn btn-primary"; - string _style = ""; - bool _editmode = true; - bool _authorized = false; - string _iconSpan = ""; - protected override void OnParametersSet() { _text = Action; @@ -90,14 +90,14 @@ private bool IsAuthorized() { - bool authorized = false; + var authorized = false; if (PageState.EditMode || !_editmode) { - SecurityAccessLevel security = SecurityAccessLevel.Host; + var security = SecurityAccessLevel.Host; if (Security == null) { - string typename = ModuleState.ModuleType.Replace(Utilities.GetTypeNameLastSegment(ModuleState.ModuleType, 0) + ",", Action + ","); - Type moduleType = Type.GetType(typename); + var typename = ModuleState.ModuleType.Replace(Utilities.GetTypeNameLastSegment(ModuleState.ModuleType, 0) + ",", Action + ","); + var moduleType = Type.GetType(typename); if (moduleType != null) { var moduleobject = Activator.CreateInstance(moduleType); @@ -113,6 +113,7 @@ { security = Security.Value; } + switch (security) { case SecurityAccessLevel.Anonymous: @@ -132,6 +133,7 @@ break; } } + return authorized; } } diff --git a/Oqtane.Client/Modules/Controls/AuditInfo.razor b/Oqtane.Client/Modules/Controls/AuditInfo.razor index 9c0d1a6c..6aa6f470 100644 --- a/Oqtane.Client/Modules/Controls/AuditInfo.razor +++ b/Oqtane.Client/Modules/Controls/AuditInfo.razor @@ -1,12 +1,15 @@ @namespace Oqtane.Modules.Controls @inherits ModuleBase -@if (_text != "") +@if (_text != string.Empty) { @((MarkupString)_text) } @code { + + private string _text = string.Empty; + [Parameter] public string CreatedBy { get; set; } @@ -31,50 +34,57 @@ [Parameter] public string Style { get; set; } - string _text = ""; - protected override void OnParametersSet() { - _text = ""; + _text = string.Empty; if (!String.IsNullOrEmpty(CreatedBy) || CreatedOn != null) { - _text += "

    Created "; + _text += "

    Created "; + if (!String.IsNullOrEmpty(CreatedBy)) { _text += " by " + CreatedBy + ""; } + if (CreatedOn != null) { _text += " on " + CreatedOn.ToString("MMM dd yyyy HH:mm:ss") + ""; } + _text += "

    "; } if (!String.IsNullOrEmpty(ModifiedBy) || ModifiedOn != null) { - _text += "

    Last modified "; + _text += "

    Last modified "; + if (!String.IsNullOrEmpty(ModifiedBy)) { _text += " by " + ModifiedBy + ""; } + if (ModifiedOn != null) { _text += " on " + ModifiedOn.ToString("MMM dd yyyy HH:mm:ss") + ""; } + _text += "

    "; } if (!String.IsNullOrEmpty(DeletedBy) || DeletedOn.HasValue) { - _text += "

    Deleted "; + _text += "

    Deleted "; + if (!String.IsNullOrEmpty(DeletedBy)) { _text += " by " + DeletedBy + ""; } + if (DeletedOn != null) { _text += " on " + DeletedOn.Value.ToString("MMM dd yyyy HH:mm:ss") + ""; } + _text += "

    "; } } diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index 17edca5f..474bfb49 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -70,7 +70,7 @@ @((MarkupString)_message) }
    - @if (_image != "") + @if (_image != string.Empty) {
    @((MarkupString)_image) @@ -81,6 +81,21 @@ } @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; + [Parameter] public string Folder { get; set; } // optional - for setting a specific folder by default @@ -99,21 +114,6 @@ [Parameter] public string UploadMultiple { get; set; } // optional - enable multiple file uploads - default false - string _id; - List _folders; - int _folderid = -1; - List _files = new List(); - int _fileid = -1; - bool _showfiles = true; - string _fileinputid = ""; - string _progressinfoid = ""; - string _progressbarid = ""; - string _filter = "*"; - bool _uploadmultiple = false; - bool _haseditpermission = false; - string _message = ""; - string _image = ""; - protected override async Task OnInitializedAsync() { if (!string.IsNullOrEmpty(Folder)) @@ -205,13 +205,13 @@ private async Task FolderChanged(ChangeEventArgs e) { - _message = ""; + _message = string.Empty; try { _folderid = int.Parse((string)e.Value); await GetFiles(); _fileid = -1; - _image = ""; + _image = string.Empty; StateHasChanged(); } catch (Exception ex) @@ -223,30 +223,31 @@ private async Task FileChanged(ChangeEventArgs e) { - _message = ""; + _message = string.Empty; _fileid = int.Parse((string)e.Value); + await SetImage(); StateHasChanged(); } private async Task SetImage() { - _image = ""; + _image = string.Empty; if (_fileid != -1) { File file = await FileService.GetFileAsync(_fileid); if (file.ImageHeight != 0 && file.ImageWidth != 0) { - int maxwidth = 200; - int maxheight = 200; + var maxwidth = 200; + var maxheight = 200; - double ratioX = (double)maxwidth / (double)file.ImageWidth; - double ratioY = (double)maxheight / (double)file.ImageHeight; - double ratio = ratioX < ratioY ? ratioX : ratioY; + var ratioX = (double)maxwidth / (double)file.ImageWidth; + var ratioY = (double)maxheight / (double)file.ImageHeight; + var ratio = ratioX < ratioY ? ratioX : ratioY; - _image = "\"""; + _image = "\string.Empty"; } } } @@ -254,7 +255,7 @@ private async Task UploadFile() { var interop = new Interop(JsRuntime); - string[] upload = await interop.GetFiles(_fileinputid); + var upload = await interop.GetFiles(_fileinputid); if (upload.Length > 0) { try @@ -268,14 +269,16 @@ { result = await FileService.UploadFilesAsync(_folderid, upload, _id); } - if (result == "") + + if (result == string.Empty) { await logger.LogInformation("File Upload Succeeded {Files}", upload); _message = "
    File Upload Succeeded
    "; await GetFiles(); + if (upload.Length == 1) { - File file = _files.Where(item => item.Name == upload[0]).FirstOrDefault(); + var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault(); if (file != null) { _fileid = file.FileId; @@ -304,7 +307,8 @@ private async Task DeleteFile() { - _message = ""; + _message = string.Empty; + try { await FileService.DeleteFileAsync(_fileid); @@ -322,9 +326,6 @@ } } - public int GetFileId() - { - return _fileid; - } + public int GetFileId() => _fileid; } diff --git a/Oqtane.Client/Modules/Controls/Label.razor b/Oqtane.Client/Modules/Controls/Label.razor index 6152baa1..af13ea16 100644 --- a/Oqtane.Client/Modules/Controls/Label.razor +++ b/Oqtane.Client/Modules/Controls/Label.razor @@ -11,8 +11,8 @@ else } @code { - string _openLabel = ""; - string _closeLabel = ""; + private string _openLabel = string.Empty; + private string _closeLabel = ""; [Parameter] public RenderFragment ChildContent { get; set; } // required - the title of the label @@ -31,12 +31,14 @@ else _openLabel = " @code { - int _pages = 0; - int _page = 1; - int _maxItems; - int _maxPages; - int _startPage; - int _endPage; + private int _pages = 0; + private int _page = 1; + private int _maxItems; + private int _maxPages; + private int _startPage; + private int _endPage; [Parameter] public string Format { get; set; } @@ -95,7 +95,7 @@ [Parameter] public string Class { get; set; } - IEnumerable ItemList { get; set; } + private IEnumerable ItemList { get; set; } protected override void OnParametersSet() { @@ -103,6 +103,7 @@ { Format = "Table"; } + if (string.IsNullOrEmpty(Class)) { if (Format == "Table") @@ -114,6 +115,7 @@ Class = "container"; } } + if (string.IsNullOrEmpty(PageSize)) { _maxItems = 10; @@ -122,6 +124,7 @@ { _maxItems = int.Parse(PageSize); } + if (string.IsNullOrEmpty(DisplayPages)) { _maxPages = 5; @@ -144,6 +147,7 @@ { ItemList = Items.Skip((currentPage - 1) * _maxItems).Take(_maxItems); _page = currentPage; + StateHasChanged(); } @@ -168,6 +172,7 @@ { _endPage = _pages; } + StateHasChanged(); } else if (direction == "back") @@ -201,6 +206,7 @@ _page -= 1; } } + 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 974bd31c..48a3d5b8 100644 --- a/Oqtane.Client/Modules/Controls/PermissionGrid.razor +++ b/Oqtane.Client/Modules/Controls/PermissionGrid.razor @@ -74,6 +74,13 @@ } @code { + private string _permissionnames = string.Empty; + private List _roles; + private List _permissions; + private List _users = new List(); + private string _username = string.Empty; + private string _message = string.Empty; + [Parameter] public string EntityName { get; set; } @@ -83,13 +90,6 @@ [Parameter] public string Permissions { get; set; } - string _permissionnames = ""; - List _roles; - List _permissions; - List _users = new List(); - string _username = ""; - string _message = ""; - protected override async Task OnInitializedAsync() { if (string.IsNullOrEmpty(PermissionNames)) @@ -105,6 +105,7 @@ _roles.Insert(0, new Role { Name = Constants.AllUsersRole }); _permissions = new List(); + foreach (string permissionname in _permissionnames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { // initialize with admin role @@ -120,13 +121,14 @@ { _permissions[_permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions; } + if (permissionstring.Permissions.Contains("[")) { foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries)) { if (user.Contains("]")) { - int userid = int.Parse(user.Substring(0, user.IndexOf("]"))); + var userid = int.Parse(user.Substring(0, user.IndexOf("]"))); if (_users.Where(item => item.UserId == userid).FirstOrDefault() == null) { _users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId)); @@ -158,16 +160,9 @@ } private bool GetPermissionDisabled(string roleName) - { - if (roleName == Constants.AdminRole) - { - return true; - } - else - { - return false; - } - } + => roleName == Constants.AdminRole + ? true + : false; private async Task AddUser() { @@ -175,7 +170,7 @@ { try { - User user = await UserService.GetUserAsync(_username, ModuleState.SiteId); + var user = await UserService.GetUserAsync(_username, ModuleState.SiteId); if (user != null) { _users.Add(user); @@ -186,16 +181,17 @@ _message = "Username Does Not Exist"; } } - _username = ""; + + _username = string.Empty; } private void PermissionChanged(bool? value, string permissionName, string securityId) { - bool? selected = value; - PermissionString permission = _permissions.Find(item => item.PermissionName == permissionName); + var selected = value; + var permission = _permissions.Find(item => item.PermissionName == permissionName); if (permission != null) { - List ids = permission.Permissions.Split(';').ToList(); + var ids = permission.Permissions.Split(';').ToList(); ids.Remove(securityId); // remove grant permission ids.Remove("!" + securityId); // remove deny permission @@ -211,6 +207,7 @@ case null: break; // permission not specified } + _permissions[_permissions.FindIndex(item => item.PermissionName == permissionName)].Permissions = string.Join(";", ids.ToArray()); } } diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index 347c799c..36368c5a 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -27,6 +27,12 @@
    @code { + private ElementReference _editorElement; + private ElementReference _toolBar; + private bool _filemanagervisible = false; + private FileManager _fileManager; + private string _message = string.Empty; + [Parameter] public RenderFragment ToolbarContent { get; set; } @@ -42,12 +48,6 @@ [Parameter] public string DebugLevel { get; set; } = "info"; - private ElementReference _editorElement; - private ElementReference _toolBar; - bool _filemanagervisible = false; - FileManager _fileManager; - string _message = ""; - protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) @@ -102,14 +102,14 @@ { if (_filemanagervisible) { - int fileid = _fileManager.GetFileId(); + var fileid = _fileManager.GetFileId(); if (fileid != -1) { await RichTextEditorInterop.InsertImage( JsRuntime, _editorElement, ContentUrl(fileid)); _filemanagervisible = false; - _message = ""; + _message = string.Empty; } else { @@ -119,15 +119,17 @@ else { _filemanagervisible = true; - _message = ""; + _message = string.Empty; } + StateHasChanged(); } public void CloseFileManager() { _filemanagervisible = false; - _message = ""; + _message = string.Empty; + StateHasChanged(); } diff --git a/Oqtane.Client/Modules/Controls/TabControl.razor b/Oqtane.Client/Modules/Controls/TabControl.razor index 366561b6..c65124aa 100644 --- a/Oqtane.Client/Modules/Controls/TabControl.razor +++ b/Oqtane.Client/Modules/Controls/TabControl.razor @@ -16,12 +16,13 @@ @code { + private List _tabPanels = new List(); + // Next line is needed so we are able to add components inside [Parameter] public RenderFragment ChildContent { get; set; } public TabPanel ActiveTabPanel { get; set; } - List _tabPanels = new List(); internal void AddTabPanel(TabPanel tabPanel) { @@ -31,12 +32,12 @@ StateHasChanged(); } - string GetButtonClass(TabPanel tabPanel) - { - return tabPanel == ActiveTabPanel ? "btn-primary" : "btn-secondary"; - } + private string GetButtonClass(TabPanel tabPanel) + => tabPanel == ActiveTabPanel + ? "btn-primary" + : "btn-secondary"; - void ActivateTabPanel(TabPanel tabPanel) + private void ActivateTabPanel(TabPanel tabPanel) { ActiveTabPanel = tabPanel; } diff --git a/Oqtane.Client/Modules/Controls/TriStateCheckBox.razor b/Oqtane.Client/Modules/Controls/TriStateCheckBox.razor index 324c2174..3c056d87 100644 --- a/Oqtane.Client/Modules/Controls/TriStateCheckBox.razor +++ b/Oqtane.Client/Modules/Controls/TriStateCheckBox.razor @@ -3,6 +3,10 @@ @code { + private bool? _value = null; + private string _title; + private string _src = string.Empty; + [Parameter] public bool? Value { get; set; } @@ -12,10 +16,6 @@ [Parameter] public Action OnChange { get; set; } - bool? _value = null; - string _title; - string _src = ""; - protected override void OnInitialized() { _value = Value; @@ -38,6 +38,7 @@ _value = true; break; } + SetImage(); OnChange(_value); } @@ -57,9 +58,10 @@ break; case null: _src = "images/null.png"; - _title = ""; + _title = string.Empty; break; } + StateHasChanged(); } } diff --git a/Oqtane.Client/Modules/Counter/Index.razor b/Oqtane.Client/Modules/Counter/Index.razor index b511cfc0..9be7d4bf 100644 --- a/Oqtane.Client/Modules/Counter/Index.razor +++ b/Oqtane.Client/Modules/Counter/Index.razor @@ -7,9 +7,9 @@ Current count: @currentCount
    @code { - int currentCount = 0; + private int currentCount = 0; - void IncrementCount() + private void IncrementCount() { currentCount++; } diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index 3fc446d8..41c55192 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -16,7 +16,7 @@ + @if (name == Constants.MasterTenant) + { + + } + else + { + + } diff --git a/Oqtane.Client/Modules/Admin/Tenants/Index.razor b/Oqtane.Client/Modules/Admin/Tenants/Index.razor index 07c2f8ad..018c1f68 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Index.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Index.razor @@ -18,7 +18,7 @@ else
    - + @context.Name
    diff --git a/Oqtane.Server/Repository/TenantRepository.cs b/Oqtane.Server/Repository/TenantRepository.cs index 4af682d6..0689f3c2 100644 --- a/Oqtane.Server/Repository/TenantRepository.cs +++ b/Oqtane.Server/Repository/TenantRepository.cs @@ -4,6 +4,7 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Oqtane.Models; +using Oqtane.Shared; namespace Oqtane.Repository { @@ -37,6 +38,13 @@ namespace Oqtane.Repository public Tenant UpdateTenant(Tenant tenant) { + var oldTenant = GetTenant(tenant.TenantId); + + if (oldTenant.Name.Equals(Constants.MasterTenant, StringComparison.OrdinalIgnoreCase) && !oldTenant.Name.Equals(tenant.Name)) + { + throw new InvalidOperationException("Unable to rename the master tenant."); + } + _db.Entry(tenant).State = EntityState.Modified; _db.SaveChanges(); _cache.Remove("tenants"); @@ -50,8 +58,8 @@ namespace Oqtane.Repository public void DeleteTenant(int tenantId) { - Tenant tenant = _db.Tenant.Find(tenantId); - if (tenant != null) + var tenant = GetTenant(tenantId); + if (tenant != null && !tenant.Name.Equals(Constants.MasterTenant, StringComparison.OrdinalIgnoreCase)) { _db.Tenant.Remove(tenant); _db.SaveChanges(); diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 198db352..eacaa4c6 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -32,6 +32,8 @@ public const string HostUser = "host"; + public const string MasterTenant = "Master"; + public const string AllUsersRole = "All Users"; public const string HostRole = "Host Users"; public const string AdminRole = "Administrators"; From e8efc5e508af361a3ac4af507dd66ac44571f534 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 2 Apr 2020 12:07:35 -0400 Subject: [PATCH 101/265] upgrade to .NET Core 3.2 Preview 3 and fixes for issues created by #314 (#323) --- .../Modules/Admin/Dashboard/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Pages/Add.razor | 2 + Oqtane.Client/Modules/Admin/Pages/Edit.razor | 16 +- .../Modules/Admin/Register/Index.razor | 10 +- Oqtane.Client/Modules/Admin/Site/Index.razor | 32 ++-- Oqtane.Client/Modules/Admin/Tenants/Add.razor | 2 +- .../Modules/Admin/Tenants/Edit.razor | 1 - .../Modules/Controls/AuditInfo.razor | 6 +- .../Modules/Controls/FileManager.razor | 14 +- Oqtane.Client/Modules/Controls/Label.razor | 4 +- Oqtane.Client/Oqtane.Client.csproj | 19 +- Oqtane.Client/Program.cs | 84 +++++++-- Oqtane.Client/Startup.cs | 92 --------- .../Themes/Controls/Breadcrumbs.razor | 2 +- Oqtane.Client/Themes/Controls/Menu.razor | 5 +- .../Themes/Controls/ModuleTitle.razor | 2 +- Oqtane.Client/wwwroot/index.html | 36 ---- Oqtane.Server/Oqtane.Server.csproj | 25 +-- Oqtane.Server/Pages/_Host.cshtml | 16 +- Oqtane.Server/Program.cs | 22 +-- Oqtane.Server/Properties/launchSettings.json | 2 + Oqtane.Server/Startup.cs | 175 +----------------- Oqtane.Shared/Oqtane.Shared.csproj | 10 +- Oqtane.Test/Oqtane.Test.csproj | 6 +- Oqtane.sln | 11 -- 25 files changed, 164 insertions(+), 432 deletions(-) delete mode 100644 Oqtane.Client/Startup.cs delete mode 100644 Oqtane.Client/wwwroot/index.html diff --git a/Oqtane.Client/Modules/Admin/Dashboard/Index.razor b/Oqtane.Client/Modules/Admin/Dashboard/Index.razor index 38202300..7e3004fb 100644 --- a/Oqtane.Client/Modules/Admin/Dashboard/Index.razor +++ b/Oqtane.Client/Modules/Admin/Dashboard/Index.razor @@ -19,7 +19,7 @@
    @code { - var List _pages; + private List _pages; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 671ce22e..f8be514c 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -165,12 +165,14 @@ private List _themeList; private List _pageList; private string _name; + private string _title; private string _path = string.Empty; private string _parentid; private string _insert = ">>"; private List _children; private int _childid = -1; private string _isnavigation = "True"; + private string _url; private string _ispersonalizable = "False"; private string _mode = "view"; private string _themetype = string.Empty; diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 0422056c..a1c964f8 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -187,6 +187,7 @@ private List _pageList; private int _pageId; private string _name; + private string _title; private string _path; private string _currentparentid; private string _parentid; @@ -194,6 +195,7 @@ private List _children; private int _childid = -1; private string _isnavigation; + private string _url; private string _ispersonalizable; private string _mode; private string _themetype; @@ -230,12 +232,12 @@ _name = page.Name; _title = page.Title; _path = page.Path; - + if (_path.Contains("/")) { _path = _path.Substring(_path.LastIndexOf("/") + 1); } - + if (page.ParentId == null) { _parentid = string.Empty; @@ -244,7 +246,7 @@ { _parentid = page.ParentId.ToString(); } - + _currentparentid = _parentid; _isnavigation = page.IsNavigation.ToString(); _url = page.Url; @@ -335,10 +337,10 @@ page.Name = _name; page.Title = _title; if (_path == "" && _name.ToLower() != "home") - if (_path == string.Empty && _name.ToLower() != "home") - { - _path = _name; - } + if (_path == string.Empty && _name.ToLower() != "home") + { + _path = _name; + } if (_path.Contains("/")) { _path = _path.Substring(_path.LastIndexOf("/") + 1); diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index 0bc613ff..f358d905 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -3,7 +3,7 @@ @inject NavigationManager NavigationManager @inject IUserService UserService -@if (_message != string.Empty) +@if (_message != string.Empty) { } @@ -40,17 +40,16 @@ private string _confirm = string.Empty; private string _email = string.Empty; private string _displayName = string.Empty; - - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous; private async Task Register() { try { _message = string.Empty; - _isEmailValid = Utilities.IsValidEmail(_email); - + bool _isEmailValid = Utilities.IsValidEmail(_email); + if (_username != "" && _password != "" && _confirm != "" && _isEmailValid) { if (_password == _confirm) @@ -97,3 +96,4 @@ { NavigationManager.NavigateTo(NavigateUrl(string.Empty)); } +} \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index 6e61f3bf..afcf50ab 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -226,15 +226,23 @@ private List _aliasList; private string _urls = string.Empty; private int _logofileid = -1; - private FileManager _filemanager; + private FileManager _logofilemanager; + private int _faviconfileid = -1; + private FileManager _faviconfilemanager; private string _themetype; private string _layouttype; - private string _containertype + private string _containertype; + private string _allowregistration; private string _smtphost = string.Empty; private string _smtpport = string.Empty; private string _smtpssl = string.Empty; private string _smtpusername = string.Empty; private string _smtppassword = string.Empty; + private string _pwaisenabled; + private int _pwaappiconfileid = -1; + private FileManager _pwaappiconfilemanager; + private int _pwasplashiconfileid = -1; + private FileManager _pwasplashiconfilemanager; private string _createdby; private DateTime _createdon; private string _modifiedby; @@ -265,12 +273,12 @@ { _logofileid = site.LogoFileId.Value; } - + if (site.FaviconFileId != null) { _faviconfileid = site.FaviconFileId.Value; } - + _themetype = site.DefaultThemeType; _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); _layouttype = site.DefaultLayoutType; @@ -285,12 +293,12 @@ _smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty); _pwaisenabled = site.PwaIsEnabled.ToString(); - + if (site.PwaAppIconFileId != null) { _pwaappiconfileid = site.PwaAppIconFileId.Value; } - + if (site.PwaSplashIconFileId != null) { _pwasplashiconfileid = site.PwaSplashIconFileId.Value; @@ -361,7 +369,7 @@ unique = false; } } - + if (unique) { var site = await SiteService.GetSiteAsync(PageState.Site.SiteId, PageState.Alias); @@ -369,12 +377,12 @@ { site.Name = _name; site.LogoFileId = null; - var logofileid = _filemanager.GetFileId(); + var logofileid = _logofilemanager.GetFileId(); if (logofileid != -1) { site.LogoFileId = logofileid; } - + site.DefaultThemeType = _themetype; site.DefaultLayoutType = (_layouttype == null ? string.Empty : _layouttype); site.DefaultContainerType = _containertype; @@ -382,13 +390,13 @@ site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted)); site.PwaIsEnabled = (_pwaisenabled == null ? true : Boolean.Parse(_pwaisenabled)); - + var pwaappiconfileid = _pwaappiconfilemanager.GetFileId(); if (pwaappiconfileid != -1) { site.PwaAppIconFileId = pwaappiconfileid; } - + var pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId(); if (pwasplashiconfileid != -1) { @@ -406,7 +414,7 @@ await AliasService.DeleteAliasAsync(alias.AliasId); } } - + foreach (string name in names) { if (!_aliasList.Exists(item => item.Name == name)) diff --git a/Oqtane.Client/Modules/Admin/Tenants/Add.razor b/Oqtane.Client/Modules/Admin/Tenants/Add.razor index 5b6492bd..962b9e4c 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Add.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Add.razor @@ -101,7 +101,7 @@ { ShowProgressIndicator(); - var connectionstring = string.Empty; + var connectionString = string.Empty; if (type == "LocalDB") { connectionString = "Data Source=" + server + ";AttachDbFilename=|DataDirectory|\\" + database + ".mdf;Initial Catalog=" + database + ";Integrated Security=SSPI;"; diff --git a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor index 3becfbe7..56f3fe20 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor @@ -69,7 +69,6 @@ { tenant.Name = name; tenant.DBConnectionString = connectionstring; - tenant.DBSchema = schema; await TenantService.UpdateTenantAsync(tenant); await logger.LogInformation("Tenant Saved {TenantId}", tenantid); diff --git a/Oqtane.Client/Modules/Controls/AuditInfo.razor b/Oqtane.Client/Modules/Controls/AuditInfo.razor index 6aa6f470..75079c6c 100644 --- a/Oqtane.Client/Modules/Controls/AuditInfo.razor +++ b/Oqtane.Client/Modules/Controls/AuditInfo.razor @@ -39,7 +39,7 @@ _text = string.Empty; if (!String.IsNullOrEmpty(CreatedBy) || CreatedOn != null) { - _text += "

    Created "; + _text += "

    Created "; if (!String.IsNullOrEmpty(CreatedBy)) { @@ -56,7 +56,7 @@ if (!String.IsNullOrEmpty(ModifiedBy) || ModifiedOn != null) { - _text += "

    Last modified "; + _text += "

    Last modified "; if (!String.IsNullOrEmpty(ModifiedBy)) { @@ -73,7 +73,7 @@ if (!String.IsNullOrEmpty(DeletedBy) || DeletedOn.HasValue) { - _text += "

    Deleted "; + _text += "

    Deleted "; if (!String.IsNullOrEmpty(DeletedBy)) { diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index 474bfb49..6dd696b3 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -225,7 +225,7 @@ { _message = string.Empty; _fileid = int.Parse((string)e.Value); - + await SetImage(); StateHasChanged(); } @@ -245,9 +245,9 @@ var ratioY = (double)maxheight / (double)file.ImageHeight; var ratio = ratioX < ratioY ? ratioX : ratioY; - _image = "\string.Empty"; + _image = "\"""; } } } @@ -269,13 +269,13 @@ { result = await FileService.UploadFilesAsync(_folderid, upload, _id); } - + if (result == string.Empty) { await logger.LogInformation("File Upload Succeeded {Files}", upload); _message = "

    File Upload Succeeded
    "; await GetFiles(); - + if (upload.Length == 1) { var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault(); @@ -308,7 +308,7 @@ private async Task DeleteFile() { _message = string.Empty; - + try { await FileService.DeleteFileAsync(_fileid); diff --git a/Oqtane.Client/Modules/Controls/Label.razor b/Oqtane.Client/Modules/Controls/Label.razor index af13ea16..a643e118 100644 --- a/Oqtane.Client/Modules/Controls/Label.razor +++ b/Oqtane.Client/Modules/Controls/Label.razor @@ -31,12 +31,12 @@ else _openLabel = " netstandard2.1 Exe - false - - https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; - https://dotnet.myget.org/F/blazor-dev/api/v3/index.json; - 7.3 3.0 - Debug;Release;WebAssembly + Debug;Release 0.0.9 Oqtane Shaun Walker @@ -24,10 +19,6 @@ Oqtane - - TRACE;WASM - - @@ -36,10 +27,10 @@ - - - - + + + + diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index b256ca9b..4172834b 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -1,25 +1,79 @@ -// DO NOT REMOVE - needed for client-side Blazor -using Microsoft.AspNetCore.Blazor.Hosting; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; +using Oqtane.Services; +using System.Reflection; +using System; +using System.Linq; +using Oqtane.Modules; +using Oqtane.Shared; +using Oqtane.Providers; +using Microsoft.AspNetCore.Components.Authorization; namespace Oqtane.Client { public class Program { -#if DEBUG || RELEASE - public static void Main(string[] args) + public static async Task Main(string[] args) { - } -#endif + var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("app"); -#if WASM - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } + builder.Services.AddBaseAddressHttpClient(); - public static IWebAssemblyHostBuilder CreateHostBuilder(string[] args) => - BlazorWebAssemblyHost.CreateDefaultBuilder() - .UseBlazorStartup(); -#endif + // register auth services + builder.Services.AddAuthorizationCore(); + builder.Services.AddScoped(); + builder.Services.AddScoped(s => s.GetRequiredService()); + + // register scoped core services + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + + // 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) + { + Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name)); + if (servicetype != null) + { + builder.Services.AddScoped(servicetype, implementationtype); // traditional service interface + } + else + { + builder.Services.AddScoped(implementationtype, implementationtype); // no interface defined for service + } + } + } + + await builder.Build().RunAsync(); + } } } diff --git a/Oqtane.Client/Startup.cs b/Oqtane.Client/Startup.cs deleted file mode 100644 index 8d4dfdeb..00000000 --- a/Oqtane.Client/Startup.cs +++ /dev/null @@ -1,92 +0,0 @@ -using Microsoft.AspNetCore.Components.Builder; -using Microsoft.Extensions.DependencyInjection; -using System; -using Oqtane.Services; -using System.Linq; -using System.Net.Http; -using Microsoft.AspNetCore.Components; -using System.Reflection; -using Oqtane.Modules; -using Oqtane.Shared; -using Oqtane.Providers; -using Microsoft.AspNetCore.Blazor.Http; -using Microsoft.AspNetCore.Components.Authorization; - -namespace Oqtane.Client -{ - public class Startup - { -#if DEBUG || RELEASE - public void ConfigureServices(IServiceCollection services) - { - - } - - public void Configure(IComponentsApplicationBuilder app) - { - - } -#endif -#if WASM - public void ConfigureServices(IServiceCollection services) - { - // register auth services - services.AddAuthorizationCore(); - services.AddScoped(); - services.AddScoped(s => s.GetRequiredService()); - - // register scoped core services - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - // 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) - { - Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name)); - if (servicetype != null) - { - services.AddScoped(servicetype, implementationtype); // traditional service interface - } - else - { - services.AddScoped(implementationtype, implementationtype); // no interface defined for service - } - } - } - } - - public void Configure(IComponentsApplicationBuilder app) - { - app.AddComponent("app"); - } -#endif - } -} diff --git a/Oqtane.Client/Themes/Controls/Breadcrumbs.razor b/Oqtane.Client/Themes/Controls/Breadcrumbs.razor index 77d95f77..c0107601 100644 --- a/Oqtane.Client/Themes/Controls/Breadcrumbs.razor +++ b/Oqtane.Client/Themes/Controls/Breadcrumbs.razor @@ -12,7 +12,7 @@ protected override void OnParametersSet() { breadcrumbs = string.Empty; - var pageid = PageState.Page.PageId; + int? pageid = PageState.Page.PageId; for (int i = PageState.Pages.Count - 1; i >= 0; i--) { var p = PageState.Pages[i]; diff --git a/Oqtane.Client/Themes/Controls/Menu.razor b/Oqtane.Client/Themes/Controls/Menu.razor index 3b70830e..442729d6 100644 --- a/Oqtane.Client/Themes/Controls/Menu.razor +++ b/Oqtane.Client/Themes/Controls/Menu.razor @@ -54,7 +54,6 @@ } menu += ""; } - menu += ""; if (p.HasChildren) { @@ -114,14 +113,14 @@ if (p.PageId == PageState.Page.PageId) { menu += "
  • " + - "" + + "" + ((p.Icon != string.Empty) ? " " : string.Empty) + p.Name + " (current)
  • "; } else { menu += "
  • " + - "" + + "" + ((p.Icon != string.Empty) ? " " : string.Empty) + p.Name + "
  • "; } diff --git a/Oqtane.Client/Themes/Controls/ModuleTitle.razor b/Oqtane.Client/Themes/Controls/ModuleTitle.razor index 785d9217..bb8d6f62 100644 --- a/Oqtane.Client/Themes/Controls/ModuleTitle.razor +++ b/Oqtane.Client/Themes/Controls/ModuleTitle.razor @@ -4,7 +4,7 @@ @((MarkupString)title) @code { - private title = ""; + private string title = ""; protected override Task OnParametersSetAsync() { diff --git a/Oqtane.Client/wwwroot/index.html b/Oqtane.Client/wwwroot/index.html deleted file mode 100644 index 4244d8d4..00000000 --- a/Oqtane.Client/wwwroot/index.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - Oqtane - - - - - - - - - - - -
    - -
    -
    - - - - - - - - - - - - - - - diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 7471c460..bbf00ff7 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -3,12 +3,7 @@ netcoreapp3.1 7.3 - - https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; - https://dotnet.myget.org/F/blazor-dev/api/v3/index.json; - - true - Debug;Release;WebAssembly + Debug;Release 0.0.9 Oqtane Shaun Walker @@ -22,10 +17,6 @@ Oqtane - - TRACE;WASM - - @@ -34,13 +25,13 @@ - - - - - - - + + + + + + + diff --git a/Oqtane.Server/Pages/_Host.cshtml b/Oqtane.Server/Pages/_Host.cshtml index ac372241..c6758960 100644 --- a/Oqtane.Server/Pages/_Host.cshtml +++ b/Oqtane.Server/Pages/_Host.cshtml @@ -1,6 +1,8 @@ @page "/" @namespace Oqtane.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@using Microsoft.Extensions.Configuration +@inject IConfiguration Configuration @@ -19,7 +21,9 @@ @(Html.AntiForgeryToken()) - @(await Html.RenderComponentAsync(RenderMode.Server)) + + + @@ -28,6 +32,14 @@ - + + @if (Configuration.GetSection("Runtime").Value == "WebAssembly") + { + + } + else + { + + } diff --git a/Oqtane.Server/Program.cs b/Oqtane.Server/Program.cs index 6402cf5e..e46805e6 100644 --- a/Oqtane.Server/Program.cs +++ b/Oqtane.Server/Program.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; // DO NOT REMOVE - needed for client-side Blazor -using Microsoft.AspNetCore.Blazor.Hosting; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.AspNetCore; using Microsoft.Extensions.DependencyInjection; @@ -11,33 +11,17 @@ namespace Oqtane.Server { public class Program { -#if DEBUG || RELEASE public static void Main(string[] args) { - var host = CreateHostBuilder(args).Build(); + var host = BuildWebHost(args); using (var serviceScope = host.Services.GetRequiredService().CreateScope()) { var manager = serviceScope.ServiceProvider.GetService(); manager.StartupMigration(); } - //DatabaseManager.StartupMigration(); host.Run(); } - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); -#endif - -#if WASM - public static void Main(string[] args) - { - BuildWebHost(args).Run(); - } - public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseConfiguration(new ConfigurationBuilder() @@ -45,7 +29,5 @@ namespace Oqtane.Server .Build()) .UseStartup() .Build(); -#endif - } } diff --git a/Oqtane.Server/Properties/launchSettings.json b/Oqtane.Server/Properties/launchSettings.json index 55612d8c..fcddfc65 100644 --- a/Oqtane.Server/Properties/launchSettings.json +++ b/Oqtane.Server/Properties/launchSettings.json @@ -11,6 +11,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -18,6 +19,7 @@ "Oqtane.Server": { "commandName": "Project", "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 190e4adb..46b5e697 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -21,11 +21,6 @@ using Oqtane.Security; using Oqtane.Services; using Oqtane.Shared; -#if WASM -// DO NOT REMOVE - needed for client-side Blazor -using Microsoft.AspNetCore.ResponseCompression; -#endif - namespace Oqtane { public class Startup @@ -42,18 +37,13 @@ namespace Oqtane AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(env.ContentRootPath, "Data")); } -#if DEBUG || RELEASE // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { - services.AddRazorPages(); -#if DEBUG - services.AddServerSideBlazor().AddCircuitOptions(options => { options.DetailedErrors = true; }); -#endif -#if RELEASE + services.AddMvc().AddNewtonsoftJson(); services.AddServerSideBlazor(); -#endif + // setup HttpClient for server side in a client side compatible fashion ( with auth cookie ) if (!services.Any(x => x.ServiceType == typeof(HttpClient))) { @@ -217,6 +207,7 @@ namespace Oqtane if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); + app.UseWebAssemblyDebugging(); } else { @@ -243,168 +234,10 @@ namespace Oqtane app.UseEndpoints(endpoints => { - endpoints.MapRazorPages(); - endpoints.MapControllers(); endpoints.MapBlazorHub(); + endpoints.MapControllers(); endpoints.MapFallbackToPage("/_Host"); }); } -#endif - -#if WASM - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) - { - // register authorization services - services.AddAuthorizationCore(options => - { - options.AddPolicy("ViewPage", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.View))); - options.AddPolicy("EditPage", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.Edit))); - options.AddPolicy("ViewModule", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Module, PermissionNames.View))); - options.AddPolicy("EditModule", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Module, PermissionNames.Edit))); - }); - - // register scoped core services - services.AddScoped(); - services.AddScoped(); - - services.AddSingleton(); - - services.AddDbContext(options => - options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection") - .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString()) - )); - services.AddDbContext(options => { }); - - services.AddIdentityCore(options => { }) - .AddEntityFrameworkStores() - .AddSignInManager() - .AddDefaultTokenProviders(); - - services.Configure(options => - { - // Password settings - options.Password.RequireDigit = false; - options.Password.RequiredLength = 6; - options.Password.RequireNonAlphanumeric = false; - options.Password.RequireUppercase = false; - options.Password.RequireLowercase = false; - - // Lockout settings - options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30); - options.Lockout.MaxFailedAccessAttempts = 10; - options.Lockout.AllowedForNewUsers = true; - - // User settings - options.User.RequireUniqueEmail = false; - }); - - services.AddAuthentication(IdentityConstants.ApplicationScheme) - .AddCookie(IdentityConstants.ApplicationScheme); - - services.ConfigureApplicationCookie(options => - { - options.Cookie.HttpOnly = false; - options.Events.OnRedirectToLogin = context => - { - context.Response.StatusCode = 401; - return Task.CompletedTask; - }; - }); - - // register custom claims principal factory for role claims - services.AddTransient, ClaimsPrincipalFactory>(); - - // register singleton scoped core services - services.AddSingleton(Configuration); - services.AddSingleton(); - services.AddSingleton(); - - // register transient scoped core services - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - - services.AddOqtaneModules(); - services.AddOqtaneThemes(); - services.AddOqtaneSiteTemplates(); - - services.AddMvc() - .AddOqtaneApplicationParts() - .AddNewtonsoftJson(); - - services.AddOqtaneServices(); - services.AddOqtaneHostedServices(); - - services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "Oqtane", Version = "v1" }); - }); - - services.AddResponseCompression(opts => - { - opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat( - new[] { "application/octet-stream" }); - }); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IInstallationManager InstallationManager) - { - app.UseResponseCompression(); - - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseBlazorDebugging(); - } - - // install any modules or themes - InstallationManager.InstallPackages("Modules,Themes", false); - - app.UseClientSideBlazorFiles(); - app.UseStaticFiles(); - - app.UseRouting(); - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseSwagger(); - app.UseSwaggerUI(c => - { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "Oqtane V1"); - }); - - app.UseEndpoints(endpoints => - { - endpoints.MapDefaultControllerRoute(); - endpoints.MapFallbackToClientSideBlazor("index.html"); - }); - } -#endif - } } diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index 830bda51..6a0c0d87 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -3,7 +3,7 @@ netstandard2.1 7.3 - Debug;Release;WebAssembly + Debug;Release 0.0.9 Oqtane Shaun Walker @@ -17,13 +17,9 @@ Oqtane - - TRACE;WASM - - - - + + diff --git a/Oqtane.Test/Oqtane.Test.csproj b/Oqtane.Test/Oqtane.Test.csproj index b8e24038..022b6616 100644 --- a/Oqtane.Test/Oqtane.Test.csproj +++ b/Oqtane.Test/Oqtane.Test.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/Oqtane.sln b/Oqtane.sln index 3832d333..0caeb4f6 100644 --- a/Oqtane.sln +++ b/Oqtane.sln @@ -17,39 +17,28 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU - WebAssembly|Any CPU = WebAssembly|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {083BB22D-DF24-43A2-95E5-8F385CCB3318}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {083BB22D-DF24-43A2-95E5-8F385CCB3318}.Debug|Any CPU.Build.0 = Debug|Any CPU {083BB22D-DF24-43A2-95E5-8F385CCB3318}.Release|Any CPU.ActiveCfg = Release|Any CPU {083BB22D-DF24-43A2-95E5-8F385CCB3318}.Release|Any CPU.Build.0 = Release|Any CPU - {083BB22D-DF24-43A2-95E5-8F385CCB3318}.WebAssembly|Any CPU.ActiveCfg = WebAssembly|Any CPU - {083BB22D-DF24-43A2-95E5-8F385CCB3318}.WebAssembly|Any CPU.Build.0 = WebAssembly|Any CPU {FD15B24A-7F6A-4830-9CA2-9C621771C330}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD15B24A-7F6A-4830-9CA2-9C621771C330}.Debug|Any CPU.Build.0 = Debug|Any CPU {FD15B24A-7F6A-4830-9CA2-9C621771C330}.Release|Any CPU.ActiveCfg = Release|Any CPU {FD15B24A-7F6A-4830-9CA2-9C621771C330}.Release|Any CPU.Build.0 = Release|Any CPU - {FD15B24A-7F6A-4830-9CA2-9C621771C330}.WebAssembly|Any CPU.ActiveCfg = WebAssembly|Any CPU - {FD15B24A-7F6A-4830-9CA2-9C621771C330}.WebAssembly|Any CPU.Build.0 = WebAssembly|Any CPU {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Release|Any CPU.Build.0 = Release|Any CPU - {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.WebAssembly|Any CPU.ActiveCfg = WebAssembly|Any CPU - {19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.WebAssembly|Any CPU.Build.0 = WebAssembly|Any CPU {2E8C6889-37CF-4C8D-88B1-505547F25098}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2E8C6889-37CF-4C8D-88B1-505547F25098}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E8C6889-37CF-4C8D-88B1-505547F25098}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E8C6889-37CF-4C8D-88B1-505547F25098}.Release|Any CPU.Build.0 = Release|Any CPU - {2E8C6889-37CF-4C8D-88B1-505547F25098}.WebAssembly|Any CPU.ActiveCfg = Debug|Any CPU - {2E8C6889-37CF-4C8D-88B1-505547F25098}.WebAssembly|Any CPU.Build.0 = Debug|Any CPU {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.Debug|Any CPU.Build.0 = Debug|Any CPU {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.Release|Any CPU.ActiveCfg = Release|Any CPU {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.Release|Any CPU.Build.0 = Release|Any CPU - {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.WebAssembly|Any CPU.ActiveCfg = Debug|Any CPU - {823B556D-8D4E-4BB8-A65A-C4EB5E7E7424}.WebAssembly|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 679076cb237c3402d7935f227ac39ec088886f66 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 2 Apr 2020 12:12:02 -0400 Subject: [PATCH 102/265] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5637d10d..0b4c247c 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,11 @@ Please note that this project is governed by the **[.NET Foundation Contributor **To get started with Oqtane:** - 1. Install **[.NET Core 3.1 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.1)**. + 1. Install **[.NET Core 3.2 SDK (v3.1.3](https://dotnet.microsoft.com/download/dotnet-core/3.1)**. - 2. Install the latest edition of [Visual Studio 2019](https://visualstudio.com/vs/) (version 16.4 or higher) with the **ASP.NET and web development** workload. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**. + 2. Install the Preview edition of [Visual Studio 2019](https://visualstudio.microsoft.com/vs/preview/) (version 16.6 or higher) with the **ASP.NET and web development** workload. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**. - 3. Download or Clone the Oqtane source code to your local system. Open the **Oqtane.sln** solution file. If you want to develop using **server-side** Blazor (which includes a full debugging experience in Visual Studio) you should choose to Build the solution using the default Debug configuration. If you want to develop using **client-side** Blazor (WebAssembly) you should first choose the "Wasm" configuration option in the Visual Studio toolbar and then Build. + 3. Download or Clone the Oqtane source code to your local system. Open the **Oqtane.sln** solution file and Build the solution. NOTE: If you have already installed a previous version of Oqtane and you wish to install a newer version, there is currently no upgrade path from one version to the next. The recommended upgrade approach is to get the latest code and build it, and then reset the DefaultConnection value to "" in the appsettings.json file in the Oqtane.server project. This will trigger a re-install when you run the application which will execute the latest database scripts. From 7492b018e35ff73526447acf9919525fce99b928 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 2 Apr 2020 12:12:20 -0400 Subject: [PATCH 103/265] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b4c247c..0d10315f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Please note that this project is governed by the **[.NET Foundation Contributor **To get started with Oqtane:** - 1. Install **[.NET Core 3.2 SDK (v3.1.3](https://dotnet.microsoft.com/download/dotnet-core/3.1)**. + 1. Install **[.NET Core 3.2 SDK (v3.1.3)](https://dotnet.microsoft.com/download/dotnet-core/3.1)**. 2. Install the Preview edition of [Visual Studio 2019](https://visualstudio.microsoft.com/vs/preview/) (version 16.6 or higher) with the **ASP.NET and web development** workload. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**. From 2433cc06be650fb082418561d3d68e9fa5d4ff9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Vesel=C3=BD?= Date: Fri, 3 Apr 2020 17:36:59 +0200 Subject: [PATCH 104/265] Tenant repository bug (#329) Database Manager bug --- Oqtane.Server/Infrastructure/DatabaseManager.cs | 8 +++++++- Oqtane.Server/Repository/TenantRepository.cs | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 1b7c5dd2..2d23f219 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -63,6 +63,7 @@ namespace Oqtane.Infrastructure if (_isInstalled && !IsDefaultSiteInstalled(defaultConnectionString)) { BuildDefaultSite(password,email); + } } @@ -319,10 +320,13 @@ namespace Oqtane.Infrastructure var userRoles = scope.ServiceProvider.GetRequiredService(); var folders = scope.ServiceProvider.GetRequiredService(); var identityUserManager = scope.ServiceProvider.GetRequiredService>(); + var tenants = scope.ServiceProvider.GetRequiredService(); + + var tenant = tenants.GetTenants().First(); var site = new Site { - TenantId = -1, + TenantId = tenant.TenantId, Name = "Default Site", LogoFileId = null, DefaultThemeType = GetInstallationConfig(SettingKeys.DefaultThemeKey, Constants.DefaultTheme), @@ -341,6 +345,8 @@ namespace Oqtane.Infrastructure DisplayName = GetInstallationConfig(SettingKeys.HostUserKey, Constants.HostUser), }; CreateHostUser(folders, userRoles, roles, users, identityUserManager, user); + tenant.IsInitialized = true; + tenants.UpdateTenant(tenant); } } diff --git a/Oqtane.Server/Repository/TenantRepository.cs b/Oqtane.Server/Repository/TenantRepository.cs index 0689f3c2..6c22ba75 100644 --- a/Oqtane.Server/Repository/TenantRepository.cs +++ b/Oqtane.Server/Repository/TenantRepository.cs @@ -38,9 +38,9 @@ namespace Oqtane.Repository public Tenant UpdateTenant(Tenant tenant) { - var oldTenant = GetTenant(tenant.TenantId); + var oldTenant =_db.Tenant.AsNoTracking().FirstOrDefault(t=> t.TenantId == tenant.TenantId); - if (oldTenant.Name.Equals(Constants.MasterTenant, StringComparison.OrdinalIgnoreCase) && !oldTenant.Name.Equals(tenant.Name)) + if (oldTenant != null && (oldTenant.Name.Equals(Constants.MasterTenant, StringComparison.OrdinalIgnoreCase) && !oldTenant.Name.Equals(tenant.Name))) { throw new InvalidOperationException("Unable to rename the master tenant."); } From 7786cd027be6f3a867bcfa0744d6be010492bd79 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Fri, 3 Apr 2020 19:44:54 +0300 Subject: [PATCH 105/265] Use string Interpolation for constructing Urls (#324) --- Oqtane.Client/Services/AliasService.cs | 8 +++---- Oqtane.Client/Services/FileService.cs | 22 +++++++++++-------- Oqtane.Client/Services/FolderService.cs | 22 ++++++++++++------- Oqtane.Client/Services/InstallationService.cs | 4 ++-- Oqtane.Client/Services/JobLogService.cs | 6 ++--- Oqtane.Client/Services/JobService.cs | 10 ++++----- Oqtane.Client/Services/LogService.cs | 4 ++-- .../Services/ModuleDefinitionService.cs | 16 +++++++------- Oqtane.Client/Services/ModuleService.cs | 12 +++++----- Oqtane.Client/Services/NotificationService.cs | 10 ++++----- Oqtane.Client/Services/PackageService.cs | 4 ++-- Oqtane.Client/Services/PageModuleService.cs | 10 ++++----- Oqtane.Client/Services/PageService.cs | 19 +++++++++------- Oqtane.Client/Services/ProfileService.cs | 8 +++---- Oqtane.Client/Services/RoleService.cs | 8 +++---- Oqtane.Client/Services/ServiceBase.cs | 9 ++++---- Oqtane.Client/Services/SettingService.cs | 14 +++++++----- Oqtane.Client/Services/SiteService.cs | 6 ++--- Oqtane.Client/Services/TenantService.cs | 6 ++--- Oqtane.Client/Services/ThemeService.cs | 8 +++---- Oqtane.Client/Services/UserRoleService.cs | 8 +++---- Oqtane.Client/Services/UserService.cs | 18 +++++++-------- 22 files changed, 124 insertions(+), 108 deletions(-) diff --git a/Oqtane.Client/Services/AliasService.cs b/Oqtane.Client/Services/AliasService.cs index 82b26061..d7fdf3e6 100644 --- a/Oqtane.Client/Services/AliasService.cs +++ b/Oqtane.Client/Services/AliasService.cs @@ -36,7 +36,7 @@ namespace Oqtane.Services public async Task GetAliasAsync(int aliasId) { - return await _http.GetJsonAsync(Apiurl + "/" + aliasId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{aliasId.ToString()}"); } public async Task GetAliasAsync(string url, DateTime lastSyncDate) @@ -51,7 +51,7 @@ namespace Oqtane.Services { name = name.Substring(0, name.Length - 1); } - return await _http.GetJsonAsync(Apiurl + "/name/" + WebUtility.UrlEncode(name) + "?lastsyncdate=" + lastSyncDate.ToString("yyyyMMddHHmmssfff")); + return await _http.GetJsonAsync($"{Apiurl}/name/{WebUtility.UrlEncode(name)}?lastsyncdate={lastSyncDate.ToString("yyyyMMddHHmmssfff")}"); } public async Task AddAliasAsync(Alias alias) @@ -61,11 +61,11 @@ namespace Oqtane.Services public async Task UpdateAliasAsync(Alias alias) { - return await _http.PutJsonAsync(Apiurl + "/" + alias.AliasId.ToString(), alias); + return await _http.PutJsonAsync($"{Apiurl}/{alias.AliasId.ToString()}", alias); } public async Task DeleteAliasAsync(int aliasId) { - await _http.DeleteAsync(Apiurl + "/" + aliasId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{aliasId.ToString()}"); } } } diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index d3f419ee..3a875ac4 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -39,19 +39,24 @@ namespace Oqtane.Services public async Task> GetFilesAsync(string folder) { - return await _http.GetJsonAsync>(Apiurl + "?folder=" + folder); + return await _http.GetJsonAsync>($"{Apiurl}?folder={folder}"); } public async Task> GetFilesAsync(int siteId, string folderPath) { - if (!folderPath.EndsWith("\\")) folderPath += "\\"; + if (!folderPath.EndsWith("\\")) + { + folderPath += "\\"; + } + var path = WebUtility.UrlEncode(folderPath); + return await _http.GetJsonAsync>($"{Apiurl}/{siteId}/{path}"); } public async Task GetFileAsync(int fileId) { - return await _http.GetJsonAsync(Apiurl + "/" + fileId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{fileId.ToString()}"); } public async Task AddFileAsync(File file) @@ -61,18 +66,17 @@ namespace Oqtane.Services public async Task UpdateFileAsync(File file) { - return await _http.PutJsonAsync(Apiurl + "/" + file.FileId.ToString(), file); + return await _http.PutJsonAsync($"{Apiurl}/{file.FileId.ToString()}", file); } public async Task DeleteFileAsync(int fileId) { - await _http.DeleteAsync(Apiurl + "/" + fileId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{fileId.ToString()}"); } public async Task UploadFileAsync(string url, int folderId) { - return await _http.GetJsonAsync(Apiurl + "/upload?url=" + WebUtility.UrlEncode(url) + "&folderid=" + - folderId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/upload?url={WebUtility.UrlEncode(url)}&folderid={folderId.ToString()}"); } public async Task UploadFilesAsync(int folderId, string[] files, string id) @@ -85,7 +89,7 @@ namespace Oqtane.Services string result = ""; var interop = new Interop(_jsRuntime); - await interop.UploadFiles(Apiurl + "/upload", folder, id); + await interop.UploadFiles($"{Apiurl}/upload", folder, id); // uploading files is asynchronous so we need to wait for the upload to complete bool success = false; @@ -122,7 +126,7 @@ namespace Oqtane.Services public async Task DownloadFileAsync(int fileId) { - return await _http.GetByteArrayAsync(Apiurl + "/download/" + fileId.ToString()); + return await _http.GetByteArrayAsync($"{Apiurl}/download/{fileId.ToString()}"); } } } diff --git a/Oqtane.Client/Services/FolderService.cs b/Oqtane.Client/Services/FolderService.cs index 3c80957c..642aeafa 100644 --- a/Oqtane.Client/Services/FolderService.cs +++ b/Oqtane.Client/Services/FolderService.cs @@ -28,20 +28,25 @@ namespace Oqtane.Services public async Task> GetFoldersAsync(int siteId) { - List folders = await _http.GetJsonAsync>(ApiUrl + "?siteid=" + siteId.ToString()); + List folders = await _http.GetJsonAsync>($"{ApiUrl}?siteid={siteId.ToString()}"); folders = GetFoldersHierarchy(folders); return folders; } public async Task GetFolderAsync(int folderId) { - return await _http.GetJsonAsync(ApiUrl + "/" + folderId.ToString()); + return await _http.GetJsonAsync($"{ApiUrl}/{folderId.ToString()}"); } public async Task GetFolderAsync(int siteId, [NotNull] string folderPath) { - if (!folderPath.EndsWith("\\")) folderPath += "\\"; + if (!folderPath.EndsWith("\\")) + { + folderPath += "\\"; + } + var path = WebUtility.UrlEncode(folderPath); + return await _http.GetJsonAsync($"{ApiUrl}/{siteId}/{path}"); } @@ -52,19 +57,20 @@ namespace Oqtane.Services public async Task UpdateFolderAsync(Folder folder) { - return await _http.PutJsonAsync(ApiUrl + "/" + folder.FolderId.ToString(), folder); + return await _http.PutJsonAsync($"{ApiUrl}/{folder.FolderId.ToString()}", folder); } public async Task UpdateFolderOrderAsync(int siteId, int folderId, int? parentId) { - await _http.PutJsonAsync( - ApiUrl + "/?siteid=" + siteId.ToString() + "&folderid=" + folderId.ToString() + "&parentid=" + - ((parentId == null) ? "" : parentId.ToString()), null); + var parent = parentId == null + ? string.Empty + : parentId.ToString(); + await _http.PutJsonAsync($"{ApiUrl}/?siteid={siteId.ToString()}&folderid={folderId.ToString()}&parentid={parent}", null); } public async Task DeleteFolderAsync(int folderId) { - await _http.DeleteAsync(ApiUrl + "/" + folderId.ToString()); + await _http.DeleteAsync($"{ApiUrl}/{folderId.ToString()}"); } private static List GetFoldersHierarchy(List folders) diff --git a/Oqtane.Client/Services/InstallationService.cs b/Oqtane.Client/Services/InstallationService.cs index 0a11650b..40da3010 100644 --- a/Oqtane.Client/Services/InstallationService.cs +++ b/Oqtane.Client/Services/InstallationService.cs @@ -23,7 +23,7 @@ namespace Oqtane.Services public async Task IsInstalled() { - return await _http.GetJsonAsync(ApiUrl + "/installed"); + return await _http.GetJsonAsync($"{ApiUrl}/installed"); } public async Task Install(InstallConfig config) @@ -33,7 +33,7 @@ namespace Oqtane.Services public async Task Upgrade() { - return await _http.GetJsonAsync(ApiUrl + "/upgrade"); + return await _http.GetJsonAsync($"{ApiUrl}/upgrade"); } } } diff --git a/Oqtane.Client/Services/JobLogService.cs b/Oqtane.Client/Services/JobLogService.cs index e1bd8bfa..f564f46c 100644 --- a/Oqtane.Client/Services/JobLogService.cs +++ b/Oqtane.Client/Services/JobLogService.cs @@ -34,7 +34,7 @@ namespace Oqtane.Services public async Task GetJobLogAsync(int jobLogId) { - return await _http.GetJsonAsync(Apiurl + "/" + jobLogId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{jobLogId.ToString()}"); } public async Task AddJobLogAsync(JobLog joblog) @@ -44,11 +44,11 @@ namespace Oqtane.Services public async Task UpdateJobLogAsync(JobLog joblog) { - return await _http.PutJsonAsync(Apiurl + "/" + joblog.JobLogId.ToString(), joblog); + return await _http.PutJsonAsync($"{Apiurl}/{joblog.JobLogId.ToString()}", joblog); } public async Task DeleteJobLogAsync(int jobLogId) { - await _http.DeleteAsync(Apiurl + "/" + jobLogId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{jobLogId.ToString()}"); } } } diff --git a/Oqtane.Client/Services/JobService.cs b/Oqtane.Client/Services/JobService.cs index 87a2d70c..b06b2fd7 100644 --- a/Oqtane.Client/Services/JobService.cs +++ b/Oqtane.Client/Services/JobService.cs @@ -34,7 +34,7 @@ namespace Oqtane.Services public async Task GetJobAsync(int jobId) { - return await _http.GetJsonAsync(Apiurl + "/" + jobId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{jobId.ToString()}"); } public async Task AddJobAsync(Job job) @@ -44,21 +44,21 @@ namespace Oqtane.Services public async Task UpdateJobAsync(Job job) { - return await _http.PutJsonAsync(Apiurl + "/" + job.JobId.ToString(), job); + return await _http.PutJsonAsync($"{Apiurl}/{job.JobId.ToString()}", job); } public async Task DeleteJobAsync(int jobId) { - await _http.DeleteAsync(Apiurl + "/" + jobId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{jobId.ToString()}"); } public async Task StartJobAsync(int jobId) { - await _http.GetAsync(Apiurl + "/start/" + jobId.ToString()); + await _http.GetAsync($"{Apiurl}/start/{jobId.ToString()}"); } public async Task StopJobAsync(int jobId) { - await _http.GetAsync(Apiurl + "/stop/" + jobId.ToString()); + await _http.GetAsync($"{Apiurl}/stop/{jobId.ToString()}"); } } } diff --git a/Oqtane.Client/Services/LogService.cs b/Oqtane.Client/Services/LogService.cs index 95f718b1..a679ee3d 100644 --- a/Oqtane.Client/Services/LogService.cs +++ b/Oqtane.Client/Services/LogService.cs @@ -30,12 +30,12 @@ namespace Oqtane.Services public async Task> GetLogsAsync(int siteId, string level, string function, int rows) { - return await _http.GetJsonAsync>(Apiurl + "?siteid=" + siteId.ToString() + "&level=" + level + "&function=" + function + "&rows=" + rows.ToString()); + return await _http.GetJsonAsync>($"{Apiurl}?siteid={siteId.ToString()}&level={level}&function={function}&rows={rows.ToString()}"); } public async Task GetLogAsync(int logId) { - return await _http.GetJsonAsync(Apiurl + "/" + logId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{logId.ToString()}"); } public async Task Log(int? pageId, int? moduleId, int? userId, string category, string feature, LogFunction function, LogLevel level, Exception exception, string message, params object[] args) diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index cc537e86..119aa9dd 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -31,28 +31,28 @@ namespace Oqtane.Services public async Task> GetModuleDefinitionsAsync(int siteId) { - List moduledefinitions = await _http.GetJsonAsync>(Apiurl + "?siteid=" + siteId.ToString()); + List moduledefinitions = await _http.GetJsonAsync>($"{Apiurl}?siteid={siteId.ToString()}"); return moduledefinitions.OrderBy(item => item.Name).ToList(); } public async Task GetModuleDefinitionAsync(int moduleDefinitionId, int siteId) { - return await _http.GetJsonAsync(Apiurl + "/" + moduleDefinitionId.ToString() + "?siteid=" + siteId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{moduleDefinitionId.ToString()}?siteid={siteId.ToString()}"); } public async Task UpdateModuleDefinitionAsync(ModuleDefinition moduleDefinition) { - await _http.PutJsonAsync(Apiurl + "/" + moduleDefinition.ModuleDefinitionId.ToString(), moduleDefinition); + await _http.PutJsonAsync($"{Apiurl}/{moduleDefinition.ModuleDefinitionId.ToString()}", moduleDefinition); } public async Task InstallModuleDefinitionsAsync() { - await _http.GetJsonAsync>(Apiurl + "/install"); + await _http.GetJsonAsync>($"{Apiurl}/install"); } public async Task DeleteModuleDefinitionAsync(int moduleDefinitionId, int siteId) { - await _http.DeleteAsync(Apiurl + "/" + moduleDefinitionId.ToString() + "?siteid=" + siteId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{moduleDefinitionId.ToString()}?siteid={siteId.ToString()}"); } public async Task LoadModuleDefinitionsAsync(int siteId, Runtime runtime) @@ -77,7 +77,7 @@ namespace Oqtane.Services 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"); + var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{assemblyname}.dll"); Assembly.Load(bytes); } } @@ -86,7 +86,7 @@ namespace Oqtane.Services 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"); + var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{moduledefinition.AssemblyName}.dll"); Assembly.Load(bytes); } } @@ -94,7 +94,7 @@ namespace Oqtane.Services } public async Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId) { - await _http.PostJsonAsync(Apiurl + "?moduleid=" + moduleId.ToString(), moduleDefinition); + await _http.PostJsonAsync($"{Apiurl}?moduleid={moduleId.ToString()}", moduleDefinition); } } } diff --git a/Oqtane.Client/Services/ModuleService.cs b/Oqtane.Client/Services/ModuleService.cs index 2393abaa..c78403e4 100644 --- a/Oqtane.Client/Services/ModuleService.cs +++ b/Oqtane.Client/Services/ModuleService.cs @@ -28,7 +28,7 @@ namespace Oqtane.Services public async Task> GetModulesAsync(int siteId) { - List modules = await _http.GetJsonAsync>(Apiurl + "?siteid=" + siteId.ToString()); + List modules = await _http.GetJsonAsync>($"{Apiurl}?siteid={siteId.ToString()}"); modules = modules .OrderBy(item => item.Order) .ToList(); @@ -37,7 +37,7 @@ namespace Oqtane.Services public async Task GetModuleAsync(int moduleId) { - return await _http.GetJsonAsync(Apiurl + "/" + moduleId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{moduleId.ToString()}"); } public async Task AddModuleAsync(Module module) @@ -47,22 +47,22 @@ namespace Oqtane.Services public async Task UpdateModuleAsync(Module module) { - return await _http.PutJsonAsync(Apiurl + "/" + module.ModuleId.ToString(), module); + return await _http.PutJsonAsync($"{Apiurl}/{module.ModuleId.ToString()}", module); } public async Task DeleteModuleAsync(int moduleId) { - await _http.DeleteAsync(Apiurl + "/" + moduleId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{moduleId.ToString()}"); } public async Task ImportModuleAsync(int moduleId, string content) { - return await _http.PostJsonAsync(Apiurl + "/import?moduleid=" + moduleId, content); + return await _http.PostJsonAsync($"{Apiurl}/import?moduleid={moduleId}", content); } public async Task ExportModuleAsync(int moduleId) { - return await _http.GetStringAsync(Apiurl + "/export?moduleid=" + moduleId.ToString()); + return await _http.GetStringAsync($"{Apiurl}/export?moduleid={moduleId.ToString()}"); } } } diff --git a/Oqtane.Client/Services/NotificationService.cs b/Oqtane.Client/Services/NotificationService.cs index 1e361e24..6239ca8a 100644 --- a/Oqtane.Client/Services/NotificationService.cs +++ b/Oqtane.Client/Services/NotificationService.cs @@ -28,14 +28,14 @@ namespace Oqtane.Services public async Task> GetNotificationsAsync(int siteId, string direction, int userId) { - string querystring = "?siteid=" + siteId.ToString() + "&direction=" + direction.ToLower() + "&userid=" + userId.ToString(); - List notifications = await _http.GetJsonAsync>(Apiurl + querystring); + var notifications = await _http.GetJsonAsync>($"{Apiurl}? siteid={siteId.ToString()}&direction={direction.ToLower()}&userid={userId.ToString()}"); + return notifications.OrderByDescending(item => item.CreatedOn).ToList(); } public async Task GetNotificationAsync(int notificationId) { - return await _http.GetJsonAsync(Apiurl + "/" + notificationId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{notificationId.ToString()}"); } public async Task AddNotificationAsync(Notification notification) @@ -45,11 +45,11 @@ namespace Oqtane.Services public async Task UpdateNotificationAsync(Notification notification) { - return await _http.PutJsonAsync(Apiurl + "/" + notification.NotificationId.ToString(), notification); + return await _http.PutJsonAsync($"{Apiurl}/{notification.NotificationId.ToString()}", notification); } public async Task DeleteNotificationAsync(int notificationId) { - await _http.DeleteAsync(Apiurl + "/" + notificationId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{notificationId.ToString()}"); } } } diff --git a/Oqtane.Client/Services/PackageService.cs b/Oqtane.Client/Services/PackageService.cs index 61c277e1..952ea4cb 100644 --- a/Oqtane.Client/Services/PackageService.cs +++ b/Oqtane.Client/Services/PackageService.cs @@ -28,13 +28,13 @@ namespace Oqtane.Services public async Task> GetPackagesAsync(string tag) { - List packages = await _http.GetJsonAsync>(Apiurl + "?tag=" + tag); + List packages = await _http.GetJsonAsync>($"{Apiurl}?tag={tag}"); return packages.OrderByDescending(item => item.Downloads).ToList(); } public async Task DownloadPackageAsync(string packageId, string version, string folder) { - await _http.PostJsonAsync(Apiurl + "?packageid=" + packageId + "&version=" + version + "&folder=" + folder, null); + await _http.PostJsonAsync($"{Apiurl}?packageid={packageId}&version={version}&folder={folder}", null); } } } diff --git a/Oqtane.Client/Services/PageModuleService.cs b/Oqtane.Client/Services/PageModuleService.cs index fdf610ed..11fece8c 100644 --- a/Oqtane.Client/Services/PageModuleService.cs +++ b/Oqtane.Client/Services/PageModuleService.cs @@ -26,12 +26,12 @@ namespace Oqtane.Services public async Task GetPageModuleAsync(int pageModuleId) { - return await _http.GetJsonAsync(Apiurl + "/" + pageModuleId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{pageModuleId.ToString()}"); } public async Task GetPageModuleAsync(int pageId, int moduleId) { - return await _http.GetJsonAsync(Apiurl + "/" + pageId.ToString() + "/" + moduleId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{pageId.ToString()}/{moduleId.ToString()}"); } public async Task AddPageModuleAsync(PageModule pageModule) @@ -41,17 +41,17 @@ namespace Oqtane.Services public async Task UpdatePageModuleAsync(PageModule pageModule) { - return await _http.PutJsonAsync(Apiurl + "/" + pageModule.PageModuleId.ToString(), pageModule); + return await _http.PutJsonAsync($"{Apiurl}/{pageModule.PageModuleId.ToString()}", pageModule); } public async Task UpdatePageModuleOrderAsync(int pageId, string pane) { - await _http.PutJsonAsync(Apiurl + "/?pageid=" + pageId.ToString() + "&pane=" + pane, null); + await _http.PutJsonAsync($"{Apiurl}/?pageid={pageId.ToString()}&pane={pane}", null); } public async Task DeletePageModuleAsync(int pageModuleId) { - await _http.DeleteAsync(Apiurl + "/" + pageModuleId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{pageModuleId.ToString()}"); } } } diff --git a/Oqtane.Client/Services/PageService.cs b/Oqtane.Client/Services/PageService.cs index 7479c501..6cc832c7 100644 --- a/Oqtane.Client/Services/PageService.cs +++ b/Oqtane.Client/Services/PageService.cs @@ -30,26 +30,26 @@ namespace Oqtane.Services public async Task> GetPagesAsync(int siteId) { - List pages = await _http.GetJsonAsync>(Apiurl + "?siteid=" + siteId.ToString()); + List pages = await _http.GetJsonAsync>($"{Apiurl}?siteid={siteId.ToString()}"); pages = GetPagesHierarchy(pages); return pages; } public async Task GetPageAsync(int pageId) { - return await _http.GetJsonAsync(Apiurl + "/" + pageId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{pageId.ToString()}"); } public async Task GetPageAsync(int pageId, int userId) { - return await _http.GetJsonAsync(Apiurl + "/" + pageId.ToString() + "?userid=" + userId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{pageId.ToString()}?userid={userId.ToString()}"); } public async Task GetPageAsync(string path, int siteId) { try { - return await _http.GetJsonAsync(Apiurl + "/path/" + siteId.ToString() + "?path=" + WebUtility.UrlEncode(path)); + return await _http.GetJsonAsync($"{Apiurl}/path/{siteId.ToString()}?path={WebUtility.UrlEncode(path)}"); } catch { @@ -64,22 +64,25 @@ namespace Oqtane.Services public async Task AddPageAsync(int pageId, int userId) { - return await _http.PostJsonAsync(Apiurl + "/" + pageId.ToString() + "?userid=" + userId.ToString(), null); + return await _http.PostJsonAsync($"{Apiurl}/{pageId.ToString()}?userid={userId.ToString()}", null); } public async Task UpdatePageAsync(Page page) { - return await _http.PutJsonAsync(Apiurl + "/" + page.PageId.ToString(), page); + return await _http.PutJsonAsync($"{Apiurl}/{page.PageId.ToString()}", page); } public async Task UpdatePageOrderAsync(int siteId, int pageId, int? parentId) { - await _http.PutJsonAsync(Apiurl + "/?siteid=" + siteId.ToString() + "&pageid=" + pageId.ToString() + "&parentid=" + ((parentId == null) ? "" : parentId.ToString()), null); + var parent = parentId == null + ? string.Empty + : parentId.ToString(); + await _http.PutJsonAsync($"{Apiurl}/?siteid={siteId.ToString()}&pageid={pageId.ToString()}&parentid={parent}", null); } public async Task DeletePageAsync(int pageId) { - await _http.DeleteAsync(Apiurl + "/" + pageId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{pageId.ToString()}"); } private static List GetPagesHierarchy(List pages) diff --git a/Oqtane.Client/Services/ProfileService.cs b/Oqtane.Client/Services/ProfileService.cs index f2955a78..4ceb4cac 100644 --- a/Oqtane.Client/Services/ProfileService.cs +++ b/Oqtane.Client/Services/ProfileService.cs @@ -28,13 +28,13 @@ namespace Oqtane.Services public async Task> GetProfilesAsync(int siteId) { - List profiles = await _http.GetJsonAsync>(Apiurl + "?siteid=" + siteId.ToString()); + List profiles = await _http.GetJsonAsync>($"{Apiurl}?siteid={siteId.ToString()}"); return profiles.OrderBy(item => item.ViewOrder).ToList(); } public async Task GetProfileAsync(int profileId) { - return await _http.GetJsonAsync(Apiurl + "/" + profileId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{profileId.ToString()}"); } public async Task AddProfileAsync(Profile profile) @@ -44,11 +44,11 @@ namespace Oqtane.Services public async Task UpdateProfileAsync(Profile profile) { - return await _http.PutJsonAsync(Apiurl + "/" + profile.SiteId.ToString(), profile); + return await _http.PutJsonAsync($"{Apiurl}/{profile.SiteId.ToString()}", profile); } public async Task DeleteProfileAsync(int profileId) { - await _http.DeleteAsync(Apiurl + "/" + profileId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{profileId.ToString()}"); } } } diff --git a/Oqtane.Client/Services/RoleService.cs b/Oqtane.Client/Services/RoleService.cs index 7328cbbb..708f0721 100644 --- a/Oqtane.Client/Services/RoleService.cs +++ b/Oqtane.Client/Services/RoleService.cs @@ -28,13 +28,13 @@ namespace Oqtane.Services public async Task> GetRolesAsync(int siteId) { - List roles = await _http.GetJsonAsync>(Apiurl + "?siteid=" + siteId.ToString()); + List roles = await _http.GetJsonAsync>($"{Apiurl}?siteid={siteId.ToString()}"); return roles.OrderBy(item => item.Name).ToList(); } public async Task GetRoleAsync(int roleId) { - return await _http.GetJsonAsync(Apiurl + "/" + roleId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{roleId.ToString()}"); } public async Task AddRoleAsync(Role role) @@ -44,11 +44,11 @@ namespace Oqtane.Services public async Task UpdateRoleAsync(Role role) { - return await _http.PutJsonAsync(Apiurl + "/" + role.RoleId.ToString(), role); + return await _http.PutJsonAsync($"{Apiurl}/{role.RoleId.ToString()}", role); } public async Task DeleteRoleAsync(int roleId) { - await _http.DeleteAsync(Apiurl + "/" + roleId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{roleId.ToString()}"); } } } diff --git a/Oqtane.Client/Services/ServiceBase.cs b/Oqtane.Client/Services/ServiceBase.cs index f8a6e767..13b5e1c6 100644 --- a/Oqtane.Client/Services/ServiceBase.cs +++ b/Oqtane.Client/Services/ServiceBase.cs @@ -14,8 +14,8 @@ namespace Oqtane.Services if (alias != null) { // build a url which passes the alias that may include a subfolder for multi-tenancy - apiurl = uri.Scheme + "://" + alias.Name + "/"; - if (alias.Path == "") + apiurl = $"{uri.Scheme}://{alias.Name}/"; + if (alias.Path == string.Empty) { apiurl += "~/"; } @@ -23,9 +23,10 @@ namespace Oqtane.Services else { // build a url which ignores any subfolder for multi-tenancy - apiurl = uri.Scheme + "://" + uri.Authority + "/~/"; + apiurl = $"{uri.Scheme}://{uri.Authority}/~/"; } - apiurl += "api/" + serviceName; + apiurl += $"api/{serviceName}"; + return apiurl; } diff --git a/Oqtane.Client/Services/SettingService.cs b/Oqtane.Client/Services/SettingService.cs index d52878c8..22ac7fc4 100644 --- a/Oqtane.Client/Services/SettingService.cs +++ b/Oqtane.Client/Services/SettingService.cs @@ -98,8 +98,9 @@ namespace Oqtane.Services public async Task> GetSettingsAsync(string entityName, int entityId) { - Dictionary dictionary = new Dictionary(); - List settings = await _http.GetJsonAsync>(Apiurl + "?entityname=" + entityName + "&entityid=" + entityId.ToString()); + var dictionary = new Dictionary(); + var settings = await _http.GetJsonAsync>($"{Apiurl}?entityname={entityName}&entityid={entityId.ToString()}"); + foreach(Setting setting in settings.OrderBy(item => item.SettingName).ToList()) { dictionary.Add(setting.SettingName, setting.SettingValue); @@ -109,7 +110,8 @@ namespace Oqtane.Services public async Task UpdateSettingsAsync(Dictionary settings, string entityName, int entityId) { - List settingsList = await _http.GetJsonAsync>(Apiurl + "?entityname=" + entityName + "&entityid=" + entityId.ToString()); + var settingsList = await _http.GetJsonAsync>($"{Apiurl}?entityname={entityName}&entityid={entityId.ToString()}"); + foreach (KeyValuePair kvp in settings) { Setting setting = settingsList.FirstOrDefault(item => item.SettingName == kvp.Key); @@ -136,7 +138,7 @@ namespace Oqtane.Services public async Task GetSettingAsync(int settingId) { - return await _http.GetJsonAsync(Apiurl + "/" + settingId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{settingId.ToString()}"); } public async Task AddSettingAsync(Setting setting) @@ -146,12 +148,12 @@ namespace Oqtane.Services public async Task UpdateSettingAsync(Setting setting) { - return await _http.PutJsonAsync(Apiurl + "/" + setting.SettingId.ToString(), setting); + return await _http.PutJsonAsync($"{Apiurl}/{setting.SettingId.ToString()}", setting); } public async Task DeleteSettingAsync(int settingId) { - await _http.DeleteAsync(Apiurl + "/" + settingId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{settingId.ToString()}"); } diff --git a/Oqtane.Client/Services/SiteService.cs b/Oqtane.Client/Services/SiteService.cs index 0735f8e1..ce4a379c 100644 --- a/Oqtane.Client/Services/SiteService.cs +++ b/Oqtane.Client/Services/SiteService.cs @@ -34,7 +34,7 @@ namespace Oqtane.Services public async Task GetSiteAsync(int siteId, Alias alias) { - return await _http.GetJsonAsync(CreateCrossTenantUrl(Apiurl + "/" + siteId.ToString(), alias)); + return await _http.GetJsonAsync(CreateCrossTenantUrl($"{Apiurl}/{siteId.ToString()}", alias)); } public async Task AddSiteAsync(Site site, Alias alias) @@ -44,12 +44,12 @@ namespace Oqtane.Services public async Task UpdateSiteAsync(Site site, Alias alias) { - return await _http.PutJsonAsync(CreateCrossTenantUrl(Apiurl + "/" + site.SiteId.ToString(), alias), site); + return await _http.PutJsonAsync(CreateCrossTenantUrl($"{Apiurl}/{site.SiteId.ToString()}", alias), site); } public async Task DeleteSiteAsync(int siteId, Alias alias) { - await _http.DeleteAsync(CreateCrossTenantUrl(Apiurl + "/" + siteId.ToString(), alias)); + await _http.DeleteAsync(CreateCrossTenantUrl($"{Apiurl}/{siteId.ToString()}", alias)); } } } diff --git a/Oqtane.Client/Services/TenantService.cs b/Oqtane.Client/Services/TenantService.cs index 54edb128..d325fd91 100644 --- a/Oqtane.Client/Services/TenantService.cs +++ b/Oqtane.Client/Services/TenantService.cs @@ -34,7 +34,7 @@ namespace Oqtane.Services public async Task GetTenantAsync(int tenantId) { - return await _http.GetJsonAsync(Apiurl + "/" + tenantId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{tenantId.ToString()}"); } public async Task AddTenantAsync(Tenant tenant) @@ -44,12 +44,12 @@ namespace Oqtane.Services public async Task UpdateTenantAsync(Tenant tenant) { - return await _http.PutJsonAsync(Apiurl + "/" + tenant.TenantId.ToString(), tenant); + return await _http.PutJsonAsync($"{Apiurl}/{tenant.TenantId.ToString()}", tenant); } public async Task DeleteTenantAsync(int tenantId) { - await _http.DeleteAsync(Apiurl + "/" + tenantId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{tenantId.ToString()}"); } } } diff --git a/Oqtane.Client/Services/ThemeService.cs b/Oqtane.Client/Services/ThemeService.cs index e3d5e2a3..e872aae1 100644 --- a/Oqtane.Client/Services/ThemeService.cs +++ b/Oqtane.Client/Services/ThemeService.cs @@ -45,7 +45,7 @@ namespace Oqtane.Services 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"); + var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{assemblyname}.dll"); Assembly.Load(bytes); } } @@ -53,7 +53,7 @@ namespace Oqtane.Services 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"); + var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{theme.AssemblyName}.dll"); Assembly.Load(bytes); } } @@ -105,12 +105,12 @@ namespace Oqtane.Services public async Task InstallThemesAsync() { - await _http.GetJsonAsync>(Apiurl + "/install"); + await _http.GetJsonAsync>($"{Apiurl}/install"); } public async Task DeleteThemeAsync(string themeName) { - await _http.DeleteAsync(Apiurl + "/" + themeName); + await _http.DeleteAsync($"{Apiurl}/{themeName}"); } } } diff --git a/Oqtane.Client/Services/UserRoleService.cs b/Oqtane.Client/Services/UserRoleService.cs index d45a000c..66e9feb9 100644 --- a/Oqtane.Client/Services/UserRoleService.cs +++ b/Oqtane.Client/Services/UserRoleService.cs @@ -27,12 +27,12 @@ namespace Oqtane.Services public async Task> GetUserRolesAsync(int siteId) { - return await _http.GetJsonAsync>(Apiurl + "?siteid=" + siteId.ToString()); + return await _http.GetJsonAsync>($"{Apiurl}?siteid={siteId.ToString()}"); } public async Task GetUserRoleAsync(int userRoleId) { - return await _http.GetJsonAsync(Apiurl + "/" + userRoleId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{userRoleId.ToString()}"); } public async Task AddUserRoleAsync(UserRole userRole) @@ -42,12 +42,12 @@ namespace Oqtane.Services public async Task UpdateUserRoleAsync(UserRole userRole) { - return await _http.PutJsonAsync(Apiurl + "/" + userRole.UserRoleId.ToString(), userRole); + return await _http.PutJsonAsync($"{Apiurl}/{userRole.UserRoleId.ToString()}", userRole); } public async Task DeleteUserRoleAsync(int userRoleId) { - await _http.DeleteAsync(Apiurl + "/" + userRoleId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{userRoleId.ToString()}"); } } } diff --git a/Oqtane.Client/Services/UserService.cs b/Oqtane.Client/Services/UserService.cs index 43bc3ec4..d81077ff 100644 --- a/Oqtane.Client/Services/UserService.cs +++ b/Oqtane.Client/Services/UserService.cs @@ -26,12 +26,12 @@ namespace Oqtane.Services public async Task GetUserAsync(int userId, int siteId) { - return await _http.GetJsonAsync(Apiurl + "/" + userId.ToString() + "?siteid=" + siteId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/{userId.ToString()}?siteid={siteId.ToString()}"); } public async Task GetUserAsync(string username, int siteId) { - return await _http.GetJsonAsync(Apiurl + "/name/" + username + "?siteid=" + siteId.ToString()); + return await _http.GetJsonAsync($"{Apiurl}/name/{username}?siteid={siteId.ToString()}"); } public async Task AddUserAsync(User user) @@ -60,37 +60,37 @@ namespace Oqtane.Services public async Task UpdateUserAsync(User user) { - return await _http.PutJsonAsync(Apiurl + "/" + user.UserId.ToString(), user); + return await _http.PutJsonAsync($"{Apiurl}/{user.UserId.ToString()}", user); } public async Task DeleteUserAsync(int userId) { - await _http.DeleteAsync(Apiurl + "/" + userId.ToString()); + await _http.DeleteAsync($"{Apiurl}/{userId.ToString()}"); } public async Task LoginUserAsync(User user, bool setCookie, bool isPersistent) { - return await _http.PostJsonAsync(Apiurl + "/login?setcookie=" + setCookie.ToString() + "&persistent=" + isPersistent.ToString(), user); + return await _http.PostJsonAsync($"{Apiurl}/login?setcookie={setCookie.ToString()}&persistent={isPersistent.ToString()}", user); } public async Task LogoutUserAsync(User user) { // best practices recommend post is preferrable to get for logout - await _http.PostJsonAsync(Apiurl + "/logout", user); + await _http.PostJsonAsync($"{Apiurl}/logout", user); } public async Task VerifyEmailAsync(User user, string token) { - return await _http.PostJsonAsync(Apiurl + "/verify?token=" + token, user); + return await _http.PostJsonAsync($"{Apiurl}/verify?token={token}", user); } public async Task ForgotPasswordAsync(User user) { - await _http.PostJsonAsync(Apiurl + "/forgot", user); + await _http.PostJsonAsync($"{Apiurl}/forgot", user); } public async Task ResetPasswordAsync(User user, string token) { - return await _http.PostJsonAsync(Apiurl + "/reset?token=" + token, user); + return await _http.PostJsonAsync($"{Apiurl}/reset?token={token}", user); } } From c38dff5e7cb48b80d4b3515822de5dfde3e51530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Vesel=C3=BD?= Date: Fri, 3 Apr 2020 18:45:27 +0200 Subject: [PATCH 106/265] No more magic strings in module definition (#332) --- Oqtane.Server/Repository/SiteRepository.cs | 41 +++++++++++----------- Oqtane.Shared/Shared/Utilities.cs | 9 ++++- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index dc40978b..a2f7e33d 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -64,7 +64,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Dashboard, Oqtane.Client", Title = "Admin Dashboard", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Dashboard.Index).ToModuleDefinitionName(), Title = "Admin Dashboard", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -86,7 +86,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Sites, Oqtane.Client", Title = "Site Management", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sites.Index).ToModuleDefinitionName(), Title = "Site Management", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -108,7 +108,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Site, Oqtane.Client", Title = "Site Settings", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Site.Index).ToModuleDefinitionName(), Title = "Site Settings", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -130,7 +130,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Pages, Oqtane.Client", Title = "Page Management", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Pages.Index).ToModuleDefinitionName(), Title = "Page Management", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -152,7 +152,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Users, Oqtane.Client", Title = "User Management", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Users.Index).ToModuleDefinitionName(), Title = "User Management", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -174,7 +174,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Profiles, Oqtane.Client", Title = "Profile Management", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Profiles.Index).ToModuleDefinitionName(), Title = "Profile Management", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -196,7 +196,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Roles, Oqtane.Client", Title = "Role Management", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Roles.Index).ToModuleDefinitionName(), Title = "Role Management", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -218,7 +218,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Logs, Oqtane.Client", Title = "Event Log", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Logs.Index).ToModuleDefinitionName(), Title = "Event Log", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -240,7 +240,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Files, Oqtane.Client", Title = "File Management", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Files.Index).ToModuleDefinitionName(), Title = "File Management", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -262,7 +262,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.RecycleBin, Oqtane.Client", Title = "Recycle Bin", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.RecycleBin.Index).ToModuleDefinitionName(), Title = "Recycle Bin", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -284,7 +284,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Tenants, Oqtane.Client", Title = "Tenant Management", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Tenants.Index).ToModuleDefinitionName(), Title = "Tenant Management", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -306,7 +306,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.ModuleDefinitions, Oqtane.Client", Title = "Module Management", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.ModuleDefinitions.Index).ToModuleDefinitionName(), Title = "Module Management", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -328,7 +328,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Themes, Oqtane.Client", Title = "Theme Management", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Themes.Index).ToModuleDefinitionName(), Title = "Theme Management", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -350,7 +350,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Jobs, Oqtane.Client", Title = "Scheduled Jobs", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Jobs.Index).ToModuleDefinitionName(), Title = "Scheduled Jobs", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -378,7 +378,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Sql, Oqtane.Client", Title = "Sql Management", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sql.Index).ToModuleDefinitionName(), Title = "Sql Management", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -400,7 +400,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Upgrade, Oqtane.Client", Title = "Upgrade Service", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Upgrade.Index).ToModuleDefinitionName(), Title = "Upgrade Service", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -423,7 +423,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Login, Oqtane.Client", Title = "User Login", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Login.Index).ToModuleDefinitionName(), Title = "User Login", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -447,7 +447,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Register, Oqtane.Client", Title = "User Registration", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Register.Index).ToModuleDefinitionName(), Title = "User Registration", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -472,7 +472,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.Reset, Oqtane.Client", Title = "Password Reset", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Reset.Index).ToModuleDefinitionName(), Title = "Password Reset", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -496,7 +496,7 @@ namespace Oqtane.Repository { new PageTemplateModule { - ModuleDefinitionName = "Oqtane.Modules.Admin.UserProfile, Oqtane.Client", Title = "User Profile", Pane = "Content", + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.UserProfile.Index).ToModuleDefinitionName(), Title = "User Profile", Pane = "Content", ModulePermissions = _permissionRepository.EncodePermissions(new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -517,6 +517,7 @@ namespace Oqtane.Repository public Site AddSite(Site site) { + _db.Site.Add(site); _db.SaveChanges(); CreateSite(site); diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 46c02fa7..6c2825fc 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -5,8 +5,15 @@ using System.Text.RegularExpressions; namespace Oqtane.Shared { - public class Utilities + public static class Utilities { + public static string ToModuleDefinitionName(this Type type) + { + if (type == null) return null; + var assemblyFullName = type.Assembly.FullName; + var assemblyName = assemblyFullName.Substring(0, assemblyFullName.IndexOf(",", StringComparison.Ordinal)); + return $"{type.Namespace}, {assemblyName}"; + } public static string NavigateUrl(string alias, string path, string parameters) { string url = ""; From d8b15e7a4e9d85ccf4538d0b307dce6520288a48 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 3 Apr 2020 15:04:25 -0400 Subject: [PATCH 107/265] Components based on Bootstrap4 for Sections and TabStrip to increase productivity and promote uniformity in Module UIs (#333) * upgrade to .NET Core 3.2 Preview 3 and fixes for issues created by #314 * Components based on Bootstrap4 for Sections and TabStrip to increase productivity and promote uniformity in Module UIs --- .../Modules/Admin/RecycleBin/Index.razor | 130 ++++++++---------- Oqtane.Client/Modules/Admin/Site/Index.razor | 16 +-- Oqtane.Client/Modules/Controls/Label.razor | 2 +- Oqtane.Client/Modules/Controls/Section.razor | 44 ++++++ .../Modules/Controls/TabControl.razor | 44 ------ Oqtane.Client/Modules/Controls/TabPanel.razor | 24 ++-- Oqtane.Client/Modules/Controls/TabStrip.razor | 56 ++++++++ 7 files changed, 177 insertions(+), 139 deletions(-) create mode 100644 Oqtane.Client/Modules/Controls/Section.razor delete mode 100644 Oqtane.Client/Modules/Controls/TabControl.razor create mode 100644 Oqtane.Client/Modules/Controls/TabStrip.razor diff --git a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor index 27978203..d3955307 100644 --- a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor +++ b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor @@ -5,80 +5,62 @@ @inject IModuleService ModuleService @inject IPageService PageService -
    -
    - - - -
    -
    - @if (_pages == null) - { -
    -

    No Deleted Pages

    - } - else - { - -
    -   -   - Name - Deleted By - Deleted On -
    - - - - @context.Name - @context.DeletedBy - @context.DeletedOn - -
    - } -
    -
    - @if (_modules == null) - { -
    -

    No Deleted Modules

    - } - else - { - -
    -   -   - Page - Module - Deleted By - Deleted On -
    - - - - @PageState.Pages.Find(item => item.PageId == context.PageId).Name - @context.Title - @context.DeletedBy - @context.DeletedOn - -
    - } -
    -
    -
    -
    + + + @if (_pages == null) + { +
    +

    No Deleted Pages

    + } + else + { + +
    +   +   + Name + Deleted By + Deleted On +
    + + + + @context.Name + @context.DeletedBy + @context.DeletedOn + +
    + } +
    + + @if (_modules == null) + { +
    +

    No Deleted Modules

    + } + else + { + +
    +   +   + Page + Module + Deleted By + Deleted On +
    + + + + @PageState.Pages.Find(item => item.PageId == context.PageId).Name + @context.Title + @context.DeletedBy + @context.DeletedOn + +
    + } +
    +
    @code { private List _pages; diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index afcf50ab..bb188e8d 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -123,10 +123,7 @@ - -
    +
    @@ -169,12 +166,8 @@
    -
    - - -
    + +
    -
    @@ -203,9 +196,8 @@
    -
    +
    diff --git a/Oqtane.Client/Modules/Controls/Label.razor b/Oqtane.Client/Modules/Controls/Label.razor index a643e118..0826e0cb 100644 --- a/Oqtane.Client/Modules/Controls/Label.razor +++ b/Oqtane.Client/Modules/Controls/Label.razor @@ -15,7 +15,7 @@ else private string _closeLabel = ""; [Parameter] - public RenderFragment ChildContent { get; set; } // required - the title of the label + public RenderFragment ChildContent { get; set; } [Parameter] public string For { get; set; } // optional - the id of the associated input control for accessibility diff --git a/Oqtane.Client/Modules/Controls/Section.razor b/Oqtane.Client/Modules/Controls/Section.razor new file mode 100644 index 00000000..6921f933 --- /dev/null +++ b/Oqtane.Client/Modules/Controls/Section.razor @@ -0,0 +1,44 @@ +@namespace Oqtane.Modules.Controls +@inherits ModuleBase + + +
    +
    +
    +
    + @ChildContent +
    + +@code { + private string _heading = string.Empty; + private string _expanded = string.Empty; + + [Parameter] + public RenderFragment ChildContent { get; set; } + + [Parameter] + public string Name { get; set; } // required - the name of the section + + [Parameter] + public string Heading { get; set; } // optional - will default to Name if not provided + + [Parameter] + public string Expanded { get; set; } // optional - will default to false if not provided + + protected override void OnInitialized() + { + _heading = (!string.IsNullOrEmpty(Heading)) ? Heading : Name; + _expanded = (!string.IsNullOrEmpty(Expanded)) ? Expanded : "false"; + } +} diff --git a/Oqtane.Client/Modules/Controls/TabControl.razor b/Oqtane.Client/Modules/Controls/TabControl.razor deleted file mode 100644 index c65124aa..00000000 --- a/Oqtane.Client/Modules/Controls/TabControl.razor +++ /dev/null @@ -1,44 +0,0 @@ -@namespace Oqtane.Modules.Controls -@inherits ModuleBase - - -
    - @foreach (TabPanel tabPanel in _tabPanels) - { - - } -
    - @ChildContent -
    - -@code { - private List _tabPanels = new List(); - - // Next line is needed so we are able to add components inside - [Parameter] - public RenderFragment ChildContent { get; set; } - - public TabPanel ActiveTabPanel { get; set; } - - internal void AddTabPanel(TabPanel tabPanel) - { - _tabPanels.Add(tabPanel); - if (_tabPanels.Count == 1) - ActiveTabPanel = tabPanel; - StateHasChanged(); - } - - private string GetButtonClass(TabPanel tabPanel) - => tabPanel == ActiveTabPanel - ? "btn-primary" - : "btn-secondary"; - - private void ActivateTabPanel(TabPanel tabPanel) - { - ActiveTabPanel = tabPanel; - } -} diff --git a/Oqtane.Client/Modules/Controls/TabPanel.razor b/Oqtane.Client/Modules/Controls/TabPanel.razor index 475d6258..d09ff53f 100644 --- a/Oqtane.Client/Modules/Controls/TabPanel.razor +++ b/Oqtane.Client/Modules/Controls/TabPanel.razor @@ -1,27 +1,35 @@ @namespace Oqtane.Modules.Controls @inherits ModuleBase -@if (Parent.ActiveTabPanel == (TabPanel)(object)this) +@if (Name == Parent.ActiveTab) { - @ChildContent +
    + @ChildContent +
    +} +else +{ +
    + @ChildContent +
    } @code { [CascadingParameter] - private TabControl Parent { get; set; } + private TabStrip Parent { get; set; } [Parameter] public RenderFragment ChildContent { get; set; } [Parameter] - public string Text { get; set; } + public string Name { get; set; } // required - name of the TabPanel + + [Parameter] + public string Heading { get; set; } // optional - defaults to name if not specified protected override void OnInitialized() { - if (Parent == null) - throw new ArgumentNullException(nameof(Parent), "TabPanel must exist within a TabControl"); - base.OnInitialized(); - Parent.AddTabPanel((TabPanel)(object)this); + Parent.AddTabPanel((TabPanel)this); } } diff --git a/Oqtane.Client/Modules/Controls/TabStrip.razor b/Oqtane.Client/Modules/Controls/TabStrip.razor new file mode 100644 index 00000000..10527d0f --- /dev/null +++ b/Oqtane.Client/Modules/Controls/TabStrip.razor @@ -0,0 +1,56 @@ +@namespace Oqtane.Modules.Controls +@inherits ModuleBase + + +
    +
    + +
    +
    + @ChildContent +
    +
    +
    +
    + +@code { + private List _tabPanels = new List(); + + [Parameter] + public RenderFragment ChildContent { get; set; } // contains the TabPanels + + [Parameter] + public string ActiveTab { get; set; } // optional - defaults to first TabPanel if not specified + + internal void AddTabPanel(TabPanel tabPanel) + { + _tabPanels.Add(tabPanel); + if (string.IsNullOrEmpty(ActiveTab)) + { + ActiveTab = tabPanel.Name; + } + } + + private string DisplayHeading(string Name, string Heading) + { + return (string.IsNullOrEmpty(Heading)) ? Name : Heading; + } +} From 6fa2a38f363abc83ec23ccd27c3dd7c42d97d093 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Fri, 3 Apr 2020 23:37:02 +0300 Subject: [PATCH 108/265] Fix regression bugs (#334) --- Oqtane.Client/Modules/Admin/Pages/Add.razor | 4 ++-- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 4 ++-- Oqtane.Client/Modules/Admin/Site/Index.razor | 6 +++--- Oqtane.Client/Modules/Admin/Sites/Add.razor | 8 ++++---- Oqtane.Client/Modules/Admin/Sites/Edit.razor | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index f8be514c..483cc107 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -118,7 +118,7 @@ - + @foreach (KeyValuePair panelayout in _panelayouts) { diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index a1c964f8..2b13870e 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -129,7 +129,7 @@ - + @foreach (KeyValuePair panelayout in _panelayouts) { diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index bb188e8d..b20ccd1d 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -56,7 +56,7 @@ - + @foreach (KeyValuePair panelayout in _panelayouts) { @@ -91,7 +91,7 @@ - + @foreach (KeyValuePair item in _themes) { @@ -65,7 +65,7 @@ else - + @foreach (KeyValuePair container in _containers) { @@ -93,7 +93,7 @@ else - + @foreach (KeyValuePair item in _themes) { if (item.Key == _themetype) @@ -60,7 +60,7 @@ - + @foreach (KeyValuePair container in _containers) { From 71bd3a8d6aa6acd4d683d91a1c9e0a34ab55dfbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Vesel=C3=BD?= Date: Fri, 3 Apr 2020 23:18:33 +0200 Subject: [PATCH 109/265] Namespace fix (#335) --- Oqtane.Client/App.razor | 3 ++- .../Modules/Admin/Dashboard/Index.razor | 1 + Oqtane.Client/Modules/Admin/Error/Index.razor | 1 + Oqtane.Client/Modules/Admin/Files/Add.razor | 1 + Oqtane.Client/Modules/Admin/Files/Edit.razor | 1 + Oqtane.Client/Modules/Admin/Files/Index.razor | 1 + Oqtane.Client/Modules/Admin/Jobs/Add.razor | 1 + Oqtane.Client/Modules/Admin/Jobs/Edit.razor | 1 + Oqtane.Client/Modules/Admin/Jobs/Index.razor | 1 + Oqtane.Client/Modules/Admin/Jobs/Log.razor | 1 + Oqtane.Client/Modules/Admin/Login/Index.razor | 1 + Oqtane.Client/Modules/Admin/Logs/Detail.razor | 1 + Oqtane.Client/Modules/Admin/Logs/Index.razor | 1 + .../Modules/Admin/ModuleCreator/Index.razor | 1 + .../Modules/Admin/ModuleDefinitions/Add.razor | 1 + .../Admin/ModuleDefinitions/Edit.razor | 1 + .../Admin/ModuleDefinitions/Index.razor | 1 + .../Modules/Admin/Modules/Export.razor | 1 + .../Modules/Admin/Modules/Import.razor | 1 + .../Modules/Admin/Modules/Settings.razor | 1 + Oqtane.Client/Modules/Admin/Pages/Add.razor | 1 + Oqtane.Client/Modules/Admin/Pages/Edit.razor | 1 + Oqtane.Client/Modules/Admin/Pages/Index.razor | 1 + .../Modules/Admin/Profiles/Edit.razor | 1 + .../Modules/Admin/Profiles/Index.razor | 1 + .../Modules/Admin/RecycleBin/Index.razor | 1 + .../Modules/Admin/Register/Index.razor | 1 + Oqtane.Client/Modules/Admin/Reset/Index.razor | 1 + Oqtane.Client/Modules/Admin/Roles/Add.razor | 1 + Oqtane.Client/Modules/Admin/Roles/Edit.razor | 1 + Oqtane.Client/Modules/Admin/Roles/Index.razor | 1 + Oqtane.Client/Modules/Admin/Site/Index.razor | 1 + Oqtane.Client/Modules/Admin/Sites/Add.razor | 1 + Oqtane.Client/Modules/Admin/Sites/Edit.razor | 1 + Oqtane.Client/Modules/Admin/Sites/Index.razor | 1 + Oqtane.Client/Modules/Admin/Sql/Index.razor | 1 + Oqtane.Client/Modules/Admin/Tenants/Add.razor | 1 + .../Modules/Admin/Tenants/Edit.razor | 1 + .../Modules/Admin/Tenants/Index.razor | 1 + Oqtane.Client/Modules/Admin/Themes/Add.razor | 1 + .../Modules/Admin/Themes/Index.razor | 1 + .../Modules/Admin/Upgrade/Index.razor | 1 + .../Modules/Admin/UserProfile/Add.razor | 1 + .../Modules/Admin/UserProfile/Index.razor | 1 + .../Modules/Admin/UserProfile/View.razor | 1 + Oqtane.Client/Modules/Admin/Users/Add.razor | 1 + Oqtane.Client/Modules/Admin/Users/Edit.razor | 1 + Oqtane.Client/Modules/Admin/Users/Index.razor | 1 + Oqtane.Client/Modules/Admin/Users/Roles.razor | 1 + .../Modules/Controls/ActionDialog.razor | 1 + .../Modules/Controls/ActionLink.razor | 1 + Oqtane.Client/Modules/HtmlText/Edit.razor | 4 ++-- Oqtane.Client/Modules/HtmlText/Index.razor | 1 - .../HtmlText/Services/HtmlTextService.cs | 2 +- .../HtmlText/Services/IHtmlTextService.cs | 2 +- Oqtane.Client/Modules/IModuleControl.cs | 3 ++- Oqtane.Client/Modules/ModuleBase.cs | 1 + Oqtane.Client/Modules/Weather/Index.razor | 3 ++- .../Modules/Weather/Models/WeatherForecast.cs | 2 +- .../Services/IWeatherForecastService.cs | 1 + .../Services/WeatherForecastService.cs | 1 + Oqtane.Client/Program.cs | 23 +++++++++++-------- Oqtane.Client/Services/AliasService.cs | 1 + Oqtane.Client/Services/FileService.cs | 1 + Oqtane.Client/Services/FolderService.cs | 1 + Oqtane.Client/Services/InstallationService.cs | 1 + .../Services/Interfaces/IAliasService.cs | 6 ++--- .../Services/Interfaces/IFileService.cs | 6 ++--- .../Services/Interfaces/IFolderService.cs | 6 ++--- .../Interfaces/IInstallationService.cs | 6 ++--- .../Services/Interfaces/IJobLogService.cs | 6 ++--- .../Services/Interfaces/IJobService.cs | 6 ++--- .../Services/Interfaces/ILogService.cs | 8 +++---- .../Interfaces/IModuleDefinitionService.cs | 8 +++---- .../Services/Interfaces/IModuleService.cs | 6 ++--- .../Interfaces/INotificationService.cs | 6 ++--- .../Services/Interfaces/IPackageService.cs | 6 ++--- .../Services/Interfaces/IPageModuleService.cs | 6 ++--- .../Services/Interfaces/IPageService.cs | 6 ++--- .../Services/Interfaces/IProfileService.cs | 6 ++--- .../Services/Interfaces/IRoleService.cs | 6 ++--- .../Services/Interfaces/ISettingService.cs | 6 ++--- .../Services/Interfaces/ISiteService.cs | 6 ++--- .../Interfaces/ISiteTemplateService.cs | 6 ++--- .../Services/Interfaces/ISqlService.cs | 6 ++--- .../Services/Interfaces/ITenantService.cs | 6 ++--- .../Services/Interfaces/IThemeService.cs | 6 ++--- .../Services/Interfaces/IUserRoleService.cs | 6 ++--- .../Services/Interfaces/IUserService.cs | 6 ++--- Oqtane.Client/Services/JobLogService.cs | 1 + Oqtane.Client/Services/JobService.cs | 1 + Oqtane.Client/Services/LogService.cs | 1 + .../Services/ModuleDefinitionService.cs | 1 + Oqtane.Client/Services/ModuleService.cs | 1 + Oqtane.Client/Services/NotificationService.cs | 1 + Oqtane.Client/Services/PackageService.cs | 1 + Oqtane.Client/Services/PageModuleService.cs | 1 + Oqtane.Client/Services/PageService.cs | 1 + Oqtane.Client/Services/ProfileService.cs | 1 + Oqtane.Client/Services/RoleService.cs | 1 + Oqtane.Client/Services/SettingService.cs | 1 + Oqtane.Client/Services/SiteService.cs | 1 + Oqtane.Client/Services/SiteTemplateService.cs | 1 + Oqtane.Client/Services/SqlService.cs | 1 + Oqtane.Client/Services/TenantService.cs | 1 + Oqtane.Client/Services/ThemeService.cs | 1 + Oqtane.Client/Services/UserRoleService.cs | 1 + Oqtane.Client/Services/UserService.cs | 1 + Oqtane.Client/UI/Pane.razor | 3 ++- Oqtane.Client/_Imports.razor | 3 ++- Oqtane.Server/Controllers/AliasController.cs | 1 + Oqtane.Server/Controllers/FileController.cs | 1 + Oqtane.Server/Controllers/FolderController.cs | 1 + Oqtane.Server/Controllers/JobController.cs | 1 + Oqtane.Server/Controllers/JobLogController.cs | 1 + Oqtane.Server/Controllers/LogController.cs | 1 + Oqtane.Server/Controllers/ModuleController.cs | 1 + .../Controllers/ModuleDefinitionController.cs | 2 ++ .../Controllers/NotificationController.cs | 1 + Oqtane.Server/Controllers/PageController.cs | 1 + .../Controllers/PageModuleController.cs | 1 + .../Controllers/ProfileController.cs | 1 + Oqtane.Server/Controllers/RoleController.cs | 1 + .../Controllers/SettingController.cs | 1 + Oqtane.Server/Controllers/SiteController.cs | 1 + .../Controllers/SiteTemplateController.cs | 1 + Oqtane.Server/Controllers/SqlController.cs | 1 + Oqtane.Server/Controllers/TenantController.cs | 1 + Oqtane.Server/Controllers/ThemeController.cs | 1 + Oqtane.Server/Controllers/UserController.cs | 1 + .../Controllers/UserRoleController.cs | 1 + .../OqtaneServiceCollectionExtensions.cs | 4 ++++ .../Infrastructure/DatabaseManager.cs | 2 ++ .../Infrastructure/Jobs/HostedServiceBase.cs | 4 ++-- .../Infrastructure/Jobs/NotificationJob.cs | 4 ++-- Oqtane.Server/Infrastructure/LogManager.cs | 1 + .../SiteTemplates/DefaultSiteTemplate.cs | 10 ++++---- Oqtane.Server/Infrastructure/SyncManager.cs | 1 + .../Controllers/HtmlTextController.cs | 2 +- .../HtmlText/Manager/HtmlTextManager.cs | 2 +- .../HtmlText/Repository/HtmlTextContext.cs | 6 ++++- .../HtmlText/Repository/HtmlTextRepository.cs | 4 +++- .../Repository/IHtmlTextRepository.cs | 2 +- Oqtane.Server/Pages/Login.cshtml | 2 +- Oqtane.Server/Program.cs | 11 ++++----- Oqtane.Server/Repository/AliasRepository.cs | 2 ++ .../Repository/Context/DBContextBase.cs | 4 +++- .../Repository/Context/InstallationContext.cs | 5 ++-- .../Repository/Context/MasterDBContext.cs | 3 ++- .../Repository/Context/TenantDBContext.cs | 3 ++- Oqtane.Server/Repository/FileRepository.cs | 2 ++ Oqtane.Server/Repository/FolderRepository.cs | 2 ++ .../Repository/Interfaces/IAliasRepository.cs | 2 +- .../Repository/Interfaces/IFileRepository.cs | 2 +- .../Interfaces/IFolderRepository.cs | 2 +- .../Interfaces/IJobLogRepository.cs | 2 +- .../Repository/Interfaces/IJobRepository.cs | 2 +- .../Repository/Interfaces/ILogRepository.cs | 2 +- .../Interfaces/IModuleDefinitionRepository.cs | 2 +- .../Interfaces/IModuleRepository.cs | 2 +- .../Interfaces/INotificationRepository.cs | 2 +- .../Interfaces/IPageModuleRepository.cs | 2 +- .../Repository/Interfaces/IPageRepository.cs | 2 +- .../Interfaces/IPermissionRepository.cs | 2 +- .../Interfaces/IProfileRepository.cs | 2 +- .../Repository/Interfaces/IRoleRepository.cs | 2 +- .../Interfaces/ISettingRepository.cs | 2 +- .../Repository/Interfaces/ISiteRepository.cs | 2 +- .../Interfaces/ISiteTemplateRepository.cs | 2 +- .../Repository/Interfaces/ISqlRepository.cs | 2 +- .../Interfaces/ITenantRepository.cs | 2 +- .../Repository/Interfaces/ITenantResolver.cs | 2 +- .../Repository/Interfaces/IThemeRepository.cs | 2 +- .../Repository/Interfaces/IUserRepository.cs | 2 +- .../Interfaces/IUserRoleRepository.cs | 2 +- Oqtane.Server/Repository/JobLogRepository.cs | 2 ++ Oqtane.Server/Repository/JobRepository.cs | 2 ++ Oqtane.Server/Repository/LogRepository.cs | 2 ++ .../Repository/ModuleDefinitionRepository.cs | 2 ++ Oqtane.Server/Repository/ModuleRepository.cs | 2 ++ .../Repository/NotificationRepository.cs | 2 ++ .../Repository/PageModuleRepository.cs | 2 ++ Oqtane.Server/Repository/PageRepository.cs | 2 ++ .../Repository/PermissionRepository.cs | 2 ++ Oqtane.Server/Repository/ProfileRepository.cs | 2 ++ Oqtane.Server/Repository/RoleRepository.cs | 2 ++ Oqtane.Server/Repository/SettingRepository.cs | 2 ++ Oqtane.Server/Repository/SiteRepository.cs | 2 ++ .../Repository/SiteTemplateRepository.cs | 1 + Oqtane.Server/Repository/SqlRepository.cs | 1 + Oqtane.Server/Repository/TenantRepository.cs | 2 ++ Oqtane.Server/Repository/TenantResolver.cs | 1 + Oqtane.Server/Repository/ThemeRepository.cs | 1 + Oqtane.Server/Repository/UserRepository.cs | 2 ++ .../Repository/UserRoleRepository.cs | 2 ++ .../Security/ClaimsPrincipalFactory.cs | 1 + Oqtane.Server/Security/UserPermissions.cs | 1 + Oqtane.Server/Startup.cs | 3 +++ Oqtane.Server/appsettings.json | 4 ++-- Oqtane.Shared/Enums/LogLevel.cs | 2 +- Oqtane.Shared/Enums/SecurityAccessLevel.cs | 2 +- Oqtane.Shared/Interfaces/IAuditable.cs | 2 +- Oqtane.Shared/Interfaces/IDeletable.cs | 2 +- Oqtane.Shared/Interfaces/IService.cs | 2 +- Oqtane.Shared/Models/Alias.cs | 1 + Oqtane.Shared/Models/File.cs | 1 + Oqtane.Shared/Models/Folder.cs | 1 + Oqtane.Shared/Models/Job.cs | 1 + Oqtane.Shared/Models/Module.cs | 2 ++ Oqtane.Shared/Models/ModuleDefinition.cs | 1 + Oqtane.Shared/Models/Notification.cs | 1 + Oqtane.Shared/Models/Page.cs | 1 + Oqtane.Shared/Models/PageModule.cs | 1 + Oqtane.Shared/Models/Permission.cs | 1 + Oqtane.Shared/Models/Profile.cs | 1 + Oqtane.Shared/Models/Role.cs | 1 + Oqtane.Shared/Models/Setting.cs | 1 + Oqtane.Shared/Models/Site.cs | 1 + Oqtane.Shared/Models/Tenant.cs | 1 + Oqtane.Shared/Models/User.cs | 1 + Oqtane.Shared/Models/UserRole.cs | 1 + .../Modules/Models/HtmlText/HtmlTextInfo.cs | 5 ++-- Oqtane.Upgrade/Program.cs | 2 +- 223 files changed, 338 insertions(+), 153 deletions(-) diff --git a/Oqtane.Client/App.razor b/Oqtane.Client/App.razor index 3d13adff..00ab86b3 100644 --- a/Oqtane.Client/App.razor +++ b/Oqtane.Client/App.razor @@ -1,4 +1,5 @@ -@inject IInstallationService InstallationService +@using Oqtane.Services.Interfaces +@inject IInstallationService InstallationService @if (_initialized) { diff --git a/Oqtane.Client/Modules/Admin/Dashboard/Index.razor b/Oqtane.Client/Modules/Admin/Dashboard/Index.razor index 7e3004fb..bb83de5e 100644 --- a/Oqtane.Client/Modules/Admin/Dashboard/Index.razor +++ b/Oqtane.Client/Modules/Admin/Dashboard/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Dashboard +@using Oqtane.Enums @inherits ModuleBase @inject IPageService PageService @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Error/Index.razor b/Oqtane.Client/Modules/Admin/Error/Index.razor index 9e0df574..43a4c9a0 100644 --- a/Oqtane.Client/Modules/Admin/Error/Index.razor +++ b/Oqtane.Client/Modules/Admin/Error/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Error +@using Oqtane.Enums @inherits ModuleBase @inject IModuleService ModuleService diff --git a/Oqtane.Client/Modules/Admin/Files/Add.razor b/Oqtane.Client/Modules/Admin/Files/Add.razor index 182f08f7..b8daae66 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 Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index 167c0b3c..e7e49e7c 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Files +@using Oqtane.Enums @inherits ModuleBase @inject IFolderService FolderService @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/Admin/Files/Index.razor b/Oqtane.Client/Modules/Admin/Files/Index.razor index d92e0994..610d9d99 100644 --- a/Oqtane.Client/Modules/Admin/Files/Index.razor +++ b/Oqtane.Client/Modules/Admin/Files/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Files +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFolderService FolderService diff --git a/Oqtane.Client/Modules/Admin/Jobs/Add.razor b/Oqtane.Client/Modules/Admin/Jobs/Add.razor index 4775f8a0..ee2b440a 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Add.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Jobs +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IJobService JobService diff --git a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor index 5339a226..d4a2f624 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Jobs +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IJobService JobService diff --git a/Oqtane.Client/Modules/Admin/Jobs/Index.razor b/Oqtane.Client/Modules/Admin/Jobs/Index.razor index f15c50df..9ccab6e7 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Jobs +@using Oqtane.Enums @inherits ModuleBase @inject IJobService JobService diff --git a/Oqtane.Client/Modules/Admin/Jobs/Log.razor b/Oqtane.Client/Modules/Admin/Jobs/Log.razor index 9efc1486..fc466aa5 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Log.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Log.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Jobs +@using Oqtane.Enums @inherits ModuleBase @inject IJobLogService JobLogService diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index 098ebcb4..70922950 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Login +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IJSRuntime JsRuntime diff --git a/Oqtane.Client/Modules/Admin/Logs/Detail.razor b/Oqtane.Client/Modules/Admin/Logs/Detail.razor index 2f9c054d..1d4fd33c 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Detail.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Detail.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Admin.Logs @using System.Globalization +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ILogService LogService diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor index e87450bd..b51a4bc1 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Logs +@using Oqtane.Enums @inherits ModuleBase @inject ILogService LogService diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor index b1d72ce0..7dcb0ee7 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.ModuleCreator +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IModuleDefinitionService ModuleDefinitionService diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index a342297b..6158f7b4 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.ModuleDefinitions +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index 732d7d5c..f9fb24a9 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.ModuleDefinitions +@using Oqtane.Enums @inherits ModuleBase @inject IModuleDefinitionService ModuleDefinitionService @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor index d348ddb4..06793559 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.ModuleDefinitions +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IModuleDefinitionService ModuleDefinitionService diff --git a/Oqtane.Client/Modules/Admin/Modules/Export.razor b/Oqtane.Client/Modules/Admin/Modules/Export.razor index 77c0ba53..b797755c 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Export.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Export.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Modules +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IModuleService ModuleService diff --git a/Oqtane.Client/Modules/Admin/Modules/Import.razor b/Oqtane.Client/Modules/Admin/Modules/Import.razor index 2a4d3aa0..95d99999 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Import.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Import.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Modules +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IModuleService ModuleService diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index a98d015e..ee10e2cd 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Modules +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IThemeService ThemeService diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 483cc107..e1ec4aad 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Pages +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IPageService PageService diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 2b13870e..6624c9fe 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Pages +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IPageService PageService diff --git a/Oqtane.Client/Modules/Admin/Pages/Index.razor b/Oqtane.Client/Modules/Admin/Pages/Index.razor index b53f3811..005f85ea 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Index.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Pages +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IPageService PageService diff --git a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor index bcfd2d3f..a3c7fb4b 100644 --- a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Profiles +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IProfileService ProfileService diff --git a/Oqtane.Client/Modules/Admin/Profiles/Index.razor b/Oqtane.Client/Modules/Admin/Profiles/Index.razor index e3cefdaa..dedcc2c3 100644 --- a/Oqtane.Client/Modules/Admin/Profiles/Index.razor +++ b/Oqtane.Client/Modules/Admin/Profiles/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Profiles +@using Oqtane.Enums @inherits ModuleBase @inject IProfileService ProfileService diff --git a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor index d3955307..d10240ae 100644 --- a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor +++ b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.RecycleBin +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IPageModuleService PageModuleService diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index f358d905..ba85ef58 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Register +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Reset/Index.razor b/Oqtane.Client/Modules/Admin/Reset/Index.razor index b6e4eb66..8b309a16 100644 --- a/Oqtane.Client/Modules/Admin/Reset/Index.razor +++ b/Oqtane.Client/Modules/Admin/Reset/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Reset +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Roles/Add.razor b/Oqtane.Client/Modules/Admin/Roles/Add.razor index 34e93e2d..7637d0b1 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Add.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Roles +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IRoleService RoleService diff --git a/Oqtane.Client/Modules/Admin/Roles/Edit.razor b/Oqtane.Client/Modules/Admin/Roles/Edit.razor index 4b0dd0e0..e82050e6 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Edit.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Roles +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IRoleService RoleService diff --git a/Oqtane.Client/Modules/Admin/Roles/Index.razor b/Oqtane.Client/Modules/Admin/Roles/Index.razor index 0acfa614..9e329c0c 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Index.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Roles +@using Oqtane.Enums @inherits ModuleBase @inject IRoleService RoleService diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index b20ccd1d..b734013a 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Site +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ISiteService SiteService diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 0bb51513..f91abfe9 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Sites +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ITenantService TenantService diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor index 838615ee..414fc10b 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Sites +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ISiteService SiteService diff --git a/Oqtane.Client/Modules/Admin/Sites/Index.razor b/Oqtane.Client/Modules/Admin/Sites/Index.razor index 978f7ad3..00c5dabc 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Sites +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IAliasService AliasService diff --git a/Oqtane.Client/Modules/Admin/Sql/Index.razor b/Oqtane.Client/Modules/Admin/Sql/Index.razor index 4899301c..54bab14d 100644 --- a/Oqtane.Client/Modules/Admin/Sql/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sql/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Sql +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ITenantService TenantService diff --git a/Oqtane.Client/Modules/Admin/Tenants/Add.razor b/Oqtane.Client/Modules/Admin/Tenants/Add.razor index 962b9e4c..9b8020c7 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Add.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Tenants +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ITenantService TenantService diff --git a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor index 56f3fe20..8d24fec6 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Tenants +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ITenantService TenantService diff --git a/Oqtane.Client/Modules/Admin/Tenants/Index.razor b/Oqtane.Client/Modules/Admin/Tenants/Index.razor index 018c1f68..1c333833 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Index.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Tenants +@using Oqtane.Enums @inherits ModuleBase @inject ITenantService TenantService diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor index d78ab5e5..9c1ee9bf 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Add.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Themes +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService diff --git a/Oqtane.Client/Modules/Admin/Themes/Index.razor b/Oqtane.Client/Modules/Admin/Themes/Index.razor index 4a3113b6..287b46f1 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Index.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Themes +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IThemeService ThemeService diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index 5d55b6aa..ea80e420 100644 --- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor +++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Upgrade +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor index 6cf41e3f..967a62b4 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.UserProfile +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserRoleService UserRoleService diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index 6711617a..38ee0ac8 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.UserProfile +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/UserProfile/View.razor b/Oqtane.Client/Modules/Admin/UserProfile/View.razor index 07140f63..61a9920c 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/View.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/View.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.UserProfile +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserRoleService UserRoleService diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor index 561185f6..a339b3df 100644 --- a/Oqtane.Client/Modules/Admin/Users/Add.razor +++ b/Oqtane.Client/Modules/Admin/Users/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Users +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index a0b8370b..ba041d7d 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Users +@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index a9d4366f..0abeb865 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Users +@using Oqtane.Enums @inherits ModuleBase @inject IUserRoleService UserRoleService @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Users/Roles.razor b/Oqtane.Client/Modules/Admin/Users/Roles.razor index fa84b46d..22453aa0 100644 --- a/Oqtane.Client/Modules/Admin/Users/Roles.razor +++ b/Oqtane.Client/Modules/Admin/Users/Roles.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Users +@using Oqtane.Enums @inherits ModuleBase @inject IRoleService RoleService @inject IUserRoleService UserRoleService diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor index 08b2d77d..310abc06 100644 --- a/Oqtane.Client/Modules/Controls/ActionDialog.razor +++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Controls +@using Oqtane.Enums @inherits ModuleBase @if (_visible) diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index cf588579..887f1b2f 100644 --- a/Oqtane.Client/Modules/Controls/ActionLink.razor +++ b/Oqtane.Client/Modules/Controls/ActionLink.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Controls +@using Oqtane.Enums @inherits ModuleBase @inject IUserService UserService diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index 41c55192..e90603d9 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -1,6 +1,6 @@ @using Oqtane.Modules.HtmlText.Services -@using Oqtane.Modules.HtmlText.Models -@using Oqtane.Modules.Controls +@using Oqtane.Modules.Models.HtmlText +@using Oqtane.Enums @namespace Oqtane.Modules.HtmlText @inherits ModuleBase @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/HtmlText/Index.razor b/Oqtane.Client/Modules/HtmlText/Index.razor index 47bf0b61..3e5c35d6 100644 --- a/Oqtane.Client/Modules/HtmlText/Index.razor +++ b/Oqtane.Client/Modules/HtmlText/Index.razor @@ -1,5 +1,4 @@ @using Oqtane.Modules.HtmlText.Services -@using Oqtane.Modules.HtmlText.Models @namespace Oqtane.Modules.HtmlText @inherits ModuleBase @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs index 1b3a7834..cfbe8a7f 100644 --- a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs +++ b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; -using Oqtane.Modules.HtmlText.Models; +using Oqtane.Modules.Models.HtmlText; using Oqtane.Services; using Oqtane.Shared; diff --git a/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs b/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs index 6ce1d646..246612a4 100644 --- a/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs +++ b/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Modules.HtmlText.Models; +using Oqtane.Modules.Models.HtmlText; namespace Oqtane.Modules.HtmlText.Services { diff --git a/Oqtane.Client/Modules/IModuleControl.cs b/Oqtane.Client/Modules/IModuleControl.cs index c4f2fdee..4fb55d7b 100644 --- a/Oqtane.Client/Modules/IModuleControl.cs +++ b/Oqtane.Client/Modules/IModuleControl.cs @@ -1,4 +1,5 @@ -using Oqtane.Shared; +using Oqtane.Enums; +using Oqtane.Shared; namespace Oqtane.Modules { diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 1fa56c65..db39b87d 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Oqtane.Services; using System; using Oqtane.Enums; +using Oqtane.Services.Interfaces; using Oqtane.UI; namespace Oqtane.Modules diff --git a/Oqtane.Client/Modules/Weather/Index.razor b/Oqtane.Client/Modules/Weather/Index.razor index 2d20ff38..ae440590 100644 --- a/Oqtane.Client/Modules/Weather/Index.razor +++ b/Oqtane.Client/Modules/Weather/Index.razor @@ -1,4 +1,5 @@ @using Oqtane.Modules.Weather.Services +@using Oqtane.Modules.Weather.Models @namespace Oqtane.Modules.Weather @inherits ModuleBase @@ -39,4 +40,4 @@ else WeatherForecastService forecastservice = new WeatherForecastService(); forecasts = await forecastservice.GetForecastAsync(DateTime.UtcNow); } -} \ No newline at end of file +} diff --git a/Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs b/Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs index 7e02ea8a..ea5e7156 100644 --- a/Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs +++ b/Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs @@ -1,6 +1,6 @@ using System; -namespace Oqtane.Modules.Weather +namespace Oqtane.Modules.Weather.Models { public class WeatherForecast { diff --git a/Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs b/Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs index 743848fb..2b6b9621 100644 --- a/Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs +++ b/Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Oqtane.Modules.Weather.Models; namespace Oqtane.Modules.Weather.Services { diff --git a/Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs b/Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs index e6ad13d9..1fde0100 100644 --- a/Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs +++ b/Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs @@ -2,6 +2,7 @@ using Oqtane.Modules; using System; using System.Linq; using System.Threading.Tasks; +using Oqtane.Modules.Weather.Models; namespace Oqtane.Modules.Weather.Services { diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index 4172834b..3563396e 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -1,16 +1,19 @@ -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Microsoft.Extensions.DependencyInjection; -using System.Threading.Tasks; -using Oqtane.Services; -using System.Reflection; -using System; +using System; using System.Linq; -using Oqtane.Modules; -using Oqtane.Shared; -using Oqtane.Providers; +using System.Reflection; +using System.Threading.Tasks; using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Oqtane.Interfaces; +using Oqtane.Models; +using Oqtane.Modules; +using Oqtane.Providers; +using Oqtane.Services; +using Oqtane.Services.Interfaces; +using Oqtane.Shared; -namespace Oqtane.Client +namespace Oqtane { public class Program { diff --git a/Oqtane.Client/Services/AliasService.cs b/Oqtane.Client/Services/AliasService.cs index d7fdf3e6..8c3c2fa7 100644 --- a/Oqtane.Client/Services/AliasService.cs +++ b/Oqtane.Client/Services/AliasService.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using Oqtane.Shared; using System.Net; using System; +using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index 3a875ac4..d4714d6f 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using Oqtane.Models; +using Oqtane.Services.Interfaces; using Oqtane.Shared; using Oqtane.UI; diff --git a/Oqtane.Client/Services/FolderService.cs b/Oqtane.Client/Services/FolderService.cs index 642aeafa..44522474 100644 --- a/Oqtane.Client/Services/FolderService.cs +++ b/Oqtane.Client/Services/FolderService.cs @@ -8,6 +8,7 @@ using Oqtane.Shared; using System; using System.Diagnostics.CodeAnalysis; using System.Net; +using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/InstallationService.cs b/Oqtane.Client/Services/InstallationService.cs index 40da3010..014aa436 100644 --- a/Oqtane.Client/Services/InstallationService.cs +++ b/Oqtane.Client/Services/InstallationService.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using System.Net.Http; using Microsoft.AspNetCore.Components; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/Interfaces/IAliasService.cs b/Oqtane.Client/Services/Interfaces/IAliasService.cs index f15356f1..2bd39d7e 100644 --- a/Oqtane.Client/Services/Interfaces/IAliasService.cs +++ b/Oqtane.Client/Services/Interfaces/IAliasService.cs @@ -1,9 +1,9 @@ -using Oqtane.Models; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IAliasService { diff --git a/Oqtane.Client/Services/Interfaces/IFileService.cs b/Oqtane.Client/Services/Interfaces/IFileService.cs index 7e10f4fd..925f699d 100644 --- a/Oqtane.Client/Services/Interfaces/IFileService.cs +++ b/Oqtane.Client/Services/Interfaces/IFileService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IFileService { diff --git a/Oqtane.Client/Services/Interfaces/IFolderService.cs b/Oqtane.Client/Services/Interfaces/IFolderService.cs index 34edb609..9e70b690 100644 --- a/Oqtane.Client/Services/Interfaces/IFolderService.cs +++ b/Oqtane.Client/Services/Interfaces/IFolderService.cs @@ -1,9 +1,9 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IFolderService { diff --git a/Oqtane.Client/Services/Interfaces/IInstallationService.cs b/Oqtane.Client/Services/Interfaces/IInstallationService.cs index c9eeaca8..0485bdec 100644 --- a/Oqtane.Client/Services/Interfaces/IInstallationService.cs +++ b/Oqtane.Client/Services/Interfaces/IInstallationService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Oqtane.Models; using Oqtane.Shared; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IInstallationService { diff --git a/Oqtane.Client/Services/Interfaces/IJobLogService.cs b/Oqtane.Client/Services/Interfaces/IJobLogService.cs index 015d83f0..70d0da46 100644 --- a/Oqtane.Client/Services/Interfaces/IJobLogService.cs +++ b/Oqtane.Client/Services/Interfaces/IJobLogService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IJobLogService { diff --git a/Oqtane.Client/Services/Interfaces/IJobService.cs b/Oqtane.Client/Services/Interfaces/IJobService.cs index d6f0decf..8e56454f 100644 --- a/Oqtane.Client/Services/Interfaces/IJobService.cs +++ b/Oqtane.Client/Services/Interfaces/IJobService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IJobService { diff --git a/Oqtane.Client/Services/Interfaces/ILogService.cs b/Oqtane.Client/Services/Interfaces/ILogService.cs index 30867c72..cdcdd26a 100644 --- a/Oqtane.Client/Services/Interfaces/ILogService.cs +++ b/Oqtane.Client/Services/Interfaces/ILogService.cs @@ -1,11 +1,11 @@ -using Oqtane.Models; -using Oqtane.Shared; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Oqtane.Enums; +using Oqtane.Models; +using Oqtane.Shared; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface ILogService { diff --git a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs index 8560c661..88417a29 100644 --- a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs +++ b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs @@ -1,9 +1,9 @@ -using Oqtane.Models; -using Oqtane.UI; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; +using Oqtane.UI; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IModuleDefinitionService { diff --git a/Oqtane.Client/Services/Interfaces/IModuleService.cs b/Oqtane.Client/Services/Interfaces/IModuleService.cs index 27f11dff..ad85a26f 100644 --- a/Oqtane.Client/Services/Interfaces/IModuleService.cs +++ b/Oqtane.Client/Services/Interfaces/IModuleService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IModuleService { diff --git a/Oqtane.Client/Services/Interfaces/INotificationService.cs b/Oqtane.Client/Services/Interfaces/INotificationService.cs index bd52b7d0..bc479803 100644 --- a/Oqtane.Client/Services/Interfaces/INotificationService.cs +++ b/Oqtane.Client/Services/Interfaces/INotificationService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface INotificationService { diff --git a/Oqtane.Client/Services/Interfaces/IPackageService.cs b/Oqtane.Client/Services/Interfaces/IPackageService.cs index eeb243c1..a1e69541 100644 --- a/Oqtane.Client/Services/Interfaces/IPackageService.cs +++ b/Oqtane.Client/Services/Interfaces/IPackageService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IPackageService { diff --git a/Oqtane.Client/Services/Interfaces/IPageModuleService.cs b/Oqtane.Client/Services/Interfaces/IPageModuleService.cs index f51c56a8..3cae8abc 100644 --- a/Oqtane.Client/Services/Interfaces/IPageModuleService.cs +++ b/Oqtane.Client/Services/Interfaces/IPageModuleService.cs @@ -1,7 +1,7 @@ -using Oqtane.Models; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IPageModuleService { diff --git a/Oqtane.Client/Services/Interfaces/IPageService.cs b/Oqtane.Client/Services/Interfaces/IPageService.cs index 60f1d86e..4cd8dd69 100644 --- a/Oqtane.Client/Services/Interfaces/IPageService.cs +++ b/Oqtane.Client/Services/Interfaces/IPageService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IPageService { diff --git a/Oqtane.Client/Services/Interfaces/IProfileService.cs b/Oqtane.Client/Services/Interfaces/IProfileService.cs index cc079f3c..83fd6c5d 100644 --- a/Oqtane.Client/Services/Interfaces/IProfileService.cs +++ b/Oqtane.Client/Services/Interfaces/IProfileService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IProfileService { diff --git a/Oqtane.Client/Services/Interfaces/IRoleService.cs b/Oqtane.Client/Services/Interfaces/IRoleService.cs index 23ae0d05..90373202 100644 --- a/Oqtane.Client/Services/Interfaces/IRoleService.cs +++ b/Oqtane.Client/Services/Interfaces/IRoleService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IRoleService { diff --git a/Oqtane.Client/Services/Interfaces/ISettingService.cs b/Oqtane.Client/Services/Interfaces/ISettingService.cs index f7c06591..9c128612 100644 --- a/Oqtane.Client/Services/Interfaces/ISettingService.cs +++ b/Oqtane.Client/Services/Interfaces/ISettingService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface ISettingService { diff --git a/Oqtane.Client/Services/Interfaces/ISiteService.cs b/Oqtane.Client/Services/Interfaces/ISiteService.cs index 32ca6926..0ca7847c 100644 --- a/Oqtane.Client/Services/Interfaces/ISiteService.cs +++ b/Oqtane.Client/Services/Interfaces/ISiteService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface ISiteService { diff --git a/Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs b/Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs index abb9a6c1..5993b7f8 100644 --- a/Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs +++ b/Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface ISiteTemplateService { diff --git a/Oqtane.Client/Services/Interfaces/ISqlService.cs b/Oqtane.Client/Services/Interfaces/ISqlService.cs index f563a795..5c7127ee 100644 --- a/Oqtane.Client/Services/Interfaces/ISqlService.cs +++ b/Oqtane.Client/Services/Interfaces/ISqlService.cs @@ -1,7 +1,7 @@ -using Oqtane.Models; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface ISqlService { diff --git a/Oqtane.Client/Services/Interfaces/ITenantService.cs b/Oqtane.Client/Services/Interfaces/ITenantService.cs index 24bdd79b..0b863bab 100644 --- a/Oqtane.Client/Services/Interfaces/ITenantService.cs +++ b/Oqtane.Client/Services/Interfaces/ITenantService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface ITenantService { diff --git a/Oqtane.Client/Services/Interfaces/IThemeService.cs b/Oqtane.Client/Services/Interfaces/IThemeService.cs index 8756042b..36e25e9f 100644 --- a/Oqtane.Client/Services/Interfaces/IThemeService.cs +++ b/Oqtane.Client/Services/Interfaces/IThemeService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IThemeService { diff --git a/Oqtane.Client/Services/Interfaces/IUserRoleService.cs b/Oqtane.Client/Services/Interfaces/IUserRoleService.cs index 25cb9ab4..55d553db 100644 --- a/Oqtane.Client/Services/Interfaces/IUserRoleService.cs +++ b/Oqtane.Client/Services/Interfaces/IUserRoleService.cs @@ -1,8 +1,8 @@ -using Oqtane.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IUserRoleService { diff --git a/Oqtane.Client/Services/Interfaces/IUserService.cs b/Oqtane.Client/Services/Interfaces/IUserService.cs index 0a18b23d..9eba77b3 100644 --- a/Oqtane.Client/Services/Interfaces/IUserService.cs +++ b/Oqtane.Client/Services/Interfaces/IUserService.cs @@ -1,7 +1,7 @@ -using Oqtane.Models; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Oqtane.Models; -namespace Oqtane.Services +namespace Oqtane.Services.Interfaces { public interface IUserService { diff --git a/Oqtane.Client/Services/JobLogService.cs b/Oqtane.Client/Services/JobLogService.cs index f564f46c..4cb22566 100644 --- a/Oqtane.Client/Services/JobLogService.cs +++ b/Oqtane.Client/Services/JobLogService.cs @@ -4,6 +4,7 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/JobService.cs b/Oqtane.Client/Services/JobService.cs index b06b2fd7..3903a375 100644 --- a/Oqtane.Client/Services/JobService.cs +++ b/Oqtane.Client/Services/JobService.cs @@ -4,6 +4,7 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/LogService.cs b/Oqtane.Client/Services/LogService.cs index a679ee3d..d278fbd7 100644 --- a/Oqtane.Client/Services/LogService.cs +++ b/Oqtane.Client/Services/LogService.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Oqtane.Enums; using Oqtane.Models; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index 119aa9dd..f0aa9818 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using System; using System.Reflection; +using Oqtane.Services.Interfaces; using Oqtane.Shared; using Oqtane.UI; diff --git a/Oqtane.Client/Services/ModuleService.cs b/Oqtane.Client/Services/ModuleService.cs index c78403e4..4408fbd2 100644 --- a/Oqtane.Client/Services/ModuleService.cs +++ b/Oqtane.Client/Services/ModuleService.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/NotificationService.cs b/Oqtane.Client/Services/NotificationService.cs index 6239ca8a..7eb28b28 100644 --- a/Oqtane.Client/Services/NotificationService.cs +++ b/Oqtane.Client/Services/NotificationService.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Components; using Oqtane.Shared; using System.Collections.Generic; using System.Linq; +using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/PackageService.cs b/Oqtane.Client/Services/PackageService.cs index 952ea4cb..117ec952 100644 --- a/Oqtane.Client/Services/PackageService.cs +++ b/Oqtane.Client/Services/PackageService.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Oqtane.Shared; using System.Linq; +using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/PageModuleService.cs b/Oqtane.Client/Services/PageModuleService.cs index 11fece8c..a2ae4b6c 100644 --- a/Oqtane.Client/Services/PageModuleService.cs +++ b/Oqtane.Client/Services/PageModuleService.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/PageService.cs b/Oqtane.Client/Services/PageService.cs index 6cc832c7..c191f1e0 100644 --- a/Oqtane.Client/Services/PageService.cs +++ b/Oqtane.Client/Services/PageService.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using Oqtane.Shared; using System; using System.Net; +using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/ProfileService.cs b/Oqtane.Client/Services/ProfileService.cs index 4ceb4cac..0f0fca14 100644 --- a/Oqtane.Client/Services/ProfileService.cs +++ b/Oqtane.Client/Services/ProfileService.cs @@ -4,6 +4,7 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/RoleService.cs b/Oqtane.Client/Services/RoleService.cs index 708f0721..614293af 100644 --- a/Oqtane.Client/Services/RoleService.cs +++ b/Oqtane.Client/Services/RoleService.cs @@ -4,6 +4,7 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/SettingService.cs b/Oqtane.Client/Services/SettingService.cs index 22ac7fc4..7082bf89 100644 --- a/Oqtane.Client/Services/SettingService.cs +++ b/Oqtane.Client/Services/SettingService.cs @@ -4,6 +4,7 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/SiteService.cs b/Oqtane.Client/Services/SiteService.cs index ce4a379c..eab64ad1 100644 --- a/Oqtane.Client/Services/SiteService.cs +++ b/Oqtane.Client/Services/SiteService.cs @@ -4,6 +4,7 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/SiteTemplateService.cs b/Oqtane.Client/Services/SiteTemplateService.cs index 410f2abc..48cff86c 100644 --- a/Oqtane.Client/Services/SiteTemplateService.cs +++ b/Oqtane.Client/Services/SiteTemplateService.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/SqlService.cs b/Oqtane.Client/Services/SqlService.cs index 52371df8..0f1440ea 100644 --- a/Oqtane.Client/Services/SqlService.cs +++ b/Oqtane.Client/Services/SqlService.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Components; using Oqtane.Shared; using System.Collections.Generic; using System.Linq; +using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/TenantService.cs b/Oqtane.Client/Services/TenantService.cs index d325fd91..14f7c277 100644 --- a/Oqtane.Client/Services/TenantService.cs +++ b/Oqtane.Client/Services/TenantService.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Components; using Oqtane.Shared; using System.Collections.Generic; using System.Linq; +using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/ThemeService.cs b/Oqtane.Client/Services/ThemeService.cs index e872aae1..0f633501 100644 --- a/Oqtane.Client/Services/ThemeService.cs +++ b/Oqtane.Client/Services/ThemeService.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using System.Reflection; using System; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/UserRoleService.cs b/Oqtane.Client/Services/UserRoleService.cs index 66e9feb9..137462ca 100644 --- a/Oqtane.Client/Services/UserRoleService.cs +++ b/Oqtane.Client/Services/UserRoleService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/UserService.cs b/Oqtane.Client/Services/UserService.cs index d81077ff..ac306e8e 100644 --- a/Oqtane.Client/Services/UserService.cs +++ b/Oqtane.Client/Services/UserService.cs @@ -3,6 +3,7 @@ using Oqtane.Models; using System.Net.Http; using Microsoft.AspNetCore.Components; using System.Threading.Tasks; +using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/UI/Pane.razor b/Oqtane.Client/UI/Pane.razor index 9d0c7dd4..5ed6e109 100644 --- a/Oqtane.Client/UI/Pane.razor +++ b/Oqtane.Client/UI/Pane.razor @@ -1,4 +1,5 @@ -@namespace Oqtane.UI +@using Oqtane.Enums +@namespace Oqtane.UI @inject IUserService UserService @inject IModuleService ModuleService @inject IModuleDefinitionService ModuleDefinitionService diff --git a/Oqtane.Client/_Imports.razor b/Oqtane.Client/_Imports.razor index 22d11669..1c87349b 100644 --- a/Oqtane.Client/_Imports.razor +++ b/Oqtane.Client/_Imports.razor @@ -14,7 +14,8 @@ @using Oqtane.Providers @using Oqtane.Security @using Oqtane.Services +@using Oqtane.Services.Interfaces @using Oqtane.Shared @using Oqtane.Themes @using Oqtane.Themes.Controls -@using Oqtane.UI \ No newline at end of file +@using Oqtane.UI diff --git a/Oqtane.Server/Controllers/AliasController.cs b/Oqtane.Server/Controllers/AliasController.cs index 47f2d0a9..922c71de 100644 --- a/Oqtane.Server/Controllers/AliasController.cs +++ b/Oqtane.Server/Controllers/AliasController.cs @@ -10,6 +10,7 @@ using System.Globalization; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index ad85e6c2..5a5da650 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -16,6 +16,7 @@ using System.Net; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; // ReSharper disable StringIndexOfIsCultureSpecific.1 diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 1a2fdb2f..c2b22fc4 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -8,6 +8,7 @@ using System.Net; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; using Oqtane.Security; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/JobController.cs b/Oqtane.Server/Controllers/JobController.cs index 838a62fb..6db3404e 100644 --- a/Oqtane.Server/Controllers/JobController.cs +++ b/Oqtane.Server/Controllers/JobController.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.DependencyInjection; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/JobLogController.cs b/Oqtane.Server/Controllers/JobLogController.cs index f2a506d5..e1f766a2 100644 --- a/Oqtane.Server/Controllers/JobLogController.cs +++ b/Oqtane.Server/Controllers/JobLogController.cs @@ -6,6 +6,7 @@ using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/LogController.cs b/Oqtane.Server/Controllers/LogController.cs index d59b9411..575ee6c8 100644 --- a/Oqtane.Server/Controllers/LogController.cs +++ b/Oqtane.Server/Controllers/LogController.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Authorization; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index 348f973e..fe2064d8 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -7,6 +7,7 @@ using System.Linq; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; using Oqtane.Security; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 5e4659ce..1d7cd5ad 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -12,6 +12,8 @@ using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; using Oqtane.Security; using System; +using Oqtane.Repository.Interfaces; + // ReSharper disable StringIndexOfIsCultureSpecific.1 namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/NotificationController.cs b/Oqtane.Server/Controllers/NotificationController.cs index 47f0f85f..33c2a70f 100644 --- a/Oqtane.Server/Controllers/NotificationController.cs +++ b/Oqtane.Server/Controllers/NotificationController.cs @@ -6,6 +6,7 @@ using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; using Oqtane.Security; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 607ceaef..7e73dd6a 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -9,6 +9,7 @@ using System.Net; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/PageModuleController.cs b/Oqtane.Server/Controllers/PageModuleController.cs index c6149cbd..057efe0e 100644 --- a/Oqtane.Server/Controllers/PageModuleController.cs +++ b/Oqtane.Server/Controllers/PageModuleController.cs @@ -7,6 +7,7 @@ using System.Linq; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; using Oqtane.Security; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/ProfileController.cs b/Oqtane.Server/Controllers/ProfileController.cs index 1a3c0f40..21b5621d 100644 --- a/Oqtane.Server/Controllers/ProfileController.cs +++ b/Oqtane.Server/Controllers/ProfileController.cs @@ -6,6 +6,7 @@ using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/RoleController.cs b/Oqtane.Server/Controllers/RoleController.cs index 61f58f4a..84ff016f 100644 --- a/Oqtane.Server/Controllers/RoleController.cs +++ b/Oqtane.Server/Controllers/RoleController.cs @@ -6,6 +6,7 @@ using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index 4c551098..0ccc0e6a 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -7,6 +7,7 @@ using System.Linq; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index a8db1073..afa80966 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -7,6 +7,7 @@ using System.Linq; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/SiteTemplateController.cs b/Oqtane.Server/Controllers/SiteTemplateController.cs index c74e69a4..c560d45e 100644 --- a/Oqtane.Server/Controllers/SiteTemplateController.cs +++ b/Oqtane.Server/Controllers/SiteTemplateController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using Oqtane.Models; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/SqlController.cs b/Oqtane.Server/Controllers/SqlController.cs index 1ec75d19..21c0e6fb 100644 --- a/Oqtane.Server/Controllers/SqlController.cs +++ b/Oqtane.Server/Controllers/SqlController.cs @@ -11,6 +11,7 @@ using System.Data; using System.Dynamic; using Newtonsoft.Json; using System; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/TenantController.cs b/Oqtane.Server/Controllers/TenantController.cs index 83f84355..feb31480 100644 --- a/Oqtane.Server/Controllers/TenantController.cs +++ b/Oqtane.Server/Controllers/TenantController.cs @@ -6,6 +6,7 @@ using Oqtane.Enums; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index 5fadf164..72eb2bb3 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Hosting; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; // ReSharper disable StringIndexOfIsCultureSpecific.1 diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index d2bc94db..4cd74157 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -13,6 +13,7 @@ using System.Net; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/UserRoleController.cs b/Oqtane.Server/Controllers/UserRoleController.cs index fa3d2d6c..fcfc7665 100644 --- a/Oqtane.Server/Controllers/UserRoleController.cs +++ b/Oqtane.Server/Controllers/UserRoleController.cs @@ -6,6 +6,7 @@ using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index aef47901..e16ba3fb 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -6,7 +6,11 @@ using System.Reflection; using System.Runtime.Loader; using Microsoft.Extensions.Hosting; using Oqtane.Infrastructure; +using Oqtane.Infrastructure.Jobs; +using Oqtane.Interfaces; +using Oqtane.Models; using Oqtane.Modules; +using File = System.IO.File; // ReSharper disable once CheckNamespace namespace Microsoft.Extensions.DependencyInjection diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 2d23f219..46f7b1f7 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -15,6 +15,8 @@ using Oqtane.Controllers; using Oqtane.Extensions; using Oqtane.Models; using Oqtane.Repository; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; using File = System.IO.File; diff --git a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs index 35b8b0a9..fdc562ad 100644 --- a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs +++ b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs @@ -6,10 +6,10 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Oqtane.Models; -using Oqtane.Repository; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; -namespace Oqtane.Infrastructure +namespace Oqtane.Infrastructure.Jobs { public abstract class HostedServiceBase : IHostedService, IDisposable { diff --git a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs index bd667486..8b2e0a55 100644 --- a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs +++ b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs @@ -5,10 +5,10 @@ using System.Net; using System.Net.Mail; using Microsoft.Extensions.DependencyInjection; using Oqtane.Models; -using Oqtane.Repository; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; -namespace Oqtane.Infrastructure +namespace Oqtane.Infrastructure.Jobs { public class NotificationJob : HostedServiceBase { diff --git a/Oqtane.Server/Infrastructure/LogManager.cs b/Oqtane.Server/Infrastructure/LogManager.cs index 47101016..e42c83c3 100644 --- a/Oqtane.Server/Infrastructure/LogManager.cs +++ b/Oqtane.Server/Infrastructure/LogManager.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; using Oqtane.Security; // ReSharper disable StringIndexOfIsCultureSpecific.2 // ReSharper disable StringIndexOfIsCultureSpecific.1 diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index 45d61327..89b03b6f 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -1,11 +1,11 @@ -using Oqtane.Models; -using Oqtane.Infrastructure.Interfaces; -using System.Collections.Generic; -using Oqtane.Repository; +using System.Collections.Generic; using Microsoft.AspNetCore.Hosting; +using Oqtane.Infrastructure.Interfaces; +using Oqtane.Models; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; -namespace Oqtane.SiteTemplates +namespace Oqtane.Infrastructure.SiteTemplates { public class DefaultSiteTemplate : ISiteTemplate { diff --git a/Oqtane.Server/Infrastructure/SyncManager.cs b/Oqtane.Server/Infrastructure/SyncManager.cs index 7828bbc3..676f123d 100644 --- a/Oqtane.Server/Infrastructure/SyncManager.cs +++ b/Oqtane.Server/Infrastructure/SyncManager.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Infrastructure { diff --git a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs index 01eb92cf..fea49e28 100644 --- a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs +++ b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Modules.HtmlText.Models; using Oqtane.Modules.HtmlText.Repository; using Microsoft.AspNetCore.Http; using Oqtane.Shared; @@ -8,6 +7,7 @@ using System; using System.Collections.Generic; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; +using Oqtane.Modules.Models.HtmlText; namespace Oqtane.Modules.HtmlText.Controllers { diff --git a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs index f37ac523..a7f6082a 100644 --- a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs +++ b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs @@ -1,7 +1,7 @@ using Oqtane.Models; -using Oqtane.Modules.HtmlText.Models; using Oqtane.Modules.HtmlText.Repository; using System.Net; +using Oqtane.Modules.Models.HtmlText; namespace Oqtane.Modules.HtmlText.Manager { diff --git a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs index 70cd2065..c2efd6a5 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs @@ -1,7 +1,11 @@ using Microsoft.EntityFrameworkCore; -using Oqtane.Modules.HtmlText.Models; using Oqtane.Repository; using Microsoft.AspNetCore.Http; +using Oqtane.Interfaces; +using Oqtane.Models; +using Oqtane.Modules.Models.HtmlText; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Modules.HtmlText.Repository { diff --git a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs index c97b0de7..8163f82d 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs @@ -1,6 +1,8 @@ using Microsoft.EntityFrameworkCore; using System.Linq; -using Oqtane.Modules.HtmlText.Models; +using Oqtane.Interfaces; +using Oqtane.Models; +using Oqtane.Modules.Models.HtmlText; namespace Oqtane.Modules.HtmlText.Repository { diff --git a/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs b/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs index cbe5d0e4..ff7bd6b8 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs @@ -1,4 +1,4 @@ -using Oqtane.Modules.HtmlText.Models; +using Oqtane.Modules.Models.HtmlText; namespace Oqtane.Modules.HtmlText.Repository { diff --git a/Oqtane.Server/Pages/Login.cshtml b/Oqtane.Server/Pages/Login.cshtml index f2eab15f..44d794dc 100644 --- a/Oqtane.Server/Pages/Login.cshtml +++ b/Oqtane.Server/Pages/Login.cshtml @@ -1,3 +1,3 @@ @page "/pages/login" -@namespace Oqtane.Pages +@namespace Oqtane.Pages @model Oqtane.Pages.LoginModel diff --git a/Oqtane.Server/Program.cs b/Oqtane.Server/Program.cs index e46805e6..613f1271 100644 --- a/Oqtane.Server/Program.cs +++ b/Oqtane.Server/Program.cs @@ -1,13 +1,10 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; -// DO NOT REMOVE - needed for client-side Blazor -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; -using Microsoft.AspNetCore; using Microsoft.Extensions.DependencyInjection; -using Oqtane.Infrastructure; +using Oqtane.Infrastructure; // DO NOT REMOVE - needed for client-side Blazor -namespace Oqtane.Server +namespace Oqtane { public class Program { diff --git a/Oqtane.Server/Repository/AliasRepository.cs b/Oqtane.Server/Repository/AliasRepository.cs index ccb26681..08220ff4 100644 --- a/Oqtane.Server/Repository/AliasRepository.cs +++ b/Oqtane.Server/Repository/AliasRepository.cs @@ -4,6 +4,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/Context/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs index 247006bd..f0804065 100644 --- a/Oqtane.Server/Repository/Context/DBContextBase.cs +++ b/Oqtane.Server/Repository/Context/DBContextBase.cs @@ -4,9 +4,11 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; +using Oqtane.Interfaces; using Oqtane.Models; +using Oqtane.Repository.Interfaces; -namespace Oqtane.Repository +namespace Oqtane.Repository.Context { public class DBContextBase : IdentityUserContext { diff --git a/Oqtane.Server/Repository/Context/InstallationContext.cs b/Oqtane.Server/Repository/Context/InstallationContext.cs index 7b971a16..618fee17 100644 --- a/Oqtane.Server/Repository/Context/InstallationContext.cs +++ b/Oqtane.Server/Repository/Context/InstallationContext.cs @@ -1,8 +1,7 @@ -using System.Diagnostics.CodeAnalysis; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Context { public class InstallationContext : DbContext diff --git a/Oqtane.Server/Repository/Context/MasterDBContext.cs b/Oqtane.Server/Repository/Context/MasterDBContext.cs index 65312819..cb6a75c6 100644 --- a/Oqtane.Server/Repository/Context/MasterDBContext.cs +++ b/Oqtane.Server/Repository/Context/MasterDBContext.cs @@ -2,9 +2,10 @@ using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; +using Oqtane.Interfaces; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Context { public class MasterDBContext : DbContext { diff --git a/Oqtane.Server/Repository/Context/TenantDBContext.cs b/Oqtane.Server/Repository/Context/TenantDBContext.cs index be085b20..e865a11e 100644 --- a/Oqtane.Server/Repository/Context/TenantDBContext.cs +++ b/Oqtane.Server/Repository/Context/TenantDBContext.cs @@ -1,8 +1,9 @@ using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Interfaces; -namespace Oqtane.Repository +namespace Oqtane.Repository.Context { public class TenantDBContext : DBContextBase { diff --git a/Oqtane.Server/Repository/FileRepository.cs b/Oqtane.Server/Repository/FileRepository.cs index a74f2ce9..0939fc41 100644 --- a/Oqtane.Server/Repository/FileRepository.cs +++ b/Oqtane.Server/Repository/FileRepository.cs @@ -2,6 +2,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/FolderRepository.cs b/Oqtane.Server/Repository/FolderRepository.cs index c83fa215..50a1b750 100644 --- a/Oqtane.Server/Repository/FolderRepository.cs +++ b/Oqtane.Server/Repository/FolderRepository.cs @@ -2,6 +2,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs b/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs index 4f258307..87908391 100644 --- a/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IAliasRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IFileRepository.cs b/Oqtane.Server/Repository/Interfaces/IFileRepository.cs index 19715187..8109595d 100644 --- a/Oqtane.Server/Repository/Interfaces/IFileRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IFileRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IFileRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs index 65256184..85105ddb 100644 --- a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IFolderRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs b/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs index d5858da5..167c34fc 100644 --- a/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IJobLogRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IJobRepository.cs b/Oqtane.Server/Repository/Interfaces/IJobRepository.cs index 12be85c1..88bfbfdb 100644 --- a/Oqtane.Server/Repository/Interfaces/IJobRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IJobRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IJobRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ILogRepository.cs b/Oqtane.Server/Repository/Interfaces/ILogRepository.cs index 27752a03..7ca86392 100644 --- a/Oqtane.Server/Repository/Interfaces/ILogRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ILogRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface ILogRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs index 32c59549..ca711dc1 100644 --- a/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IModuleDefinitionRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs index 2ef4185d..2e108a9b 100644 --- a/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IModuleRepository { diff --git a/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs b/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs index ef53bc3a..bf7dbdbd 100644 --- a/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface INotificationRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs b/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs index 181fb629..3efea416 100644 --- a/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IPageModuleRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IPageRepository.cs b/Oqtane.Server/Repository/Interfaces/IPageRepository.cs index 6db92dba..66a213a6 100644 --- a/Oqtane.Server/Repository/Interfaces/IPageRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPageRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IPageRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs index 5be9bb50..00347d88 100644 --- a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IPermissionRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs b/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs index c9b4f34b..9e0d6edb 100644 --- a/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IProfileRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs b/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs index 35f9315f..b9c0bc65 100644 --- a/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IRoleRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs b/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs index 976519fb..64301777 100644 --- a/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface ISettingRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs b/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs index 5e05dccf..da470601 100644 --- a/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface ISiteRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs b/Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs index 659226ff..7202cb94 100644 --- a/Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface ISiteTemplateRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs index 4bd04e91..3b2d9f29 100644 --- a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs @@ -1,7 +1,7 @@ using System.Data.SqlClient; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface ISqlRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs b/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs index 5993eb63..790f83b7 100644 --- a/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface ITenantRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ITenantResolver.cs b/Oqtane.Server/Repository/Interfaces/ITenantResolver.cs index 95e916f7..2ee51aed 100644 --- a/Oqtane.Server/Repository/Interfaces/ITenantResolver.cs +++ b/Oqtane.Server/Repository/Interfaces/ITenantResolver.cs @@ -1,6 +1,6 @@ using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface ITenantResolver { diff --git a/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs b/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs index 90afa3bd..595f752f 100644 --- a/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IThemeRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IUserRepository.cs b/Oqtane.Server/Repository/Interfaces/IUserRepository.cs index be646e56..be389cfe 100644 --- a/Oqtane.Server/Repository/Interfaces/IUserRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IUserRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IUserRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs b/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs index c46a0e50..ad7fba60 100644 --- a/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository +namespace Oqtane.Repository.Interfaces { public interface IUserRoleRepository { diff --git a/Oqtane.Server/Repository/JobLogRepository.cs b/Oqtane.Server/Repository/JobLogRepository.cs index 440a483d..11db1cf2 100644 --- a/Oqtane.Server/Repository/JobLogRepository.cs +++ b/Oqtane.Server/Repository/JobLogRepository.cs @@ -2,6 +2,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/JobRepository.cs b/Oqtane.Server/Repository/JobRepository.cs index cbe51c19..d30f482d 100644 --- a/Oqtane.Server/Repository/JobRepository.cs +++ b/Oqtane.Server/Repository/JobRepository.cs @@ -4,6 +4,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/LogRepository.cs b/Oqtane.Server/Repository/LogRepository.cs index 1bcbc2be..a76ebbd0 100644 --- a/Oqtane.Server/Repository/LogRepository.cs +++ b/Oqtane.Server/Repository/LogRepository.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Linq; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index db4a8339..a81a2363 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -5,6 +5,8 @@ using System.Reflection; using Microsoft.Extensions.Caching.Memory; using Oqtane.Models; using Oqtane.Modules; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/ModuleRepository.cs b/Oqtane.Server/Repository/ModuleRepository.cs index 18d9df3b..e6e6e44a 100644 --- a/Oqtane.Server/Repository/ModuleRepository.cs +++ b/Oqtane.Server/Repository/ModuleRepository.cs @@ -7,6 +7,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Oqtane.Models; using Oqtane.Modules; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; using Module = Oqtane.Models.Module; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/NotificationRepository.cs b/Oqtane.Server/Repository/NotificationRepository.cs index 8f34ff50..172c7364 100644 --- a/Oqtane.Server/Repository/NotificationRepository.cs +++ b/Oqtane.Server/Repository/NotificationRepository.cs @@ -2,6 +2,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/PageModuleRepository.cs b/Oqtane.Server/Repository/PageModuleRepository.cs index ee85270d..83524786 100644 --- a/Oqtane.Server/Repository/PageModuleRepository.cs +++ b/Oqtane.Server/Repository/PageModuleRepository.cs @@ -2,6 +2,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/PageRepository.cs b/Oqtane.Server/Repository/PageRepository.cs index 9e106e74..049c0090 100644 --- a/Oqtane.Server/Repository/PageRepository.cs +++ b/Oqtane.Server/Repository/PageRepository.cs @@ -2,6 +2,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/PermissionRepository.cs b/Oqtane.Server/Repository/PermissionRepository.cs index fd9670d3..cb84c4c1 100644 --- a/Oqtane.Server/Repository/PermissionRepository.cs +++ b/Oqtane.Server/Repository/PermissionRepository.cs @@ -5,6 +5,8 @@ using System.Text; using System.Text.Json; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/ProfileRepository.cs b/Oqtane.Server/Repository/ProfileRepository.cs index 8577ea78..542ec07e 100644 --- a/Oqtane.Server/Repository/ProfileRepository.cs +++ b/Oqtane.Server/Repository/ProfileRepository.cs @@ -2,6 +2,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/RoleRepository.cs b/Oqtane.Server/Repository/RoleRepository.cs index 2814d7e0..fb2c830f 100644 --- a/Oqtane.Server/Repository/RoleRepository.cs +++ b/Oqtane.Server/Repository/RoleRepository.cs @@ -2,6 +2,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/SettingRepository.cs b/Oqtane.Server/Repository/SettingRepository.cs index 30b7f676..e3744cfe 100644 --- a/Oqtane.Server/Repository/SettingRepository.cs +++ b/Oqtane.Server/Repository/SettingRepository.cs @@ -2,6 +2,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index a2f7e33d..adebe718 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -8,6 +8,8 @@ using Microsoft.Extensions.DependencyInjection; using Oqtane.Infrastructure.Interfaces; using Oqtane.Models; using Oqtane.Modules; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; using Module = Oqtane.Models.Module; diff --git a/Oqtane.Server/Repository/SiteTemplateRepository.cs b/Oqtane.Server/Repository/SiteTemplateRepository.cs index c88934fd..93123a85 100644 --- a/Oqtane.Server/Repository/SiteTemplateRepository.cs +++ b/Oqtane.Server/Repository/SiteTemplateRepository.cs @@ -5,6 +5,7 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Oqtane.Infrastructure.Interfaces; using Oqtane.Models; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/SqlRepository.cs b/Oqtane.Server/Repository/SqlRepository.cs index c9bdf734..a7fef59c 100644 --- a/Oqtane.Server/Repository/SqlRepository.cs +++ b/Oqtane.Server/Repository/SqlRepository.cs @@ -2,6 +2,7 @@ using System.Data; using System.Data.SqlClient; using Oqtane.Models; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/TenantRepository.cs b/Oqtane.Server/Repository/TenantRepository.cs index 6c22ba75..94813ab8 100644 --- a/Oqtane.Server/Repository/TenantRepository.cs +++ b/Oqtane.Server/Repository/TenantRepository.cs @@ -4,6 +4,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/TenantResolver.cs b/Oqtane.Server/Repository/TenantResolver.cs index 8a5b85c4..451c2b21 100644 --- a/Oqtane.Server/Repository/TenantResolver.cs +++ b/Oqtane.Server/Repository/TenantResolver.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Http; using Oqtane.Models; +using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/ThemeRepository.cs b/Oqtane.Server/Repository/ThemeRepository.cs index 3bb3f7e9..0ec55b2e 100644 --- a/Oqtane.Server/Repository/ThemeRepository.cs +++ b/Oqtane.Server/Repository/ThemeRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Oqtane.Models; +using Oqtane.Repository.Interfaces; using Oqtane.Themes; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/UserRepository.cs b/Oqtane.Server/Repository/UserRepository.cs index a9e51afe..cd1e3c77 100644 --- a/Oqtane.Server/Repository/UserRepository.cs +++ b/Oqtane.Server/Repository/UserRepository.cs @@ -2,6 +2,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/UserRoleRepository.cs b/Oqtane.Server/Repository/UserRoleRepository.cs index 79f8a629..90c98ea7 100644 --- a/Oqtane.Server/Repository/UserRoleRepository.cs +++ b/Oqtane.Server/Repository/UserRoleRepository.cs @@ -2,6 +2,8 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Security/ClaimsPrincipalFactory.cs b/Oqtane.Server/Security/ClaimsPrincipalFactory.cs index 1e945938..c279b0b5 100644 --- a/Oqtane.Server/Security/ClaimsPrincipalFactory.cs +++ b/Oqtane.Server/Security/ClaimsPrincipalFactory.cs @@ -7,6 +7,7 @@ using Oqtane.Shared; using System.Collections.Generic; using System.Linq; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Security { diff --git a/Oqtane.Server/Security/UserPermissions.cs b/Oqtane.Server/Security/UserPermissions.cs index 72108b5d..8e0e56af 100644 --- a/Oqtane.Server/Security/UserPermissions.cs +++ b/Oqtane.Server/Security/UserPermissions.cs @@ -3,6 +3,7 @@ using Oqtane.Models; using System.Linq; using System.Security.Claims; using Oqtane.Repository; +using Oqtane.Repository.Interfaces; namespace Oqtane.Security { diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 46b5e697..e1ce6d62 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -17,8 +17,11 @@ using Microsoft.OpenApi.Models; using Oqtane.Infrastructure; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; +using Oqtane.Repository.Context; +using Oqtane.Repository.Interfaces; using Oqtane.Security; using Oqtane.Services; +using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane diff --git a/Oqtane.Server/appsettings.json b/Oqtane.Server/appsettings.json index a3352489..3432b151 100644 --- a/Oqtane.Server/appsettings.json +++ b/Oqtane.Server/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "" + "DefaultConnection": "Data Source=(LocalDb)\\MSSQLLocalDB;AttachDbFilename=W:\\Oqtane\\oqtane.framework\\Oqtane.Server\\Data\\Oqtane-202004031921.mdf;Initial Catalog=Oqtane-202004031921;Integrated Security=SSPI;" }, "Installation": { "DefaultAlias": "", @@ -12,4 +12,4 @@ "DefaultLayout": "", "DefaultContainer": "" } -} +} \ No newline at end of file diff --git a/Oqtane.Shared/Enums/LogLevel.cs b/Oqtane.Shared/Enums/LogLevel.cs index f772157e..ac02c377 100644 --- a/Oqtane.Shared/Enums/LogLevel.cs +++ b/Oqtane.Shared/Enums/LogLevel.cs @@ -1,4 +1,4 @@ -namespace Oqtane.Shared +namespace Oqtane.Enums { public enum LogLevel { diff --git a/Oqtane.Shared/Enums/SecurityAccessLevel.cs b/Oqtane.Shared/Enums/SecurityAccessLevel.cs index 7b993b60..f82f2e42 100644 --- a/Oqtane.Shared/Enums/SecurityAccessLevel.cs +++ b/Oqtane.Shared/Enums/SecurityAccessLevel.cs @@ -1,4 +1,4 @@ -namespace Oqtane.Shared +namespace Oqtane.Enums { public enum SecurityAccessLevel { diff --git a/Oqtane.Shared/Interfaces/IAuditable.cs b/Oqtane.Shared/Interfaces/IAuditable.cs index 83cd1c1f..bb961087 100644 --- a/Oqtane.Shared/Interfaces/IAuditable.cs +++ b/Oqtane.Shared/Interfaces/IAuditable.cs @@ -1,6 +1,6 @@ using System; -namespace Oqtane.Models +namespace Oqtane.Interfaces { public interface IAuditable { diff --git a/Oqtane.Shared/Interfaces/IDeletable.cs b/Oqtane.Shared/Interfaces/IDeletable.cs index 7880f8ff..da854e61 100644 --- a/Oqtane.Shared/Interfaces/IDeletable.cs +++ b/Oqtane.Shared/Interfaces/IDeletable.cs @@ -1,6 +1,6 @@ using System; -namespace Oqtane.Models +namespace Oqtane.Interfaces { public interface IDeletable { diff --git a/Oqtane.Shared/Interfaces/IService.cs b/Oqtane.Shared/Interfaces/IService.cs index b7330d81..4fbd91d0 100644 --- a/Oqtane.Shared/Interfaces/IService.cs +++ b/Oqtane.Shared/Interfaces/IService.cs @@ -1,4 +1,4 @@ -namespace Oqtane.Modules +namespace Oqtane.Interfaces { public interface IService { diff --git a/Oqtane.Shared/Models/Alias.cs b/Oqtane.Shared/Models/Alias.cs index 5e54e382..a2d34872 100644 --- a/Oqtane.Shared/Models/Alias.cs +++ b/Oqtane.Shared/Models/Alias.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/File.cs b/Oqtane.Shared/Models/File.cs index ae20fa11..e4aa47cf 100644 --- a/Oqtane.Shared/Models/File.cs +++ b/Oqtane.Shared/Models/File.cs @@ -1,4 +1,5 @@ using System; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Folder.cs b/Oqtane.Shared/Models/Folder.cs index 5f04f4bb..23acb72f 100644 --- a/Oqtane.Shared/Models/Folder.cs +++ b/Oqtane.Shared/Models/Folder.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Job.cs b/Oqtane.Shared/Models/Job.cs index 2d1f40fc..6a048bed 100644 --- a/Oqtane.Shared/Models/Job.cs +++ b/Oqtane.Shared/Models/Job.cs @@ -1,4 +1,5 @@ using System; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Module.cs b/Oqtane.Shared/Models/Module.cs index 927377a0..e51ffc55 100644 --- a/Oqtane.Shared/Models/Module.cs +++ b/Oqtane.Shared/Models/Module.cs @@ -1,6 +1,8 @@ using Oqtane.Shared; using System; using System.ComponentModel.DataAnnotations.Schema; +using Oqtane.Enums; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/ModuleDefinition.cs b/Oqtane.Shared/Models/ModuleDefinition.cs index 35a27840..beafe72e 100644 --- a/Oqtane.Shared/Models/ModuleDefinition.cs +++ b/Oqtane.Shared/Models/ModuleDefinition.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Notification.cs b/Oqtane.Shared/Models/Notification.cs index 0d804e41..f7b192f9 100644 --- a/Oqtane.Shared/Models/Notification.cs +++ b/Oqtane.Shared/Models/Notification.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Page.cs b/Oqtane.Shared/Models/Page.cs index f1268875..ec1a6d1c 100644 --- a/Oqtane.Shared/Models/Page.cs +++ b/Oqtane.Shared/Models/Page.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/PageModule.cs b/Oqtane.Shared/Models/PageModule.cs index 7812861a..3e9d1f2a 100644 --- a/Oqtane.Shared/Models/PageModule.cs +++ b/Oqtane.Shared/Models/PageModule.cs @@ -1,4 +1,5 @@ using System; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Permission.cs b/Oqtane.Shared/Models/Permission.cs index aff910f4..30ed4bbd 100644 --- a/Oqtane.Shared/Models/Permission.cs +++ b/Oqtane.Shared/Models/Permission.cs @@ -1,4 +1,5 @@ using System; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Profile.cs b/Oqtane.Shared/Models/Profile.cs index 11ecec07..e6835f6d 100644 --- a/Oqtane.Shared/Models/Profile.cs +++ b/Oqtane.Shared/Models/Profile.cs @@ -1,4 +1,5 @@ using System; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Role.cs b/Oqtane.Shared/Models/Role.cs index c9a316b5..f0c560bd 100644 --- a/Oqtane.Shared/Models/Role.cs +++ b/Oqtane.Shared/Models/Role.cs @@ -1,4 +1,5 @@ using System; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Setting.cs b/Oqtane.Shared/Models/Setting.cs index d7742c84..a030915c 100644 --- a/Oqtane.Shared/Models/Setting.cs +++ b/Oqtane.Shared/Models/Setting.cs @@ -1,4 +1,5 @@ using System; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs index 4b6b7f86..e785fe17 100644 --- a/Oqtane.Shared/Models/Site.cs +++ b/Oqtane.Shared/Models/Site.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Tenant.cs b/Oqtane.Shared/Models/Tenant.cs index 3f0a518a..40135ce6 100644 --- a/Oqtane.Shared/Models/Tenant.cs +++ b/Oqtane.Shared/Models/Tenant.cs @@ -1,4 +1,5 @@ using System; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/User.cs b/Oqtane.Shared/Models/User.cs index e74deef2..0c8db2eb 100644 --- a/Oqtane.Shared/Models/User.cs +++ b/Oqtane.Shared/Models/User.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/UserRole.cs b/Oqtane.Shared/Models/UserRole.cs index 556a31a8..b3771125 100644 --- a/Oqtane.Shared/Models/UserRole.cs +++ b/Oqtane.Shared/Models/UserRole.cs @@ -1,4 +1,5 @@ using System; +using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs b/Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs index e51a41fd..8876b097 100644 --- a/Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs +++ b/Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs @@ -1,9 +1,10 @@ using System; -using Oqtane.Models; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using Oqtane.Interfaces; +using Oqtane.Models; -namespace Oqtane.Modules.HtmlText.Models +namespace Oqtane.Modules.Models.HtmlText { [Table("HtmlText")] public class HtmlTextInfo : IAuditable diff --git a/Oqtane.Upgrade/Program.cs b/Oqtane.Upgrade/Program.cs index 8aa70d3b..517a6072 100644 --- a/Oqtane.Upgrade/Program.cs +++ b/Oqtane.Upgrade/Program.cs @@ -4,7 +4,7 @@ using System.IO.Compression; using System.Reflection; using System.Threading; -namespace Oqtane.Upgrade +namespace Oqtane { class Program { From a650f3847d15f30042086728b4a913604f129211 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 3 Apr 2020 17:27:04 -0400 Subject: [PATCH 110/265] removed connectionstring value --- Oqtane.Server/appsettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Server/appsettings.json b/Oqtane.Server/appsettings.json index 3432b151..a3352489 100644 --- a/Oqtane.Server/appsettings.json +++ b/Oqtane.Server/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "Data Source=(LocalDb)\\MSSQLLocalDB;AttachDbFilename=W:\\Oqtane\\oqtane.framework\\Oqtane.Server\\Data\\Oqtane-202004031921.mdf;Initial Catalog=Oqtane-202004031921;Integrated Security=SSPI;" + "DefaultConnection": "" }, "Installation": { "DefaultAlias": "", @@ -12,4 +12,4 @@ "DefaultLayout": "", "DefaultContainer": "" } -} \ No newline at end of file +} From 4bc089d2cb4786022b755b4bac900eb6fbc20a2d Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Sat, 4 Apr 2020 13:13:26 +0300 Subject: [PATCH 111/265] Simplify NavigateUrl using UriBuilder --- Oqtane.Shared/Shared/Utilities.cs | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 6c2825fc..6e446d31 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -14,30 +14,17 @@ namespace Oqtane.Shared var assemblyName = assemblyFullName.Substring(0, assemblyFullName.IndexOf(",", StringComparison.Ordinal)); return $"{type.Namespace}, {assemblyName}"; } + public static string NavigateUrl(string alias, string path, string parameters) { - string url = ""; - if (alias != "") - { - url += alias + "/"; - } - if (path != "" && path != "/") - { - url += path + "/"; - } - if (url.EndsWith("/")) - { - url = url.Substring(0, url.Length - 1); - } - if (!string.IsNullOrEmpty(parameters)) - { - url += "?" + parameters; - } - if (!url.StartsWith("/")) - { - url = "/" + url; - } - return url; + var uriBuilder = alias == string.Empty + ? new UriBuilder() + : new UriBuilder(alias); + + uriBuilder.Path = path; + uriBuilder.Query = parameters; + + return uriBuilder.Uri.AbsoluteUri; } public static string EditUrl(string alias, string path, int moduleid, string action, string parameters) From d406118d186569b0daa750827477db89857fd617 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Sat, 4 Apr 2020 13:13:40 +0300 Subject: [PATCH 112/265] Add unit tests --- .../Oqtane.Shared.Tests/UtilitiesTests.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs diff --git a/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs b/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs new file mode 100644 index 00000000..2966e8d0 --- /dev/null +++ b/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs @@ -0,0 +1,37 @@ +using Oqtane.Shared; +using Xunit; + +namespace Oqtane.Test.Oqtane.Shared.Tests +{ + public class UtilitiesTests + { + [Theory] + [InlineData("contoso", "login", "returnUrl=/admin", "http://contoso/login?returnUrl=/admin")] + [InlineData("contoso", "admin", "", "http://contoso/admin")] + [InlineData("contoso", "", "pageId=4", "http://contoso/?pageId=4")] + [InlineData("contoso", "", "", "http://contoso/")] + [InlineData("http://contoso", "login", "returnUrl=/admin", "http://contoso/login?returnUrl=/admin")] + [InlineData("http://contoso", "admin", "", "http://contoso/admin")] + [InlineData("http://contoso", "", "pageId=4", "http://contoso/?pageId=4")] + [InlineData("http://contoso", "", "", "http://contoso/")] + [InlineData("https://contoso", "login", "returnUrl=/admin", "https://contoso/login?returnUrl=/admin")] + [InlineData("https://contoso", "admin", "", "https://contoso/admin")] + [InlineData("https://contoso", "", "pageId=4", "https://contoso/?pageId=4")] + [InlineData("https://contoso", "", "", "https://contoso/")] + [InlineData("", "login", "returnUrl=/admin", "http://localhost/login?returnUrl=/admin")] + [InlineData("", "admin", "", "http://localhost/admin")] + [InlineData("", "", "pageId=4", "http://localhost/?pageId=4")] + [InlineData("", "", "", "http://localhost/")] + public void NavigateUrlTest(string alias, string path, string parameters, string expectedUrl) + { + // Arrange + var navigatedUrl = string.Empty; + + // Act + navigatedUrl = Utilities.NavigateUrl(alias, path, parameters); + + // Assert + Assert.Equal(expectedUrl, navigatedUrl); + } + } +} From 5af6f7a52d60da81c358e1b3a0f0c8a775c1f6bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Vesel=C3=BD?= Date: Sat, 4 Apr 2020 20:06:24 +0200 Subject: [PATCH 113/265] Namespace Fix undo (#340) --- Oqtane.Client/App.razor | 3 +-- .../Modules/Admin/Dashboard/Index.razor | 1 - Oqtane.Client/Modules/Admin/Error/Index.razor | 1 - Oqtane.Client/Modules/Admin/Files/Add.razor | 1 - Oqtane.Client/Modules/Admin/Files/Edit.razor | 1 - Oqtane.Client/Modules/Admin/Files/Index.razor | 1 - Oqtane.Client/Modules/Admin/Jobs/Add.razor | 1 - Oqtane.Client/Modules/Admin/Jobs/Edit.razor | 1 - Oqtane.Client/Modules/Admin/Jobs/Index.razor | 1 - Oqtane.Client/Modules/Admin/Jobs/Log.razor | 1 - Oqtane.Client/Modules/Admin/Login/Index.razor | 1 - Oqtane.Client/Modules/Admin/Logs/Detail.razor | 1 - Oqtane.Client/Modules/Admin/Logs/Index.razor | 1 - .../Modules/Admin/ModuleCreator/Index.razor | 1 - .../Modules/Admin/ModuleDefinitions/Add.razor | 1 - .../Admin/ModuleDefinitions/Edit.razor | 1 - .../Admin/ModuleDefinitions/Index.razor | 1 - .../Modules/Admin/Modules/Export.razor | 1 - .../Modules/Admin/Modules/Import.razor | 1 - .../Modules/Admin/Modules/Settings.razor | 1 - Oqtane.Client/Modules/Admin/Pages/Add.razor | 1 - Oqtane.Client/Modules/Admin/Pages/Edit.razor | 1 - Oqtane.Client/Modules/Admin/Pages/Index.razor | 1 - .../Modules/Admin/Profiles/Edit.razor | 1 - .../Modules/Admin/Profiles/Index.razor | 1 - .../Modules/Admin/RecycleBin/Index.razor | 1 - .../Modules/Admin/Register/Index.razor | 1 - Oqtane.Client/Modules/Admin/Reset/Index.razor | 1 - Oqtane.Client/Modules/Admin/Roles/Add.razor | 1 - Oqtane.Client/Modules/Admin/Roles/Edit.razor | 1 - Oqtane.Client/Modules/Admin/Roles/Index.razor | 1 - Oqtane.Client/Modules/Admin/Site/Index.razor | 1 - Oqtane.Client/Modules/Admin/Sites/Add.razor | 1 - Oqtane.Client/Modules/Admin/Sites/Edit.razor | 1 - Oqtane.Client/Modules/Admin/Sites/Index.razor | 1 - Oqtane.Client/Modules/Admin/Sql/Index.razor | 1 - Oqtane.Client/Modules/Admin/Tenants/Add.razor | 1 - .../Modules/Admin/Tenants/Edit.razor | 1 - .../Modules/Admin/Tenants/Index.razor | 1 - Oqtane.Client/Modules/Admin/Themes/Add.razor | 1 - .../Modules/Admin/Themes/Index.razor | 1 - .../Modules/Admin/Upgrade/Index.razor | 1 - .../Modules/Admin/UserProfile/Add.razor | 1 - .../Modules/Admin/UserProfile/Index.razor | 1 - .../Modules/Admin/UserProfile/View.razor | 1 - Oqtane.Client/Modules/Admin/Users/Add.razor | 1 - Oqtane.Client/Modules/Admin/Users/Edit.razor | 1 - Oqtane.Client/Modules/Admin/Users/Index.razor | 1 - Oqtane.Client/Modules/Admin/Users/Roles.razor | 1 - .../Modules/Controls/ActionDialog.razor | 1 - .../Modules/Controls/ActionLink.razor | 1 - Oqtane.Client/Modules/HtmlText/Edit.razor | 4 ++-- Oqtane.Client/Modules/HtmlText/Index.razor | 1 + .../HtmlText/Services/HtmlTextService.cs | 2 +- .../HtmlText/Services/IHtmlTextService.cs | 2 +- Oqtane.Client/Modules/IModuleControl.cs | 3 +-- Oqtane.Client/Modules/ModuleBase.cs | 1 - Oqtane.Client/Modules/Weather/Index.razor | 3 +-- .../Modules/Weather/Models/WeatherForecast.cs | 2 +- .../Services/IWeatherForecastService.cs | 1 - .../Services/WeatherForecastService.cs | 1 - Oqtane.Client/Program.cs | 21 ++++++++----------- Oqtane.Client/Services/AliasService.cs | 1 - Oqtane.Client/Services/FileService.cs | 1 - Oqtane.Client/Services/FolderService.cs | 1 - Oqtane.Client/Services/InstallationService.cs | 1 - .../Services/Interfaces/IAliasService.cs | 6 +++--- .../Services/Interfaces/IFileService.cs | 6 +++--- .../Services/Interfaces/IFolderService.cs | 6 +++--- .../Interfaces/IInstallationService.cs | 6 +++--- .../Services/Interfaces/IJobLogService.cs | 6 +++--- .../Services/Interfaces/IJobService.cs | 6 +++--- .../Services/Interfaces/ILogService.cs | 8 +++---- .../Interfaces/IModuleDefinitionService.cs | 8 +++---- .../Services/Interfaces/IModuleService.cs | 6 +++--- .../Interfaces/INotificationService.cs | 6 +++--- .../Services/Interfaces/IPackageService.cs | 6 +++--- .../Services/Interfaces/IPageModuleService.cs | 6 +++--- .../Services/Interfaces/IPageService.cs | 6 +++--- .../Services/Interfaces/IProfileService.cs | 6 +++--- .../Services/Interfaces/IRoleService.cs | 6 +++--- .../Services/Interfaces/ISettingService.cs | 6 +++--- .../Services/Interfaces/ISiteService.cs | 6 +++--- .../Interfaces/ISiteTemplateService.cs | 6 +++--- .../Services/Interfaces/ISqlService.cs | 6 +++--- .../Services/Interfaces/ITenantService.cs | 6 +++--- .../Services/Interfaces/IThemeService.cs | 6 +++--- .../Services/Interfaces/IUserRoleService.cs | 6 +++--- .../Services/Interfaces/IUserService.cs | 6 +++--- Oqtane.Client/Services/JobLogService.cs | 1 - Oqtane.Client/Services/JobService.cs | 1 - Oqtane.Client/Services/LogService.cs | 1 - .../Services/ModuleDefinitionService.cs | 1 - Oqtane.Client/Services/ModuleService.cs | 1 - Oqtane.Client/Services/NotificationService.cs | 1 - Oqtane.Client/Services/PackageService.cs | 1 - Oqtane.Client/Services/PageModuleService.cs | 1 - Oqtane.Client/Services/PageService.cs | 1 - Oqtane.Client/Services/ProfileService.cs | 1 - Oqtane.Client/Services/RoleService.cs | 1 - Oqtane.Client/Services/SettingService.cs | 1 - Oqtane.Client/Services/SiteService.cs | 1 - Oqtane.Client/Services/SiteTemplateService.cs | 1 - Oqtane.Client/Services/SqlService.cs | 1 - Oqtane.Client/Services/TenantService.cs | 1 - Oqtane.Client/Services/ThemeService.cs | 1 - Oqtane.Client/Services/UserRoleService.cs | 1 - Oqtane.Client/Services/UserService.cs | 1 - Oqtane.Client/UI/Pane.razor | 3 +-- Oqtane.Client/_Imports.razor | 3 +-- Oqtane.Server/Controllers/AliasController.cs | 1 - Oqtane.Server/Controllers/FileController.cs | 1 - Oqtane.Server/Controllers/FolderController.cs | 1 - Oqtane.Server/Controllers/JobController.cs | 1 - Oqtane.Server/Controllers/JobLogController.cs | 1 - Oqtane.Server/Controllers/LogController.cs | 1 - Oqtane.Server/Controllers/ModuleController.cs | 1 - .../Controllers/ModuleDefinitionController.cs | 2 -- .../Controllers/NotificationController.cs | 1 - Oqtane.Server/Controllers/PageController.cs | 1 - .../Controllers/PageModuleController.cs | 1 - .../Controllers/ProfileController.cs | 1 - Oqtane.Server/Controllers/RoleController.cs | 1 - .../Controllers/SettingController.cs | 1 - Oqtane.Server/Controllers/SiteController.cs | 1 - .../Controllers/SiteTemplateController.cs | 1 - Oqtane.Server/Controllers/SqlController.cs | 1 - Oqtane.Server/Controllers/TenantController.cs | 1 - Oqtane.Server/Controllers/ThemeController.cs | 1 - Oqtane.Server/Controllers/UserController.cs | 1 - .../Controllers/UserRoleController.cs | 1 - .../OqtaneServiceCollectionExtensions.cs | 4 ---- .../Infrastructure/DatabaseManager.cs | 2 -- .../Infrastructure/Jobs/HostedServiceBase.cs | 4 ++-- .../Infrastructure/Jobs/NotificationJob.cs | 4 ++-- Oqtane.Server/Infrastructure/LogManager.cs | 1 - .../SiteTemplates/DefaultSiteTemplate.cs | 10 ++++----- Oqtane.Server/Infrastructure/SyncManager.cs | 1 - .../Controllers/HtmlTextController.cs | 2 +- .../HtmlText/Manager/HtmlTextManager.cs | 2 +- .../HtmlText/Repository/HtmlTextContext.cs | 6 +----- .../HtmlText/Repository/HtmlTextRepository.cs | 4 +--- .../Repository/IHtmlTextRepository.cs | 2 +- Oqtane.Server/Pages/Login.cshtml | 2 +- Oqtane.Server/Program.cs | 11 ++++++---- Oqtane.Server/Repository/AliasRepository.cs | 2 -- .../Repository/Context/DBContextBase.cs | 4 +--- .../Repository/Context/InstallationContext.cs | 5 +++-- .../Repository/Context/MasterDBContext.cs | 3 +-- .../Repository/Context/TenantDBContext.cs | 3 +-- Oqtane.Server/Repository/FileRepository.cs | 2 -- Oqtane.Server/Repository/FolderRepository.cs | 2 -- .../Repository/Interfaces/IAliasRepository.cs | 2 +- .../Repository/Interfaces/IFileRepository.cs | 2 +- .../Interfaces/IFolderRepository.cs | 2 +- .../Interfaces/IJobLogRepository.cs | 2 +- .../Repository/Interfaces/IJobRepository.cs | 2 +- .../Repository/Interfaces/ILogRepository.cs | 2 +- .../Interfaces/IModuleDefinitionRepository.cs | 2 +- .../Interfaces/IModuleRepository.cs | 2 +- .../Interfaces/INotificationRepository.cs | 2 +- .../Interfaces/IPageModuleRepository.cs | 2 +- .../Repository/Interfaces/IPageRepository.cs | 2 +- .../Interfaces/IPermissionRepository.cs | 2 +- .../Interfaces/IProfileRepository.cs | 2 +- .../Repository/Interfaces/IRoleRepository.cs | 2 +- .../Interfaces/ISettingRepository.cs | 2 +- .../Repository/Interfaces/ISiteRepository.cs | 2 +- .../Interfaces/ISiteTemplateRepository.cs | 2 +- .../Repository/Interfaces/ISqlRepository.cs | 2 +- .../Interfaces/ITenantRepository.cs | 2 +- .../Repository/Interfaces/ITenantResolver.cs | 2 +- .../Repository/Interfaces/IThemeRepository.cs | 2 +- .../Repository/Interfaces/IUserRepository.cs | 2 +- .../Interfaces/IUserRoleRepository.cs | 2 +- Oqtane.Server/Repository/JobLogRepository.cs | 2 -- Oqtane.Server/Repository/JobRepository.cs | 2 -- Oqtane.Server/Repository/LogRepository.cs | 2 -- .../Repository/ModuleDefinitionRepository.cs | 2 -- Oqtane.Server/Repository/ModuleRepository.cs | 2 -- .../Repository/NotificationRepository.cs | 2 -- .../Repository/PageModuleRepository.cs | 2 -- Oqtane.Server/Repository/PageRepository.cs | 2 -- .../Repository/PermissionRepository.cs | 2 -- Oqtane.Server/Repository/ProfileRepository.cs | 2 -- Oqtane.Server/Repository/RoleRepository.cs | 2 -- Oqtane.Server/Repository/SettingRepository.cs | 2 -- Oqtane.Server/Repository/SiteRepository.cs | 2 -- .../Repository/SiteTemplateRepository.cs | 1 - Oqtane.Server/Repository/SqlRepository.cs | 1 - Oqtane.Server/Repository/TenantRepository.cs | 2 -- Oqtane.Server/Repository/TenantResolver.cs | 1 - Oqtane.Server/Repository/ThemeRepository.cs | 1 - Oqtane.Server/Repository/UserRepository.cs | 2 -- .../Repository/UserRoleRepository.cs | 2 -- .../Security/ClaimsPrincipalFactory.cs | 1 - Oqtane.Server/Security/UserPermissions.cs | 1 - Oqtane.Server/Startup.cs | 3 --- Oqtane.Shared/Enums/LogLevel.cs | 2 +- Oqtane.Shared/Enums/SecurityAccessLevel.cs | 2 +- Oqtane.Shared/Interfaces/IAuditable.cs | 2 +- Oqtane.Shared/Interfaces/IDeletable.cs | 2 +- Oqtane.Shared/Interfaces/IService.cs | 2 +- Oqtane.Shared/Models/Alias.cs | 1 - Oqtane.Shared/Models/File.cs | 1 - Oqtane.Shared/Models/Folder.cs | 1 - Oqtane.Shared/Models/Job.cs | 1 - Oqtane.Shared/Models/Module.cs | 2 -- Oqtane.Shared/Models/ModuleDefinition.cs | 1 - Oqtane.Shared/Models/Notification.cs | 1 - Oqtane.Shared/Models/Page.cs | 1 - Oqtane.Shared/Models/PageModule.cs | 1 - Oqtane.Shared/Models/Permission.cs | 1 - Oqtane.Shared/Models/Profile.cs | 1 - Oqtane.Shared/Models/Role.cs | 1 - Oqtane.Shared/Models/Setting.cs | 1 - Oqtane.Shared/Models/Site.cs | 1 - Oqtane.Shared/Models/Tenant.cs | 1 - Oqtane.Shared/Models/User.cs | 1 - Oqtane.Shared/Models/UserRole.cs | 1 - .../Modules/Models/HtmlText/HtmlTextInfo.cs | 5 ++--- Oqtane.Upgrade/Program.cs | 2 +- 222 files changed, 150 insertions(+), 335 deletions(-) diff --git a/Oqtane.Client/App.razor b/Oqtane.Client/App.razor index 00ab86b3..3d13adff 100644 --- a/Oqtane.Client/App.razor +++ b/Oqtane.Client/App.razor @@ -1,5 +1,4 @@ -@using Oqtane.Services.Interfaces -@inject IInstallationService InstallationService +@inject IInstallationService InstallationService @if (_initialized) { diff --git a/Oqtane.Client/Modules/Admin/Dashboard/Index.razor b/Oqtane.Client/Modules/Admin/Dashboard/Index.razor index bb83de5e..7e3004fb 100644 --- a/Oqtane.Client/Modules/Admin/Dashboard/Index.razor +++ b/Oqtane.Client/Modules/Admin/Dashboard/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Dashboard -@using Oqtane.Enums @inherits ModuleBase @inject IPageService PageService @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Error/Index.razor b/Oqtane.Client/Modules/Admin/Error/Index.razor index 43a4c9a0..9e0df574 100644 --- a/Oqtane.Client/Modules/Admin/Error/Index.razor +++ b/Oqtane.Client/Modules/Admin/Error/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Error -@using Oqtane.Enums @inherits ModuleBase @inject IModuleService ModuleService diff --git a/Oqtane.Client/Modules/Admin/Files/Add.razor b/Oqtane.Client/Modules/Admin/Files/Add.razor index b8daae66..182f08f7 100644 --- a/Oqtane.Client/Modules/Admin/Files/Add.razor +++ b/Oqtane.Client/Modules/Admin/Files/Add.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Files -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index e7e49e7c..167c0b3c 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Files -@using Oqtane.Enums @inherits ModuleBase @inject IFolderService FolderService @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/Admin/Files/Index.razor b/Oqtane.Client/Modules/Admin/Files/Index.razor index 610d9d99..d92e0994 100644 --- a/Oqtane.Client/Modules/Admin/Files/Index.razor +++ b/Oqtane.Client/Modules/Admin/Files/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Files -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFolderService FolderService diff --git a/Oqtane.Client/Modules/Admin/Jobs/Add.razor b/Oqtane.Client/Modules/Admin/Jobs/Add.razor index ee2b440a..4775f8a0 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Add.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Add.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Jobs -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IJobService JobService diff --git a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor index d4a2f624..5339a226 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Jobs -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IJobService JobService diff --git a/Oqtane.Client/Modules/Admin/Jobs/Index.razor b/Oqtane.Client/Modules/Admin/Jobs/Index.razor index 9ccab6e7..f15c50df 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Jobs -@using Oqtane.Enums @inherits ModuleBase @inject IJobService JobService diff --git a/Oqtane.Client/Modules/Admin/Jobs/Log.razor b/Oqtane.Client/Modules/Admin/Jobs/Log.razor index fc466aa5..9efc1486 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Log.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Log.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Jobs -@using Oqtane.Enums @inherits ModuleBase @inject IJobLogService JobLogService diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index 70922950..098ebcb4 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Login -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IJSRuntime JsRuntime diff --git a/Oqtane.Client/Modules/Admin/Logs/Detail.razor b/Oqtane.Client/Modules/Admin/Logs/Detail.razor index 1d4fd33c..2f9c054d 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Detail.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Detail.razor @@ -1,6 +1,5 @@ @namespace Oqtane.Modules.Admin.Logs @using System.Globalization -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ILogService LogService diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor index b51a4bc1..e87450bd 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Logs -@using Oqtane.Enums @inherits ModuleBase @inject ILogService LogService diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor index 7dcb0ee7..b1d72ce0 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.ModuleCreator -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IModuleDefinitionService ModuleDefinitionService diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index 6158f7b4..a342297b 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.ModuleDefinitions -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index f9fb24a9..732d7d5c 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.ModuleDefinitions -@using Oqtane.Enums @inherits ModuleBase @inject IModuleDefinitionService ModuleDefinitionService @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor index 06793559..d348ddb4 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.ModuleDefinitions -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IModuleDefinitionService ModuleDefinitionService diff --git a/Oqtane.Client/Modules/Admin/Modules/Export.razor b/Oqtane.Client/Modules/Admin/Modules/Export.razor index b797755c..77c0ba53 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Export.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Export.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Modules -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IModuleService ModuleService diff --git a/Oqtane.Client/Modules/Admin/Modules/Import.razor b/Oqtane.Client/Modules/Admin/Modules/Import.razor index 95d99999..2a4d3aa0 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Import.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Import.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Modules -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IModuleService ModuleService diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index ee10e2cd..a98d015e 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Modules -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IThemeService ThemeService diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index e1ec4aad..483cc107 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Pages -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IPageService PageService diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 6624c9fe..2b13870e 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Pages -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IPageService PageService diff --git a/Oqtane.Client/Modules/Admin/Pages/Index.razor b/Oqtane.Client/Modules/Admin/Pages/Index.razor index 005f85ea..b53f3811 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Index.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Pages -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IPageService PageService diff --git a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor index a3c7fb4b..bcfd2d3f 100644 --- a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Profiles -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IProfileService ProfileService diff --git a/Oqtane.Client/Modules/Admin/Profiles/Index.razor b/Oqtane.Client/Modules/Admin/Profiles/Index.razor index dedcc2c3..e3cefdaa 100644 --- a/Oqtane.Client/Modules/Admin/Profiles/Index.razor +++ b/Oqtane.Client/Modules/Admin/Profiles/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Profiles -@using Oqtane.Enums @inherits ModuleBase @inject IProfileService ProfileService diff --git a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor index d10240ae..d3955307 100644 --- a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor +++ b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.RecycleBin -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IPageModuleService PageModuleService diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index ba85ef58..f358d905 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Register -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Reset/Index.razor b/Oqtane.Client/Modules/Admin/Reset/Index.razor index 8b309a16..b6e4eb66 100644 --- a/Oqtane.Client/Modules/Admin/Reset/Index.razor +++ b/Oqtane.Client/Modules/Admin/Reset/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Reset -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Roles/Add.razor b/Oqtane.Client/Modules/Admin/Roles/Add.razor index 7637d0b1..34e93e2d 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Add.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Add.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Roles -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IRoleService RoleService diff --git a/Oqtane.Client/Modules/Admin/Roles/Edit.razor b/Oqtane.Client/Modules/Admin/Roles/Edit.razor index e82050e6..4b0dd0e0 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Edit.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Roles -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IRoleService RoleService diff --git a/Oqtane.Client/Modules/Admin/Roles/Index.razor b/Oqtane.Client/Modules/Admin/Roles/Index.razor index 9e329c0c..0acfa614 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Index.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Roles -@using Oqtane.Enums @inherits ModuleBase @inject IRoleService RoleService diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index b734013a..b20ccd1d 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Site -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ISiteService SiteService diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index f91abfe9..0bb51513 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Sites -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ITenantService TenantService diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor index 414fc10b..838615ee 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Sites -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ISiteService SiteService diff --git a/Oqtane.Client/Modules/Admin/Sites/Index.razor b/Oqtane.Client/Modules/Admin/Sites/Index.razor index 00c5dabc..978f7ad3 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Sites -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IAliasService AliasService diff --git a/Oqtane.Client/Modules/Admin/Sql/Index.razor b/Oqtane.Client/Modules/Admin/Sql/Index.razor index 54bab14d..4899301c 100644 --- a/Oqtane.Client/Modules/Admin/Sql/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sql/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Sql -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ITenantService TenantService diff --git a/Oqtane.Client/Modules/Admin/Tenants/Add.razor b/Oqtane.Client/Modules/Admin/Tenants/Add.razor index 9b8020c7..962b9e4c 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Add.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Add.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Tenants -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ITenantService TenantService diff --git a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor index 8d24fec6..56f3fe20 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Tenants -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject ITenantService TenantService diff --git a/Oqtane.Client/Modules/Admin/Tenants/Index.razor b/Oqtane.Client/Modules/Admin/Tenants/Index.razor index 1c333833..018c1f68 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Index.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Tenants -@using Oqtane.Enums @inherits ModuleBase @inject ITenantService TenantService diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor index 9c1ee9bf..d78ab5e5 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Add.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Themes -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService diff --git a/Oqtane.Client/Modules/Admin/Themes/Index.razor b/Oqtane.Client/Modules/Admin/Themes/Index.razor index 287b46f1..4a3113b6 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Index.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Themes -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IThemeService ThemeService diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index ea80e420..5d55b6aa 100644 --- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor +++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Upgrade -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor index 967a62b4..6cf41e3f 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.UserProfile -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserRoleService UserRoleService diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index 38ee0ac8..6711617a 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.UserProfile -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/UserProfile/View.razor b/Oqtane.Client/Modules/Admin/UserProfile/View.razor index 61a9920c..07140f63 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/View.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/View.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.UserProfile -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserRoleService UserRoleService diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor index a339b3df..561185f6 100644 --- a/Oqtane.Client/Modules/Admin/Users/Add.razor +++ b/Oqtane.Client/Modules/Admin/Users/Add.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Users -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index ba041d7d..a0b8370b 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Users -@using Oqtane.Enums @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index 0abeb865..a9d4366f 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Users -@using Oqtane.Enums @inherits ModuleBase @inject IUserRoleService UserRoleService @inject IUserService UserService diff --git a/Oqtane.Client/Modules/Admin/Users/Roles.razor b/Oqtane.Client/Modules/Admin/Users/Roles.razor index 22453aa0..fa84b46d 100644 --- a/Oqtane.Client/Modules/Admin/Users/Roles.razor +++ b/Oqtane.Client/Modules/Admin/Users/Roles.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Admin.Users -@using Oqtane.Enums @inherits ModuleBase @inject IRoleService RoleService @inject IUserRoleService UserRoleService diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor index 310abc06..08b2d77d 100644 --- a/Oqtane.Client/Modules/Controls/ActionDialog.razor +++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Controls -@using Oqtane.Enums @inherits ModuleBase @if (_visible) diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index 887f1b2f..cf588579 100644 --- a/Oqtane.Client/Modules/Controls/ActionLink.razor +++ b/Oqtane.Client/Modules/Controls/ActionLink.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Modules.Controls -@using Oqtane.Enums @inherits ModuleBase @inject IUserService UserService diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index e90603d9..41c55192 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -1,6 +1,6 @@ @using Oqtane.Modules.HtmlText.Services -@using Oqtane.Modules.Models.HtmlText -@using Oqtane.Enums +@using Oqtane.Modules.HtmlText.Models +@using Oqtane.Modules.Controls @namespace Oqtane.Modules.HtmlText @inherits ModuleBase @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/HtmlText/Index.razor b/Oqtane.Client/Modules/HtmlText/Index.razor index 3e5c35d6..47bf0b61 100644 --- a/Oqtane.Client/Modules/HtmlText/Index.razor +++ b/Oqtane.Client/Modules/HtmlText/Index.razor @@ -1,4 +1,5 @@ @using Oqtane.Modules.HtmlText.Services +@using Oqtane.Modules.HtmlText.Models @namespace Oqtane.Modules.HtmlText @inherits ModuleBase @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs index cfbe8a7f..1b3a7834 100644 --- a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs +++ b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; -using Oqtane.Modules.Models.HtmlText; +using Oqtane.Modules.HtmlText.Models; using Oqtane.Services; using Oqtane.Shared; diff --git a/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs b/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs index 246612a4..6ce1d646 100644 --- a/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs +++ b/Oqtane.Client/Modules/HtmlText/Services/IHtmlTextService.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Modules.Models.HtmlText; +using Oqtane.Modules.HtmlText.Models; namespace Oqtane.Modules.HtmlText.Services { diff --git a/Oqtane.Client/Modules/IModuleControl.cs b/Oqtane.Client/Modules/IModuleControl.cs index 4fb55d7b..c4f2fdee 100644 --- a/Oqtane.Client/Modules/IModuleControl.cs +++ b/Oqtane.Client/Modules/IModuleControl.cs @@ -1,5 +1,4 @@ -using Oqtane.Enums; -using Oqtane.Shared; +using Oqtane.Shared; namespace Oqtane.Modules { diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index db39b87d..1fa56c65 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Oqtane.Services; using System; using Oqtane.Enums; -using Oqtane.Services.Interfaces; using Oqtane.UI; namespace Oqtane.Modules diff --git a/Oqtane.Client/Modules/Weather/Index.razor b/Oqtane.Client/Modules/Weather/Index.razor index ae440590..2d20ff38 100644 --- a/Oqtane.Client/Modules/Weather/Index.razor +++ b/Oqtane.Client/Modules/Weather/Index.razor @@ -1,5 +1,4 @@ @using Oqtane.Modules.Weather.Services -@using Oqtane.Modules.Weather.Models @namespace Oqtane.Modules.Weather @inherits ModuleBase @@ -40,4 +39,4 @@ else WeatherForecastService forecastservice = new WeatherForecastService(); forecasts = await forecastservice.GetForecastAsync(DateTime.UtcNow); } -} +} \ No newline at end of file diff --git a/Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs b/Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs index ea5e7156..7e02ea8a 100644 --- a/Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs +++ b/Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs @@ -1,6 +1,6 @@ using System; -namespace Oqtane.Modules.Weather.Models +namespace Oqtane.Modules.Weather { public class WeatherForecast { diff --git a/Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs b/Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs index 2b6b9621..743848fb 100644 --- a/Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs +++ b/Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs @@ -1,6 +1,5 @@ using System; using System.Threading.Tasks; -using Oqtane.Modules.Weather.Models; namespace Oqtane.Modules.Weather.Services { diff --git a/Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs b/Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs index 1fde0100..e6ad13d9 100644 --- a/Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs +++ b/Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs @@ -2,7 +2,6 @@ using Oqtane.Modules; using System; using System.Linq; using System.Threading.Tasks; -using Oqtane.Modules.Weather.Models; namespace Oqtane.Modules.Weather.Services { diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index 3563396e..4172834b 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -1,19 +1,16 @@ -using System; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection; -using Oqtane.Interfaces; -using Oqtane.Models; -using Oqtane.Modules; -using Oqtane.Providers; +using System.Threading.Tasks; using Oqtane.Services; -using Oqtane.Services.Interfaces; +using System.Reflection; +using System; +using System.Linq; +using Oqtane.Modules; using Oqtane.Shared; +using Oqtane.Providers; +using Microsoft.AspNetCore.Components.Authorization; -namespace Oqtane +namespace Oqtane.Client { public class Program { diff --git a/Oqtane.Client/Services/AliasService.cs b/Oqtane.Client/Services/AliasService.cs index 8c3c2fa7..d7fdf3e6 100644 --- a/Oqtane.Client/Services/AliasService.cs +++ b/Oqtane.Client/Services/AliasService.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using Oqtane.Shared; using System.Net; using System; -using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index d4714d6f..3a875ac4 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using Oqtane.Models; -using Oqtane.Services.Interfaces; using Oqtane.Shared; using Oqtane.UI; diff --git a/Oqtane.Client/Services/FolderService.cs b/Oqtane.Client/Services/FolderService.cs index 44522474..642aeafa 100644 --- a/Oqtane.Client/Services/FolderService.cs +++ b/Oqtane.Client/Services/FolderService.cs @@ -8,7 +8,6 @@ using Oqtane.Shared; using System; using System.Diagnostics.CodeAnalysis; using System.Net; -using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/InstallationService.cs b/Oqtane.Client/Services/InstallationService.cs index 014aa436..40da3010 100644 --- a/Oqtane.Client/Services/InstallationService.cs +++ b/Oqtane.Client/Services/InstallationService.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using System.Net.Http; using Microsoft.AspNetCore.Components; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/Interfaces/IAliasService.cs b/Oqtane.Client/Services/Interfaces/IAliasService.cs index 2bd39d7e..f15356f1 100644 --- a/Oqtane.Client/Services/Interfaces/IAliasService.cs +++ b/Oqtane.Client/Services/Interfaces/IAliasService.cs @@ -1,9 +1,9 @@ -using System; +using Oqtane.Models; +using System; using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IAliasService { diff --git a/Oqtane.Client/Services/Interfaces/IFileService.cs b/Oqtane.Client/Services/Interfaces/IFileService.cs index 925f699d..7e10f4fd 100644 --- a/Oqtane.Client/Services/Interfaces/IFileService.cs +++ b/Oqtane.Client/Services/Interfaces/IFileService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IFileService { diff --git a/Oqtane.Client/Services/Interfaces/IFolderService.cs b/Oqtane.Client/Services/Interfaces/IFolderService.cs index 9e70b690..34edb609 100644 --- a/Oqtane.Client/Services/Interfaces/IFolderService.cs +++ b/Oqtane.Client/Services/Interfaces/IFolderService.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IFolderService { diff --git a/Oqtane.Client/Services/Interfaces/IInstallationService.cs b/Oqtane.Client/Services/Interfaces/IInstallationService.cs index 0485bdec..c9eeaca8 100644 --- a/Oqtane.Client/Services/Interfaces/IInstallationService.cs +++ b/Oqtane.Client/Services/Interfaces/IInstallationService.cs @@ -1,8 +1,8 @@ -using System.Threading.Tasks; -using Oqtane.Models; +using Oqtane.Models; +using System.Threading.Tasks; using Oqtane.Shared; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IInstallationService { diff --git a/Oqtane.Client/Services/Interfaces/IJobLogService.cs b/Oqtane.Client/Services/Interfaces/IJobLogService.cs index 70d0da46..015d83f0 100644 --- a/Oqtane.Client/Services/Interfaces/IJobLogService.cs +++ b/Oqtane.Client/Services/Interfaces/IJobLogService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IJobLogService { diff --git a/Oqtane.Client/Services/Interfaces/IJobService.cs b/Oqtane.Client/Services/Interfaces/IJobService.cs index 8e56454f..d6f0decf 100644 --- a/Oqtane.Client/Services/Interfaces/IJobService.cs +++ b/Oqtane.Client/Services/Interfaces/IJobService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IJobService { diff --git a/Oqtane.Client/Services/Interfaces/ILogService.cs b/Oqtane.Client/Services/Interfaces/ILogService.cs index cdcdd26a..30867c72 100644 --- a/Oqtane.Client/Services/Interfaces/ILogService.cs +++ b/Oqtane.Client/Services/Interfaces/ILogService.cs @@ -1,11 +1,11 @@ -using System; +using Oqtane.Models; +using Oqtane.Shared; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Oqtane.Enums; -using Oqtane.Models; -using Oqtane.Shared; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface ILogService { diff --git a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs index 88417a29..8560c661 100644 --- a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs +++ b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Oqtane.Models; +using Oqtane.Models; using Oqtane.UI; +using System.Collections.Generic; +using System.Threading.Tasks; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IModuleDefinitionService { diff --git a/Oqtane.Client/Services/Interfaces/IModuleService.cs b/Oqtane.Client/Services/Interfaces/IModuleService.cs index ad85a26f..27f11dff 100644 --- a/Oqtane.Client/Services/Interfaces/IModuleService.cs +++ b/Oqtane.Client/Services/Interfaces/IModuleService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IModuleService { diff --git a/Oqtane.Client/Services/Interfaces/INotificationService.cs b/Oqtane.Client/Services/Interfaces/INotificationService.cs index bc479803..bd52b7d0 100644 --- a/Oqtane.Client/Services/Interfaces/INotificationService.cs +++ b/Oqtane.Client/Services/Interfaces/INotificationService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface INotificationService { diff --git a/Oqtane.Client/Services/Interfaces/IPackageService.cs b/Oqtane.Client/Services/Interfaces/IPackageService.cs index a1e69541..eeb243c1 100644 --- a/Oqtane.Client/Services/Interfaces/IPackageService.cs +++ b/Oqtane.Client/Services/Interfaces/IPackageService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IPackageService { diff --git a/Oqtane.Client/Services/Interfaces/IPageModuleService.cs b/Oqtane.Client/Services/Interfaces/IPageModuleService.cs index 3cae8abc..f51c56a8 100644 --- a/Oqtane.Client/Services/Interfaces/IPageModuleService.cs +++ b/Oqtane.Client/Services/Interfaces/IPageModuleService.cs @@ -1,7 +1,7 @@ -using System.Threading.Tasks; -using Oqtane.Models; +using Oqtane.Models; +using System.Threading.Tasks; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IPageModuleService { diff --git a/Oqtane.Client/Services/Interfaces/IPageService.cs b/Oqtane.Client/Services/Interfaces/IPageService.cs index 4cd8dd69..60f1d86e 100644 --- a/Oqtane.Client/Services/Interfaces/IPageService.cs +++ b/Oqtane.Client/Services/Interfaces/IPageService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IPageService { diff --git a/Oqtane.Client/Services/Interfaces/IProfileService.cs b/Oqtane.Client/Services/Interfaces/IProfileService.cs index 83fd6c5d..cc079f3c 100644 --- a/Oqtane.Client/Services/Interfaces/IProfileService.cs +++ b/Oqtane.Client/Services/Interfaces/IProfileService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IProfileService { diff --git a/Oqtane.Client/Services/Interfaces/IRoleService.cs b/Oqtane.Client/Services/Interfaces/IRoleService.cs index 90373202..23ae0d05 100644 --- a/Oqtane.Client/Services/Interfaces/IRoleService.cs +++ b/Oqtane.Client/Services/Interfaces/IRoleService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IRoleService { diff --git a/Oqtane.Client/Services/Interfaces/ISettingService.cs b/Oqtane.Client/Services/Interfaces/ISettingService.cs index 9c128612..f7c06591 100644 --- a/Oqtane.Client/Services/Interfaces/ISettingService.cs +++ b/Oqtane.Client/Services/Interfaces/ISettingService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface ISettingService { diff --git a/Oqtane.Client/Services/Interfaces/ISiteService.cs b/Oqtane.Client/Services/Interfaces/ISiteService.cs index 0ca7847c..32ca6926 100644 --- a/Oqtane.Client/Services/Interfaces/ISiteService.cs +++ b/Oqtane.Client/Services/Interfaces/ISiteService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface ISiteService { diff --git a/Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs b/Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs index 5993b7f8..abb9a6c1 100644 --- a/Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs +++ b/Oqtane.Client/Services/Interfaces/ISiteTemplateService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface ISiteTemplateService { diff --git a/Oqtane.Client/Services/Interfaces/ISqlService.cs b/Oqtane.Client/Services/Interfaces/ISqlService.cs index 5c7127ee..f563a795 100644 --- a/Oqtane.Client/Services/Interfaces/ISqlService.cs +++ b/Oqtane.Client/Services/Interfaces/ISqlService.cs @@ -1,7 +1,7 @@ -using System.Threading.Tasks; -using Oqtane.Models; +using Oqtane.Models; +using System.Threading.Tasks; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface ISqlService { diff --git a/Oqtane.Client/Services/Interfaces/ITenantService.cs b/Oqtane.Client/Services/Interfaces/ITenantService.cs index 0b863bab..24bdd79b 100644 --- a/Oqtane.Client/Services/Interfaces/ITenantService.cs +++ b/Oqtane.Client/Services/Interfaces/ITenantService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface ITenantService { diff --git a/Oqtane.Client/Services/Interfaces/IThemeService.cs b/Oqtane.Client/Services/Interfaces/IThemeService.cs index 36e25e9f..8756042b 100644 --- a/Oqtane.Client/Services/Interfaces/IThemeService.cs +++ b/Oqtane.Client/Services/Interfaces/IThemeService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IThemeService { diff --git a/Oqtane.Client/Services/Interfaces/IUserRoleService.cs b/Oqtane.Client/Services/Interfaces/IUserRoleService.cs index 55d553db..25cb9ab4 100644 --- a/Oqtane.Client/Services/Interfaces/IUserRoleService.cs +++ b/Oqtane.Client/Services/Interfaces/IUserRoleService.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using Oqtane.Models; +using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IUserRoleService { diff --git a/Oqtane.Client/Services/Interfaces/IUserService.cs b/Oqtane.Client/Services/Interfaces/IUserService.cs index 9eba77b3..0a18b23d 100644 --- a/Oqtane.Client/Services/Interfaces/IUserService.cs +++ b/Oqtane.Client/Services/Interfaces/IUserService.cs @@ -1,7 +1,7 @@ -using System.Threading.Tasks; -using Oqtane.Models; +using Oqtane.Models; +using System.Threading.Tasks; -namespace Oqtane.Services.Interfaces +namespace Oqtane.Services { public interface IUserService { diff --git a/Oqtane.Client/Services/JobLogService.cs b/Oqtane.Client/Services/JobLogService.cs index 4cb22566..f564f46c 100644 --- a/Oqtane.Client/Services/JobLogService.cs +++ b/Oqtane.Client/Services/JobLogService.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/JobService.cs b/Oqtane.Client/Services/JobService.cs index 3903a375..b06b2fd7 100644 --- a/Oqtane.Client/Services/JobService.cs +++ b/Oqtane.Client/Services/JobService.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/LogService.cs b/Oqtane.Client/Services/LogService.cs index d278fbd7..a679ee3d 100644 --- a/Oqtane.Client/Services/LogService.cs +++ b/Oqtane.Client/Services/LogService.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Oqtane.Enums; using Oqtane.Models; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index f0aa9818..119aa9dd 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using System; using System.Reflection; -using Oqtane.Services.Interfaces; using Oqtane.Shared; using Oqtane.UI; diff --git a/Oqtane.Client/Services/ModuleService.cs b/Oqtane.Client/Services/ModuleService.cs index 4408fbd2..c78403e4 100644 --- a/Oqtane.Client/Services/ModuleService.cs +++ b/Oqtane.Client/Services/ModuleService.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/NotificationService.cs b/Oqtane.Client/Services/NotificationService.cs index 7eb28b28..6239ca8a 100644 --- a/Oqtane.Client/Services/NotificationService.cs +++ b/Oqtane.Client/Services/NotificationService.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Components; using Oqtane.Shared; using System.Collections.Generic; using System.Linq; -using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/PackageService.cs b/Oqtane.Client/Services/PackageService.cs index 117ec952..952ea4cb 100644 --- a/Oqtane.Client/Services/PackageService.cs +++ b/Oqtane.Client/Services/PackageService.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Oqtane.Shared; using System.Linq; -using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/PageModuleService.cs b/Oqtane.Client/Services/PageModuleService.cs index a2ae4b6c..11fece8c 100644 --- a/Oqtane.Client/Services/PageModuleService.cs +++ b/Oqtane.Client/Services/PageModuleService.cs @@ -2,7 +2,6 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/PageService.cs b/Oqtane.Client/Services/PageService.cs index c191f1e0..6cc832c7 100644 --- a/Oqtane.Client/Services/PageService.cs +++ b/Oqtane.Client/Services/PageService.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using Oqtane.Shared; using System; using System.Net; -using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/ProfileService.cs b/Oqtane.Client/Services/ProfileService.cs index 0f0fca14..4ceb4cac 100644 --- a/Oqtane.Client/Services/ProfileService.cs +++ b/Oqtane.Client/Services/ProfileService.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/RoleService.cs b/Oqtane.Client/Services/RoleService.cs index 614293af..708f0721 100644 --- a/Oqtane.Client/Services/RoleService.cs +++ b/Oqtane.Client/Services/RoleService.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/SettingService.cs b/Oqtane.Client/Services/SettingService.cs index 7082bf89..22ac7fc4 100644 --- a/Oqtane.Client/Services/SettingService.cs +++ b/Oqtane.Client/Services/SettingService.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/SiteService.cs b/Oqtane.Client/Services/SiteService.cs index eab64ad1..ce4a379c 100644 --- a/Oqtane.Client/Services/SiteService.cs +++ b/Oqtane.Client/Services/SiteService.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Linq; using Microsoft.AspNetCore.Components; using System.Collections.Generic; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/SiteTemplateService.cs b/Oqtane.Client/Services/SiteTemplateService.cs index 48cff86c..410f2abc 100644 --- a/Oqtane.Client/Services/SiteTemplateService.cs +++ b/Oqtane.Client/Services/SiteTemplateService.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/SqlService.cs b/Oqtane.Client/Services/SqlService.cs index 0f1440ea..52371df8 100644 --- a/Oqtane.Client/Services/SqlService.cs +++ b/Oqtane.Client/Services/SqlService.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Components; using Oqtane.Shared; using System.Collections.Generic; using System.Linq; -using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/TenantService.cs b/Oqtane.Client/Services/TenantService.cs index 14f7c277..d325fd91 100644 --- a/Oqtane.Client/Services/TenantService.cs +++ b/Oqtane.Client/Services/TenantService.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Components; using Oqtane.Shared; using System.Collections.Generic; using System.Linq; -using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/Services/ThemeService.cs b/Oqtane.Client/Services/ThemeService.cs index 0f633501..e872aae1 100644 --- a/Oqtane.Client/Services/ThemeService.cs +++ b/Oqtane.Client/Services/ThemeService.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using System.Reflection; using System; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/UserRoleService.cs b/Oqtane.Client/Services/UserRoleService.cs index 137462ca..66e9feb9 100644 --- a/Oqtane.Client/Services/UserRoleService.cs +++ b/Oqtane.Client/Services/UserRoleService.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane.Services diff --git a/Oqtane.Client/Services/UserService.cs b/Oqtane.Client/Services/UserService.cs index ac306e8e..d81077ff 100644 --- a/Oqtane.Client/Services/UserService.cs +++ b/Oqtane.Client/Services/UserService.cs @@ -3,7 +3,6 @@ using Oqtane.Models; using System.Net.Http; using Microsoft.AspNetCore.Components; using System.Threading.Tasks; -using Oqtane.Services.Interfaces; namespace Oqtane.Services { diff --git a/Oqtane.Client/UI/Pane.razor b/Oqtane.Client/UI/Pane.razor index 5ed6e109..9d0c7dd4 100644 --- a/Oqtane.Client/UI/Pane.razor +++ b/Oqtane.Client/UI/Pane.razor @@ -1,5 +1,4 @@ -@using Oqtane.Enums -@namespace Oqtane.UI +@namespace Oqtane.UI @inject IUserService UserService @inject IModuleService ModuleService @inject IModuleDefinitionService ModuleDefinitionService diff --git a/Oqtane.Client/_Imports.razor b/Oqtane.Client/_Imports.razor index 1c87349b..22d11669 100644 --- a/Oqtane.Client/_Imports.razor +++ b/Oqtane.Client/_Imports.razor @@ -14,8 +14,7 @@ @using Oqtane.Providers @using Oqtane.Security @using Oqtane.Services -@using Oqtane.Services.Interfaces @using Oqtane.Shared @using Oqtane.Themes @using Oqtane.Themes.Controls -@using Oqtane.UI +@using Oqtane.UI \ No newline at end of file diff --git a/Oqtane.Server/Controllers/AliasController.cs b/Oqtane.Server/Controllers/AliasController.cs index 922c71de..47f2d0a9 100644 --- a/Oqtane.Server/Controllers/AliasController.cs +++ b/Oqtane.Server/Controllers/AliasController.cs @@ -10,7 +10,6 @@ using System.Globalization; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 5a5da650..ad85e6c2 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -16,7 +16,6 @@ using System.Net; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; // ReSharper disable StringIndexOfIsCultureSpecific.1 diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index c2b22fc4..1a2fdb2f 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -8,7 +8,6 @@ using System.Net; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; using Oqtane.Security; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/JobController.cs b/Oqtane.Server/Controllers/JobController.cs index 6db3404e..838a62fb 100644 --- a/Oqtane.Server/Controllers/JobController.cs +++ b/Oqtane.Server/Controllers/JobController.cs @@ -9,7 +9,6 @@ using Microsoft.Extensions.DependencyInjection; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/JobLogController.cs b/Oqtane.Server/Controllers/JobLogController.cs index e1f766a2..f2a506d5 100644 --- a/Oqtane.Server/Controllers/JobLogController.cs +++ b/Oqtane.Server/Controllers/JobLogController.cs @@ -6,7 +6,6 @@ using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/LogController.cs b/Oqtane.Server/Controllers/LogController.cs index 575ee6c8..d59b9411 100644 --- a/Oqtane.Server/Controllers/LogController.cs +++ b/Oqtane.Server/Controllers/LogController.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Authorization; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index fe2064d8..348f973e 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -7,7 +7,6 @@ using System.Linq; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; using Oqtane.Security; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 1d7cd5ad..5e4659ce 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -12,8 +12,6 @@ using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; using Oqtane.Security; using System; -using Oqtane.Repository.Interfaces; - // ReSharper disable StringIndexOfIsCultureSpecific.1 namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/NotificationController.cs b/Oqtane.Server/Controllers/NotificationController.cs index 33c2a70f..47f0f85f 100644 --- a/Oqtane.Server/Controllers/NotificationController.cs +++ b/Oqtane.Server/Controllers/NotificationController.cs @@ -6,7 +6,6 @@ using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; using Oqtane.Security; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 7e73dd6a..607ceaef 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -9,7 +9,6 @@ using System.Net; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/PageModuleController.cs b/Oqtane.Server/Controllers/PageModuleController.cs index 057efe0e..c6149cbd 100644 --- a/Oqtane.Server/Controllers/PageModuleController.cs +++ b/Oqtane.Server/Controllers/PageModuleController.cs @@ -7,7 +7,6 @@ using System.Linq; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; using Oqtane.Security; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/ProfileController.cs b/Oqtane.Server/Controllers/ProfileController.cs index 21b5621d..1a3c0f40 100644 --- a/Oqtane.Server/Controllers/ProfileController.cs +++ b/Oqtane.Server/Controllers/ProfileController.cs @@ -6,7 +6,6 @@ using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/RoleController.cs b/Oqtane.Server/Controllers/RoleController.cs index 84ff016f..61f58f4a 100644 --- a/Oqtane.Server/Controllers/RoleController.cs +++ b/Oqtane.Server/Controllers/RoleController.cs @@ -6,7 +6,6 @@ using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index 0ccc0e6a..4c551098 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -7,7 +7,6 @@ using System.Linq; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index afa80966..a8db1073 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -7,7 +7,6 @@ using System.Linq; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/SiteTemplateController.cs b/Oqtane.Server/Controllers/SiteTemplateController.cs index c560d45e..c74e69a4 100644 --- a/Oqtane.Server/Controllers/SiteTemplateController.cs +++ b/Oqtane.Server/Controllers/SiteTemplateController.cs @@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Mvc; using Oqtane.Models; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/SqlController.cs b/Oqtane.Server/Controllers/SqlController.cs index 21c0e6fb..1ec75d19 100644 --- a/Oqtane.Server/Controllers/SqlController.cs +++ b/Oqtane.Server/Controllers/SqlController.cs @@ -11,7 +11,6 @@ using System.Data; using System.Dynamic; using Newtonsoft.Json; using System; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/TenantController.cs b/Oqtane.Server/Controllers/TenantController.cs index feb31480..83f84355 100644 --- a/Oqtane.Server/Controllers/TenantController.cs +++ b/Oqtane.Server/Controllers/TenantController.cs @@ -6,7 +6,6 @@ using Oqtane.Enums; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index 72eb2bb3..5fadf164 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Hosting; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; // ReSharper disable StringIndexOfIsCultureSpecific.1 diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 4cd74157..d2bc94db 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -13,7 +13,6 @@ using System.Net; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Controllers/UserRoleController.cs b/Oqtane.Server/Controllers/UserRoleController.cs index fcfc7665..fa3d2d6c 100644 --- a/Oqtane.Server/Controllers/UserRoleController.cs +++ b/Oqtane.Server/Controllers/UserRoleController.cs @@ -6,7 +6,6 @@ using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index e16ba3fb..aef47901 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -6,11 +6,7 @@ using System.Reflection; using System.Runtime.Loader; using Microsoft.Extensions.Hosting; using Oqtane.Infrastructure; -using Oqtane.Infrastructure.Jobs; -using Oqtane.Interfaces; -using Oqtane.Models; using Oqtane.Modules; -using File = System.IO.File; // ReSharper disable once CheckNamespace namespace Microsoft.Extensions.DependencyInjection diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 46f7b1f7..2d23f219 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -15,8 +15,6 @@ using Oqtane.Controllers; using Oqtane.Extensions; using Oqtane.Models; using Oqtane.Repository; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; using Oqtane.Shared; using File = System.IO.File; diff --git a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs index fdc562ad..35b8b0a9 100644 --- a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs +++ b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs @@ -6,10 +6,10 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Oqtane.Models; -using Oqtane.Repository.Interfaces; +using Oqtane.Repository; using Oqtane.Shared; -namespace Oqtane.Infrastructure.Jobs +namespace Oqtane.Infrastructure { public abstract class HostedServiceBase : IHostedService, IDisposable { diff --git a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs index 8b2e0a55..bd667486 100644 --- a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs +++ b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs @@ -5,10 +5,10 @@ using System.Net; using System.Net.Mail; using Microsoft.Extensions.DependencyInjection; using Oqtane.Models; -using Oqtane.Repository.Interfaces; +using Oqtane.Repository; using Oqtane.Shared; -namespace Oqtane.Infrastructure.Jobs +namespace Oqtane.Infrastructure { public class NotificationJob : HostedServiceBase { diff --git a/Oqtane.Server/Infrastructure/LogManager.cs b/Oqtane.Server/Infrastructure/LogManager.cs index e42c83c3..47101016 100644 --- a/Oqtane.Server/Infrastructure/LogManager.cs +++ b/Oqtane.Server/Infrastructure/LogManager.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; using Oqtane.Security; // ReSharper disable StringIndexOfIsCultureSpecific.2 // ReSharper disable StringIndexOfIsCultureSpecific.1 diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index 89b03b6f..45d61327 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -1,11 +1,11 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Hosting; +using Oqtane.Models; using Oqtane.Infrastructure.Interfaces; -using Oqtane.Models; -using Oqtane.Repository.Interfaces; +using System.Collections.Generic; +using Oqtane.Repository; +using Microsoft.AspNetCore.Hosting; using Oqtane.Shared; -namespace Oqtane.Infrastructure.SiteTemplates +namespace Oqtane.SiteTemplates { public class DefaultSiteTemplate : ISiteTemplate { diff --git a/Oqtane.Server/Infrastructure/SyncManager.cs b/Oqtane.Server/Infrastructure/SyncManager.cs index 676f123d..7828bbc3 100644 --- a/Oqtane.Server/Infrastructure/SyncManager.cs +++ b/Oqtane.Server/Infrastructure/SyncManager.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Infrastructure { diff --git a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs index fea49e28..01eb92cf 100644 --- a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs +++ b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; +using Oqtane.Modules.HtmlText.Models; using Oqtane.Modules.HtmlText.Repository; using Microsoft.AspNetCore.Http; using Oqtane.Shared; @@ -7,7 +8,6 @@ using System; using System.Collections.Generic; using Oqtane.Enums; using Oqtane.Infrastructure.Interfaces; -using Oqtane.Modules.Models.HtmlText; namespace Oqtane.Modules.HtmlText.Controllers { diff --git a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs index a7f6082a..f37ac523 100644 --- a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs +++ b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs @@ -1,7 +1,7 @@ using Oqtane.Models; +using Oqtane.Modules.HtmlText.Models; using Oqtane.Modules.HtmlText.Repository; using System.Net; -using Oqtane.Modules.Models.HtmlText; namespace Oqtane.Modules.HtmlText.Manager { diff --git a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs index c2efd6a5..70cd2065 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs @@ -1,11 +1,7 @@ using Microsoft.EntityFrameworkCore; +using Oqtane.Modules.HtmlText.Models; using Oqtane.Repository; using Microsoft.AspNetCore.Http; -using Oqtane.Interfaces; -using Oqtane.Models; -using Oqtane.Modules.Models.HtmlText; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Modules.HtmlText.Repository { diff --git a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs index 8163f82d..c97b0de7 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextRepository.cs @@ -1,8 +1,6 @@ using Microsoft.EntityFrameworkCore; using System.Linq; -using Oqtane.Interfaces; -using Oqtane.Models; -using Oqtane.Modules.Models.HtmlText; +using Oqtane.Modules.HtmlText.Models; namespace Oqtane.Modules.HtmlText.Repository { diff --git a/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs b/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs index ff7bd6b8..cbe5d0e4 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/IHtmlTextRepository.cs @@ -1,4 +1,4 @@ -using Oqtane.Modules.Models.HtmlText; +using Oqtane.Modules.HtmlText.Models; namespace Oqtane.Modules.HtmlText.Repository { diff --git a/Oqtane.Server/Pages/Login.cshtml b/Oqtane.Server/Pages/Login.cshtml index 44d794dc..f2eab15f 100644 --- a/Oqtane.Server/Pages/Login.cshtml +++ b/Oqtane.Server/Pages/Login.cshtml @@ -1,3 +1,3 @@ @page "/pages/login" -@namespace Oqtane.Pages +@namespace Oqtane.Pages @model Oqtane.Pages.LoginModel diff --git a/Oqtane.Server/Program.cs b/Oqtane.Server/Program.cs index 613f1271..e46805e6 100644 --- a/Oqtane.Server/Program.cs +++ b/Oqtane.Server/Program.cs @@ -1,10 +1,13 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +// DO NOT REMOVE - needed for client-side Blazor +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.Configuration; +using Microsoft.AspNetCore; using Microsoft.Extensions.DependencyInjection; -using Oqtane.Infrastructure; // DO NOT REMOVE - needed for client-side Blazor +using Oqtane.Infrastructure; -namespace Oqtane +namespace Oqtane.Server { public class Program { diff --git a/Oqtane.Server/Repository/AliasRepository.cs b/Oqtane.Server/Repository/AliasRepository.cs index 08220ff4..ccb26681 100644 --- a/Oqtane.Server/Repository/AliasRepository.cs +++ b/Oqtane.Server/Repository/AliasRepository.cs @@ -4,8 +4,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/Context/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs index f0804065..247006bd 100644 --- a/Oqtane.Server/Repository/Context/DBContextBase.cs +++ b/Oqtane.Server/Repository/Context/DBContextBase.cs @@ -4,11 +4,9 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; -using Oqtane.Interfaces; using Oqtane.Models; -using Oqtane.Repository.Interfaces; -namespace Oqtane.Repository.Context +namespace Oqtane.Repository { public class DBContextBase : IdentityUserContext { diff --git a/Oqtane.Server/Repository/Context/InstallationContext.cs b/Oqtane.Server/Repository/Context/InstallationContext.cs index 618fee17..7b971a16 100644 --- a/Oqtane.Server/Repository/Context/InstallationContext.cs +++ b/Oqtane.Server/Repository/Context/InstallationContext.cs @@ -1,7 +1,8 @@ -using Microsoft.EntityFrameworkCore; +using System.Diagnostics.CodeAnalysis; +using Microsoft.EntityFrameworkCore; using Oqtane.Models; -namespace Oqtane.Repository.Context +namespace Oqtane.Repository { public class InstallationContext : DbContext diff --git a/Oqtane.Server/Repository/Context/MasterDBContext.cs b/Oqtane.Server/Repository/Context/MasterDBContext.cs index cb6a75c6..65312819 100644 --- a/Oqtane.Server/Repository/Context/MasterDBContext.cs +++ b/Oqtane.Server/Repository/Context/MasterDBContext.cs @@ -2,10 +2,9 @@ using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; -using Oqtane.Interfaces; using Oqtane.Models; -namespace Oqtane.Repository.Context +namespace Oqtane.Repository { public class MasterDBContext : DbContext { diff --git a/Oqtane.Server/Repository/Context/TenantDBContext.cs b/Oqtane.Server/Repository/Context/TenantDBContext.cs index e865a11e..be085b20 100644 --- a/Oqtane.Server/Repository/Context/TenantDBContext.cs +++ b/Oqtane.Server/Repository/Context/TenantDBContext.cs @@ -1,9 +1,8 @@ using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Interfaces; -namespace Oqtane.Repository.Context +namespace Oqtane.Repository { public class TenantDBContext : DBContextBase { diff --git a/Oqtane.Server/Repository/FileRepository.cs b/Oqtane.Server/Repository/FileRepository.cs index 0939fc41..a74f2ce9 100644 --- a/Oqtane.Server/Repository/FileRepository.cs +++ b/Oqtane.Server/Repository/FileRepository.cs @@ -2,8 +2,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/FolderRepository.cs b/Oqtane.Server/Repository/FolderRepository.cs index 50a1b750..c83fa215 100644 --- a/Oqtane.Server/Repository/FolderRepository.cs +++ b/Oqtane.Server/Repository/FolderRepository.cs @@ -2,8 +2,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs b/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs index 87908391..4f258307 100644 --- a/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IAliasRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IFileRepository.cs b/Oqtane.Server/Repository/Interfaces/IFileRepository.cs index 8109595d..19715187 100644 --- a/Oqtane.Server/Repository/Interfaces/IFileRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IFileRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IFileRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs index 85105ddb..65256184 100644 --- a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IFolderRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs b/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs index 167c34fc..d5858da5 100644 --- a/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IJobLogRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IJobRepository.cs b/Oqtane.Server/Repository/Interfaces/IJobRepository.cs index 88bfbfdb..12be85c1 100644 --- a/Oqtane.Server/Repository/Interfaces/IJobRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IJobRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IJobRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ILogRepository.cs b/Oqtane.Server/Repository/Interfaces/ILogRepository.cs index 7ca86392..27752a03 100644 --- a/Oqtane.Server/Repository/Interfaces/ILogRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ILogRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface ILogRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs index ca711dc1..32c59549 100644 --- a/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IModuleDefinitionRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs index 2e108a9b..2ef4185d 100644 --- a/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IModuleRepository { diff --git a/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs b/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs index bf7dbdbd..ef53bc3a 100644 --- a/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface INotificationRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs b/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs index 3efea416..181fb629 100644 --- a/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IPageModuleRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IPageRepository.cs b/Oqtane.Server/Repository/Interfaces/IPageRepository.cs index 66a213a6..6db92dba 100644 --- a/Oqtane.Server/Repository/Interfaces/IPageRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPageRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IPageRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs index 00347d88..5be9bb50 100644 --- a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IPermissionRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs b/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs index 9e0d6edb..c9b4f34b 100644 --- a/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IProfileRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IProfileRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs b/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs index b9c0bc65..35f9315f 100644 --- a/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IRoleRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IRoleRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs b/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs index 64301777..976519fb 100644 --- a/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface ISettingRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs b/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs index da470601..5e05dccf 100644 --- a/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface ISiteRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs b/Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs index 7202cb94..659226ff 100644 --- a/Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISiteTemplateRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface ISiteTemplateRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs index 3b2d9f29..4bd04e91 100644 --- a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs @@ -1,7 +1,7 @@ using System.Data.SqlClient; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface ISqlRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs b/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs index 790f83b7..5993eb63 100644 --- a/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface ITenantRepository { diff --git a/Oqtane.Server/Repository/Interfaces/ITenantResolver.cs b/Oqtane.Server/Repository/Interfaces/ITenantResolver.cs index 2ee51aed..95e916f7 100644 --- a/Oqtane.Server/Repository/Interfaces/ITenantResolver.cs +++ b/Oqtane.Server/Repository/Interfaces/ITenantResolver.cs @@ -1,6 +1,6 @@ using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface ITenantResolver { diff --git a/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs b/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs index 595f752f..90afa3bd 100644 --- a/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IThemeRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IUserRepository.cs b/Oqtane.Server/Repository/Interfaces/IUserRepository.cs index be389cfe..be646e56 100644 --- a/Oqtane.Server/Repository/Interfaces/IUserRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IUserRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IUserRepository { diff --git a/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs b/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs index ad7fba60..c46a0e50 100644 --- a/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IUserRoleRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Repository.Interfaces +namespace Oqtane.Repository { public interface IUserRoleRepository { diff --git a/Oqtane.Server/Repository/JobLogRepository.cs b/Oqtane.Server/Repository/JobLogRepository.cs index 11db1cf2..440a483d 100644 --- a/Oqtane.Server/Repository/JobLogRepository.cs +++ b/Oqtane.Server/Repository/JobLogRepository.cs @@ -2,8 +2,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/JobRepository.cs b/Oqtane.Server/Repository/JobRepository.cs index d30f482d..cbe51c19 100644 --- a/Oqtane.Server/Repository/JobRepository.cs +++ b/Oqtane.Server/Repository/JobRepository.cs @@ -4,8 +4,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/LogRepository.cs b/Oqtane.Server/Repository/LogRepository.cs index a76ebbd0..1bcbc2be 100644 --- a/Oqtane.Server/Repository/LogRepository.cs +++ b/Oqtane.Server/Repository/LogRepository.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.Linq; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index a81a2363..db4a8339 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -5,8 +5,6 @@ using System.Reflection; using Microsoft.Extensions.Caching.Memory; using Oqtane.Models; using Oqtane.Modules; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/ModuleRepository.cs b/Oqtane.Server/Repository/ModuleRepository.cs index e6e6e44a..18d9df3b 100644 --- a/Oqtane.Server/Repository/ModuleRepository.cs +++ b/Oqtane.Server/Repository/ModuleRepository.cs @@ -7,8 +7,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Oqtane.Models; using Oqtane.Modules; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; using Module = Oqtane.Models.Module; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/NotificationRepository.cs b/Oqtane.Server/Repository/NotificationRepository.cs index 172c7364..8f34ff50 100644 --- a/Oqtane.Server/Repository/NotificationRepository.cs +++ b/Oqtane.Server/Repository/NotificationRepository.cs @@ -2,8 +2,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/PageModuleRepository.cs b/Oqtane.Server/Repository/PageModuleRepository.cs index 83524786..ee85270d 100644 --- a/Oqtane.Server/Repository/PageModuleRepository.cs +++ b/Oqtane.Server/Repository/PageModuleRepository.cs @@ -2,8 +2,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/PageRepository.cs b/Oqtane.Server/Repository/PageRepository.cs index 049c0090..9e106e74 100644 --- a/Oqtane.Server/Repository/PageRepository.cs +++ b/Oqtane.Server/Repository/PageRepository.cs @@ -2,8 +2,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/PermissionRepository.cs b/Oqtane.Server/Repository/PermissionRepository.cs index cb84c4c1..fd9670d3 100644 --- a/Oqtane.Server/Repository/PermissionRepository.cs +++ b/Oqtane.Server/Repository/PermissionRepository.cs @@ -5,8 +5,6 @@ using System.Text; using System.Text.Json; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/ProfileRepository.cs b/Oqtane.Server/Repository/ProfileRepository.cs index 542ec07e..8577ea78 100644 --- a/Oqtane.Server/Repository/ProfileRepository.cs +++ b/Oqtane.Server/Repository/ProfileRepository.cs @@ -2,8 +2,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/RoleRepository.cs b/Oqtane.Server/Repository/RoleRepository.cs index fb2c830f..2814d7e0 100644 --- a/Oqtane.Server/Repository/RoleRepository.cs +++ b/Oqtane.Server/Repository/RoleRepository.cs @@ -2,8 +2,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/SettingRepository.cs b/Oqtane.Server/Repository/SettingRepository.cs index e3744cfe..30b7f676 100644 --- a/Oqtane.Server/Repository/SettingRepository.cs +++ b/Oqtane.Server/Repository/SettingRepository.cs @@ -2,8 +2,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index adebe718..a2f7e33d 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -8,8 +8,6 @@ using Microsoft.Extensions.DependencyInjection; using Oqtane.Infrastructure.Interfaces; using Oqtane.Models; using Oqtane.Modules; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; using Oqtane.Shared; using Module = Oqtane.Models.Module; diff --git a/Oqtane.Server/Repository/SiteTemplateRepository.cs b/Oqtane.Server/Repository/SiteTemplateRepository.cs index 93123a85..c88934fd 100644 --- a/Oqtane.Server/Repository/SiteTemplateRepository.cs +++ b/Oqtane.Server/Repository/SiteTemplateRepository.cs @@ -5,7 +5,6 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Oqtane.Infrastructure.Interfaces; using Oqtane.Models; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/SqlRepository.cs b/Oqtane.Server/Repository/SqlRepository.cs index a7fef59c..c9bdf734 100644 --- a/Oqtane.Server/Repository/SqlRepository.cs +++ b/Oqtane.Server/Repository/SqlRepository.cs @@ -2,7 +2,6 @@ using System.Data; using System.Data.SqlClient; using Oqtane.Models; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/TenantRepository.cs b/Oqtane.Server/Repository/TenantRepository.cs index 94813ab8..6c22ba75 100644 --- a/Oqtane.Server/Repository/TenantRepository.cs +++ b/Oqtane.Server/Repository/TenantRepository.cs @@ -4,8 +4,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/TenantResolver.cs b/Oqtane.Server/Repository/TenantResolver.cs index 451c2b21..8a5b85c4 100644 --- a/Oqtane.Server/Repository/TenantResolver.cs +++ b/Oqtane.Server/Repository/TenantResolver.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Http; using Oqtane.Models; -using Oqtane.Repository.Interfaces; using Oqtane.Shared; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/ThemeRepository.cs b/Oqtane.Server/Repository/ThemeRepository.cs index 0ec55b2e..3bb3f7e9 100644 --- a/Oqtane.Server/Repository/ThemeRepository.cs +++ b/Oqtane.Server/Repository/ThemeRepository.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Oqtane.Models; -using Oqtane.Repository.Interfaces; using Oqtane.Themes; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/UserRepository.cs b/Oqtane.Server/Repository/UserRepository.cs index cd1e3c77..a9e51afe 100644 --- a/Oqtane.Server/Repository/UserRepository.cs +++ b/Oqtane.Server/Repository/UserRepository.cs @@ -2,8 +2,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Repository/UserRoleRepository.cs b/Oqtane.Server/Repository/UserRoleRepository.cs index 90c98ea7..79f8a629 100644 --- a/Oqtane.Server/Repository/UserRoleRepository.cs +++ b/Oqtane.Server/Repository/UserRoleRepository.cs @@ -2,8 +2,6 @@ using System.Linq; using Microsoft.EntityFrameworkCore; using Oqtane.Models; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; namespace Oqtane.Repository { diff --git a/Oqtane.Server/Security/ClaimsPrincipalFactory.cs b/Oqtane.Server/Security/ClaimsPrincipalFactory.cs index c279b0b5..1e945938 100644 --- a/Oqtane.Server/Security/ClaimsPrincipalFactory.cs +++ b/Oqtane.Server/Security/ClaimsPrincipalFactory.cs @@ -7,7 +7,6 @@ using Oqtane.Shared; using System.Collections.Generic; using System.Linq; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Security { diff --git a/Oqtane.Server/Security/UserPermissions.cs b/Oqtane.Server/Security/UserPermissions.cs index 8e0e56af..72108b5d 100644 --- a/Oqtane.Server/Security/UserPermissions.cs +++ b/Oqtane.Server/Security/UserPermissions.cs @@ -3,7 +3,6 @@ using Oqtane.Models; using System.Linq; using System.Security.Claims; using Oqtane.Repository; -using Oqtane.Repository.Interfaces; namespace Oqtane.Security { diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index e1ce6d62..46b5e697 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -17,11 +17,8 @@ using Microsoft.OpenApi.Models; using Oqtane.Infrastructure; using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; -using Oqtane.Repository.Context; -using Oqtane.Repository.Interfaces; using Oqtane.Security; using Oqtane.Services; -using Oqtane.Services.Interfaces; using Oqtane.Shared; namespace Oqtane diff --git a/Oqtane.Shared/Enums/LogLevel.cs b/Oqtane.Shared/Enums/LogLevel.cs index ac02c377..f772157e 100644 --- a/Oqtane.Shared/Enums/LogLevel.cs +++ b/Oqtane.Shared/Enums/LogLevel.cs @@ -1,4 +1,4 @@ -namespace Oqtane.Enums +namespace Oqtane.Shared { public enum LogLevel { diff --git a/Oqtane.Shared/Enums/SecurityAccessLevel.cs b/Oqtane.Shared/Enums/SecurityAccessLevel.cs index f82f2e42..7b993b60 100644 --- a/Oqtane.Shared/Enums/SecurityAccessLevel.cs +++ b/Oqtane.Shared/Enums/SecurityAccessLevel.cs @@ -1,4 +1,4 @@ -namespace Oqtane.Enums +namespace Oqtane.Shared { public enum SecurityAccessLevel { diff --git a/Oqtane.Shared/Interfaces/IAuditable.cs b/Oqtane.Shared/Interfaces/IAuditable.cs index bb961087..83cd1c1f 100644 --- a/Oqtane.Shared/Interfaces/IAuditable.cs +++ b/Oqtane.Shared/Interfaces/IAuditable.cs @@ -1,6 +1,6 @@ using System; -namespace Oqtane.Interfaces +namespace Oqtane.Models { public interface IAuditable { diff --git a/Oqtane.Shared/Interfaces/IDeletable.cs b/Oqtane.Shared/Interfaces/IDeletable.cs index da854e61..7880f8ff 100644 --- a/Oqtane.Shared/Interfaces/IDeletable.cs +++ b/Oqtane.Shared/Interfaces/IDeletable.cs @@ -1,6 +1,6 @@ using System; -namespace Oqtane.Interfaces +namespace Oqtane.Models { public interface IDeletable { diff --git a/Oqtane.Shared/Interfaces/IService.cs b/Oqtane.Shared/Interfaces/IService.cs index 4fbd91d0..b7330d81 100644 --- a/Oqtane.Shared/Interfaces/IService.cs +++ b/Oqtane.Shared/Interfaces/IService.cs @@ -1,4 +1,4 @@ -namespace Oqtane.Interfaces +namespace Oqtane.Modules { public interface IService { diff --git a/Oqtane.Shared/Models/Alias.cs b/Oqtane.Shared/Models/Alias.cs index a2d34872..5e54e382 100644 --- a/Oqtane.Shared/Models/Alias.cs +++ b/Oqtane.Shared/Models/Alias.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/File.cs b/Oqtane.Shared/Models/File.cs index e4aa47cf..ae20fa11 100644 --- a/Oqtane.Shared/Models/File.cs +++ b/Oqtane.Shared/Models/File.cs @@ -1,5 +1,4 @@ using System; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Folder.cs b/Oqtane.Shared/Models/Folder.cs index 23acb72f..5f04f4bb 100644 --- a/Oqtane.Shared/Models/Folder.cs +++ b/Oqtane.Shared/Models/Folder.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel.DataAnnotations.Schema; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Job.cs b/Oqtane.Shared/Models/Job.cs index 6a048bed..2d1f40fc 100644 --- a/Oqtane.Shared/Models/Job.cs +++ b/Oqtane.Shared/Models/Job.cs @@ -1,5 +1,4 @@ using System; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Module.cs b/Oqtane.Shared/Models/Module.cs index e51ffc55..927377a0 100644 --- a/Oqtane.Shared/Models/Module.cs +++ b/Oqtane.Shared/Models/Module.cs @@ -1,8 +1,6 @@ using Oqtane.Shared; using System; using System.ComponentModel.DataAnnotations.Schema; -using Oqtane.Enums; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/ModuleDefinition.cs b/Oqtane.Shared/Models/ModuleDefinition.cs index beafe72e..35a27840 100644 --- a/Oqtane.Shared/Models/ModuleDefinition.cs +++ b/Oqtane.Shared/Models/ModuleDefinition.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel.DataAnnotations.Schema; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Notification.cs b/Oqtane.Shared/Models/Notification.cs index f7b192f9..0d804e41 100644 --- a/Oqtane.Shared/Models/Notification.cs +++ b/Oqtane.Shared/Models/Notification.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel.DataAnnotations.Schema; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Page.cs b/Oqtane.Shared/Models/Page.cs index ec1a6d1c..f1268875 100644 --- a/Oqtane.Shared/Models/Page.cs +++ b/Oqtane.Shared/Models/Page.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel.DataAnnotations.Schema; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/PageModule.cs b/Oqtane.Shared/Models/PageModule.cs index 3e9d1f2a..7812861a 100644 --- a/Oqtane.Shared/Models/PageModule.cs +++ b/Oqtane.Shared/Models/PageModule.cs @@ -1,5 +1,4 @@ using System; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Permission.cs b/Oqtane.Shared/Models/Permission.cs index 30ed4bbd..aff910f4 100644 --- a/Oqtane.Shared/Models/Permission.cs +++ b/Oqtane.Shared/Models/Permission.cs @@ -1,5 +1,4 @@ using System; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Profile.cs b/Oqtane.Shared/Models/Profile.cs index e6835f6d..11ecec07 100644 --- a/Oqtane.Shared/Models/Profile.cs +++ b/Oqtane.Shared/Models/Profile.cs @@ -1,5 +1,4 @@ using System; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Role.cs b/Oqtane.Shared/Models/Role.cs index f0c560bd..c9a316b5 100644 --- a/Oqtane.Shared/Models/Role.cs +++ b/Oqtane.Shared/Models/Role.cs @@ -1,5 +1,4 @@ using System; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Setting.cs b/Oqtane.Shared/Models/Setting.cs index a030915c..d7742c84 100644 --- a/Oqtane.Shared/Models/Setting.cs +++ b/Oqtane.Shared/Models/Setting.cs @@ -1,5 +1,4 @@ using System; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs index e785fe17..4b6b7f86 100644 --- a/Oqtane.Shared/Models/Site.cs +++ b/Oqtane.Shared/Models/Site.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel.DataAnnotations.Schema; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/Tenant.cs b/Oqtane.Shared/Models/Tenant.cs index 40135ce6..3f0a518a 100644 --- a/Oqtane.Shared/Models/Tenant.cs +++ b/Oqtane.Shared/Models/Tenant.cs @@ -1,5 +1,4 @@ using System; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/User.cs b/Oqtane.Shared/Models/User.cs index 0c8db2eb..e74deef2 100644 --- a/Oqtane.Shared/Models/User.cs +++ b/Oqtane.Shared/Models/User.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel.DataAnnotations.Schema; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Models/UserRole.cs b/Oqtane.Shared/Models/UserRole.cs index b3771125..556a31a8 100644 --- a/Oqtane.Shared/Models/UserRole.cs +++ b/Oqtane.Shared/Models/UserRole.cs @@ -1,5 +1,4 @@ using System; -using Oqtane.Interfaces; namespace Oqtane.Models { diff --git a/Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs b/Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs index 8876b097..e51a41fd 100644 --- a/Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs +++ b/Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs @@ -1,10 +1,9 @@ using System; +using Oqtane.Models; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using Oqtane.Interfaces; -using Oqtane.Models; -namespace Oqtane.Modules.Models.HtmlText +namespace Oqtane.Modules.HtmlText.Models { [Table("HtmlText")] public class HtmlTextInfo : IAuditable diff --git a/Oqtane.Upgrade/Program.cs b/Oqtane.Upgrade/Program.cs index 517a6072..8aa70d3b 100644 --- a/Oqtane.Upgrade/Program.cs +++ b/Oqtane.Upgrade/Program.cs @@ -4,7 +4,7 @@ using System.IO.Compression; using System.Reflection; using System.Threading; -namespace Oqtane +namespace Oqtane.Upgrade { class Program { From 69d89aaaf9609fb5b0eb237e6941e35eeb765121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Vesel=C3=BD?= Date: Sat, 4 Apr 2020 20:39:43 +0200 Subject: [PATCH 114/265] Icon constants (#339) --- Oqtane.Server/Repository/SiteRepository.cs | 199 +++++++++--------- Oqtane.Shared/Shared/Icons.cs | 231 +++++++++++++++++++++ 2 files changed, 330 insertions(+), 100 deletions(-) create mode 100644 Oqtane.Shared/Shared/Icons.cs diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index a2f7e33d..cbbc889d 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -5,6 +5,7 @@ using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Oqtane.Extensions; using Oqtane.Infrastructure.Interfaces; using Oqtane.Models; using Oqtane.Modules; @@ -24,7 +25,6 @@ namespace Oqtane.Repository private readonly IModuleRepository _moduleRepository; private readonly IPageModuleRepository _pageModuleRepository; private readonly IModuleDefinitionRepository _moduleDefinitionRepository; - private readonly IPermissionRepository _permissionRepository; private readonly IServiceProvider _serviceProvider; @@ -43,7 +43,6 @@ namespace Oqtane.Repository _moduleRepository = moduleRepository; _pageModuleRepository = pageModuleRepository; _moduleDefinitionRepository = moduleDefinitionRepository; - _permissionRepository = permissionRepository; _serviceProvider = serviceProvider; _config = config; } @@ -55,307 +54,307 @@ namespace Oqtane.Repository pageTemplates.Add(new PageTemplate { Name = "Admin", Parent = "", Path = "admin", Icon = "", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Dashboard.Index).ToModuleDefinitionName(), Title = "Admin Dashboard", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Site Management", Parent = "Admin", Path = "admin/sites", Icon = "globe", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Site Management", Parent = "Admin", Path = "admin/sites", Icon = Icons.Globe, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sites.Index).ToModuleDefinitionName(), Title = "Site Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Site Settings", Parent = "Admin", Path = "admin/site", Icon = "home", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Site Settings", Parent = "Admin", Path = "admin/site", Icon = Icons.Home, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Site.Index).ToModuleDefinitionName(), Title = "Site Settings", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Page Management", Parent = "Admin", Path = "admin/pages", Icon = "layers", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Page Management", Parent = "Admin", Path = "admin/pages", Icon = Icons.Layers, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Pages.Index).ToModuleDefinitionName(), Title = "Page Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "User Management", Parent = "Admin", Path = "admin/users", Icon = "people", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "User Management", Parent = "Admin", Path = "admin/users", Icon = Icons.People, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Users.Index).ToModuleDefinitionName(), Title = "User Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Profile Management", Parent = "Admin", Path = "admin/profiles", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Profile Management", Parent = "Admin", Path = "admin/profiles", Icon = Icons.Person, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Profiles.Index).ToModuleDefinitionName(), Title = "Profile Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Role Management", Parent = "Admin", Path = "admin/roles", Icon = "lock-locked", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Role Management", Parent = "Admin", Path = "admin/roles", Icon = Icons.LockLocked, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Roles.Index).ToModuleDefinitionName(), Title = "Role Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Event Log", Parent = "Admin", Path = "admin/log", Icon = "magnifying-glass", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Event Log", Parent = "Admin", Path = "admin/log", Icon = Icons.MagnifyingGlass, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Logs.Index).ToModuleDefinitionName(), Title = "Event Log", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "File Management", Parent = "Admin", Path = "admin/files", Icon = "file", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "File Management", Parent = "Admin", Path = "admin/files", Icon = Icons.File, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Files.Index).ToModuleDefinitionName(), Title = "File Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Recycle Bin", Parent = "Admin", Path = "admin/recyclebin", Icon = "trash", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Recycle Bin", Parent = "Admin", Path = "admin/recyclebin", Icon = Icons.Trash, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.RecycleBin.Index).ToModuleDefinitionName(), Title = "Recycle Bin", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Tenant Management", Parent = "Admin", Path = "admin/tenants", Icon = "list", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Tenant Management", Parent = "Admin", Path = "admin/tenants", Icon = Icons.List, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Tenants.Index).ToModuleDefinitionName(), Title = "Tenant Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Module Management", Parent = "Admin", Path = "admin/modules", Icon = "browser", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Module Management", Parent = "Admin", Path = "admin/modules", Icon = Icons.Browser, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.ModuleDefinitions.Index).ToModuleDefinitionName(), Title = "Module Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = "brush", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = Icons.Brush, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Themes.Index).ToModuleDefinitionName(), Title = "Theme Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = "timer", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = Icons.Timer, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Jobs.Index).ToModuleDefinitionName(), Title = "Scheduled Jobs", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } @@ -369,91 +368,91 @@ namespace Oqtane.Repository IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sql.Index).ToModuleDefinitionName(), Title = "Sql Management", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Upgrade Service", Parent = "Admin", Path = "admin/upgrade", Icon = "aperture", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Upgrade Service", Parent = "Admin", Path = "admin/upgrade", Icon = Icons.Aperture, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Upgrade.Index).ToModuleDefinitionName(), Title = "Upgrade Service", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Login", Parent = "", Path = "login", Icon = "lock-locked", IsNavigation = false, IsPersonalizable = false, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Login", Parent = "", Path = "login", Icon = Icons.LockLocked, IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Login.Index).ToModuleDefinitionName(), Title = "User Login", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Register", Parent = "", Path = "register", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Register", Parent = "", Path = "register", Icon = Icons.Person, IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Register.Index).ToModuleDefinitionName(), Title = "User Registration", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } @@ -461,48 +460,48 @@ namespace Oqtane.Repository pageTemplates.Add(new PageTemplate { - Name = "Reset", Parent = "", Path = "reset", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Reset", Parent = "", Path = "reset", Icon = Icons.Person, IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Reset.Index).ToModuleDefinitionName(), Title = "Password Reset", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } }); pageTemplates.Add(new PageTemplate { - Name = "Profile", Parent = "", Path = "profile", Icon = "person", IsNavigation = false, IsPersonalizable = false, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions(new List + Name = "Profile", Parent = "", Path = "profile", Icon = Icons.Person, IsNavigation = false, IsPersonalizable = false, EditMode = false, + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.RegisteredRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = typeof(Oqtane.Modules.Admin.UserProfile.Index).ToModuleDefinitionName(), Title = "User Profile", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions(new List + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.RegisteredRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "" } } diff --git a/Oqtane.Shared/Shared/Icons.cs b/Oqtane.Shared/Shared/Icons.cs new file mode 100644 index 00000000..586d0bae --- /dev/null +++ b/Oqtane.Shared/Shared/Icons.cs @@ -0,0 +1,231 @@ +namespace Oqtane.Shared +{ + //Open Iconic icons set + public static class Icons + { + private const string Prefix = ""; //oi- + public const string AccountLogin = Prefix + "account-login"; + public const string AccountLogout = Prefix + "account-logout"; + public const string ActionRedo = Prefix + "action-redo"; + public const string ActionUndo = Prefix + "action-undo"; + public const string AlignCenter = Prefix + "align-center"; + public const string AlignLeft = Prefix + "align-left"; + public const string AlignRight = Prefix + "align-right"; + public const string Aperture = Prefix + "aperture"; + public const string ArrowBottom = Prefix + "arrow-bottom"; + public const string ArrowCircleBottom = Prefix + "arrow-circle-bottom"; + public const string ArrowCircleLeft = Prefix + "arrow-circle-left"; + public const string ArrowCircleRight = Prefix + "arrow-circle-right"; + public const string ArrowCircleTop = Prefix + "arrow-circle-top"; + public const string ArrowLeft = Prefix + "arrow-left"; + public const string ArrowRight = Prefix + "arrow-right"; + public const string ArrowThickBottom = Prefix + "arrow-thick-bottom"; + public const string ArrowThickLeft = Prefix + "arrow-thick-left"; + public const string ArrowThickRight = Prefix + "arrow-thick-right"; + public const string ArrowThickTop = Prefix + "arrow-thick-top"; + public const string ArrowTop = Prefix + "arrow-top"; + public const string AudioSpectrum = Prefix + "audio-spectrum"; + public const string Audio = Prefix + "audio"; + public const string Badge = Prefix + "badge"; + public const string Ban = Prefix + "ban"; + public const string BarChart = Prefix + "bar-chart"; + public const string Basket = Prefix + "basket"; + public const string BatteryEmpty = Prefix + "battery-empty"; + public const string BatteryFull = Prefix + "battery-full"; + public const string Beaker = Prefix + "beaker"; + public const string Bell = Prefix + "bell"; + public const string Bluetooth = Prefix + "bluetooth"; + public const string Bold = Prefix + "bold"; + public const string Bolt = Prefix + "bolt"; + public const string Book = Prefix + "book"; + public const string Bookmark = Prefix + "bookmark"; + public const string Box = Prefix + "box"; + public const string Briefcase = Prefix + "briefcase"; + public const string BritishPound = Prefix + "british-pound"; + public const string Browser = Prefix + "browser"; + public const string Brush = Prefix + "brush"; + public const string Bug = Prefix + "bug"; + public const string Bullhorn = Prefix + "bullhorn"; + public const string Calculator = Prefix + "calculator"; + public const string Calendar = Prefix + "calendar"; + public const string CameraSlr = Prefix + "camera-slr"; + public const string CaretBottom = Prefix + "caret-bottom"; + public const string CaretLeft = Prefix + "caret-left"; + public const string CaretRight = Prefix + "caret-right"; + public const string CaretTop = Prefix + "caret-top"; + public const string Cart = Prefix + "cart"; + public const string Chat = Prefix + "chat"; + public const string Check = Prefix + "check"; + public const string ChevronBottom = Prefix + "chevron-bottom"; + public const string ChevronLeft = Prefix + "chevron-left"; + public const string ChevronRight = Prefix + "chevron-right"; + public const string ChevronTop = Prefix + "chevron-top"; + public const string CircleCheck = Prefix + "circle-check"; + public const string CircleX = Prefix + "circle-x"; + public const string Clipboard = Prefix + "clipboard"; + public const string Clock = Prefix + "clock"; + public const string CloudDownload = Prefix + "cloud-download"; + public const string CloudUpload = Prefix + "cloud-upload"; + public const string Cloud = Prefix + "cloud"; + public const string Cloudy = Prefix + "cloudy"; + public const string Code = Prefix + "code"; + public const string Cog = Prefix + "cog"; + public const string CollapseDown = Prefix + "collapse-down"; + public const string CollapseLeft = Prefix + "collapse-left"; + public const string CollapseRight = Prefix + "collapse-right"; + public const string CollapseUp = Prefix + "collapse-up"; + public const string Command = Prefix + "command"; + public const string CommentSquare = Prefix + "comment-square"; + public const string Compass = Prefix + "compass"; + public const string Contrast = Prefix + "contrast"; + public const string Copywriting = Prefix + "copywriting"; + public const string CreditCard = Prefix + "credit-card"; + public const string Crop = Prefix + "crop"; + public const string Dashboard = Prefix + "dashboard"; + public const string DataTransferDownload = Prefix + "data-transfer-download"; + public const string DataTransferUpload = Prefix + "data-transfer-upload"; + public const string Delete = Prefix + "delete"; + public const string Dial = Prefix + "dial"; + public const string Document = Prefix + "document"; + public const string Dollar = Prefix + "dollar"; + public const string DoubleQuoteSansLeft = Prefix + "double-quote-sans-left"; + public const string DoubleQuoteSansRight = Prefix + "double-quote-sans-right"; + public const string DoubleQuoteSerifLeft = Prefix + "double-quote-serif-left"; + public const string DoubleQuoteSerifRight = Prefix + "double-quote-serif-right"; + public const string Droplet = Prefix + "droplet"; + public const string Eject = Prefix + "eject"; + public const string Elevator = Prefix + "elevator"; + public const string Ellipses = Prefix + "ellipses"; + public const string EnvelopeClosed = Prefix + "envelope-closed"; + public const string EnvelopeOpen = Prefix + "envelope-open"; + public const string Euro = Prefix + "euro"; + public const string Excerpt = Prefix + "excerpt"; + public const string ExpandDown = Prefix + "expand-down"; + public const string ExpandLeft = Prefix + "expand-left"; + public const string ExpandRight = Prefix + "expand-right"; + public const string ExpandUp = Prefix + "expand-up"; + public const string ExternalLink = Prefix + "external-link"; + public const string Eye = Prefix + "eye"; + public const string Eyedropper = Prefix + "eyedropper"; + public const string File = Prefix + "file"; + public const string Fire = Prefix + "fire"; + public const string Flag = Prefix + "flag"; + public const string Flash = Prefix + "flash"; + public const string Folder = Prefix + "folder"; + public const string Fork = Prefix + "fork"; + public const string FullscreenEnter = Prefix + "fullscreen-enter"; + public const string FullscreenExit = Prefix + "fullscreen-exit"; + public const string Globe = Prefix + "globe"; + public const string Graph = Prefix + "graph"; + public const string GridFourUp = Prefix + "grid-four-up"; + public const string GridThreeUp = Prefix + "grid-three-up"; + public const string GridTwoUp = Prefix + "grid-two-up"; + public const string HardDrive = Prefix + "hard-drive"; + public const string Header = Prefix + "header"; + public const string Headphones = Prefix + "headphones"; + public const string Heart = Prefix + "heart"; + public const string Home = Prefix + "home"; + public const string Image = Prefix + "image"; + public const string Inbox = Prefix + "inbox"; + public const string Infinity = Prefix + "infinity"; + public const string Info = Prefix + "info"; + public const string Italic = Prefix + "italic"; + public const string JustifyCenter = Prefix + "justify-center"; + public const string JustifyLeft = Prefix + "justify-left"; + public const string JustifyRight = Prefix + "justify-right"; + public const string Key = Prefix + "key"; + public const string Laptop = Prefix + "laptop"; + public const string Layers = Prefix + "layers"; + public const string Lightbulb = Prefix + "lightbulb"; + public const string LinkBroken = Prefix + "link-broken"; + public const string LinkIntact = Prefix + "link-intact"; + public const string ListRich = Prefix + "list-rich"; + public const string List = Prefix + "list"; + public const string Location = Prefix + "location"; + public const string LockLocked = Prefix + "lock-locked"; + public const string LockUnlocked = Prefix + "lock-unlocked"; + public const string LoopCircular = Prefix + "loop-circular"; + public const string LoopSquare = Prefix + "loop-square"; + public const string Loop = Prefix + "loop"; + public const string MagnifyingGlass = Prefix + "magnifying-glass"; + public const string MapMarker = Prefix + "map-marker"; + public const string Map = Prefix + "map"; + public const string MediaPause = Prefix + "media-pause"; + public const string MediaPlay = Prefix + "media-play"; + public const string MediaRecord = Prefix + "media-record"; + public const string MediaSkipBackward = Prefix + "media-skip-backward"; + public const string MediaSkipForward = Prefix + "media-skip-forward"; + public const string MediaStepBackward = Prefix + "media-step-backward"; + public const string MediaStepForward = Prefix + "media-step-forward"; + public const string MediaStop = Prefix + "media-stop"; + public const string MedicalCross = Prefix + "medical-cross"; + public const string Menu = Prefix + "menu"; + public const string Microphone = Prefix + "microphone"; + public const string Minus = Prefix + "minus"; + public const string Monitor = Prefix + "monitor"; + public const string Moon = Prefix + "moon"; + public const string Move = Prefix + "move"; + public const string MusicalNote = Prefix + "musical-note"; + public const string Paperclip = Prefix + "paperclip"; + public const string Pencil = Prefix + "pencil"; + public const string People = Prefix + "people"; + public const string Person = Prefix + "person"; + public const string Phone = Prefix + "phone"; + public const string PieChart = Prefix + "pie-chart"; + public const string Pin = Prefix + "pin"; + public const string PlayCircle = Prefix + "play-circle"; + public const string Plus = Prefix + "plus"; + public const string PowerStandby = Prefix + "power-standby"; + public const string Print = Prefix + "print"; + public const string Project = Prefix + "project"; + public const string Pulse = Prefix + "pulse"; + public const string PuzzlePiece = Prefix + "puzzle-piece"; + public const string QuestionMark = Prefix + "question-mark"; + public const string Rain = Prefix + "rain"; + public const string Random = Prefix + "random"; + public const string Reload = Prefix + "reload"; + public const string ResizeBoth = Prefix + "resize-both"; + public const string ResizeHeight = Prefix + "resize-height"; + public const string ResizeWidth = Prefix + "resize-width"; + public const string RssAlt = Prefix + "rss-alt"; + public const string Rss = Prefix + "rss"; + public const string Script = Prefix + "script"; + public const string ShareBoxed = Prefix + "share-boxed"; + public const string Share = Prefix + "share"; + public const string Shield = Prefix + "shield"; + public const string Signal = Prefix + "signal"; + public const string Signpost = Prefix + "signpost"; + public const string SortAscending = Prefix + "sort-ascending"; + public const string SortDescending = Prefix + "sort-descending"; + public const string Spreadsheet = Prefix + "spreadsheet"; + public const string Star = Prefix + "star"; + public const string Sun = Prefix + "sun"; + public const string Tablet = Prefix + "tablet"; + public const string Tag = Prefix + "tag"; + public const string Tags = Prefix + "tags"; + public const string Target = Prefix + "target"; + public const string Task = Prefix + "task"; + public const string Terminal = Prefix + "terminal"; + public const string Text = Prefix + "text"; + public const string ThumbDown = Prefix + "thumb-down"; + public const string ThumbUp = Prefix + "thumb-up"; + public const string Timer = Prefix + "timer"; + public const string Transfer = Prefix + "transfer"; + public const string Trash = Prefix + "trash"; + public const string Underline = Prefix + "underline"; + public const string VerticalAlignBottom = Prefix + "vertical-align-bottom"; + public const string VerticalAlignCenter = Prefix + "vertical-align-center"; + public const string VerticalAlignTop = Prefix + "vertical-align-top"; + public const string Video = Prefix + "video"; + public const string VolumeHigh = Prefix + "volume-high"; + public const string VolumeLow = Prefix + "volume-low"; + public const string VolumeOff = Prefix + "volume-off"; + public const string Warning = Prefix + "warning"; + public const string Wifi = Prefix + "wifi"; + public const string Wrench = Prefix + "wrench"; + public const string X = Prefix + "x"; + public const string Yen = Prefix + "yen"; + public const string ZoomIn = Prefix + "zoom-in"; + public const string ZoomOut = Prefix + "zoom-out"; + } +} From a2f756729c4f6989836d4b42ba76ca06ea350e72 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Sun, 5 Apr 2020 01:14:12 +0300 Subject: [PATCH 115/265] Use relative path instead --- Oqtane.Shared/Oqtane.Shared.csproj | 1 + Oqtane.Shared/Shared/Utilities.cs | 29 +++++++++++++++---- .../Oqtane.Shared.Tests/UtilitiesTests.cs | 26 +++++++---------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index 6a0c0d87..01053668 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -18,6 +18,7 @@ + diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 6e446d31..c828e9e9 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -2,6 +2,8 @@ using System.Globalization; using System.Text; using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; namespace Oqtane.Shared { @@ -17,14 +19,29 @@ namespace Oqtane.Shared public static string NavigateUrl(string alias, string path, string parameters) { - var uriBuilder = alias == string.Empty - ? new UriBuilder() - : new UriBuilder(alias); + if (!alias.StartsWith("/")) + { + alias = $"/{alias}"; + } - uriBuilder.Path = path; - uriBuilder.Query = parameters; + if (!path.StartsWith("/")) + { + path = $"/{path}"; + } - return uriBuilder.Uri.AbsoluteUri; + var pathPaseValue = alias == string.Empty + ? default + : new PathString(alias); + + var pathValue = path == string.Empty + ? default + : new PathString(path); + + var queryStringValue = parameters == string.Empty + ? default + : new QueryString($"?{parameters}"); + + return UriHelper.BuildRelative(pathPaseValue, pathValue, queryStringValue); } public static string EditUrl(string alias, string path, int moduleid, string action, string parameters) diff --git a/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs b/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs index 2966e8d0..ccafa193 100644 --- a/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs +++ b/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs @@ -6,22 +6,16 @@ namespace Oqtane.Test.Oqtane.Shared.Tests public class UtilitiesTests { [Theory] - [InlineData("contoso", "login", "returnUrl=/admin", "http://contoso/login?returnUrl=/admin")] - [InlineData("contoso", "admin", "", "http://contoso/admin")] - [InlineData("contoso", "", "pageId=4", "http://contoso/?pageId=4")] - [InlineData("contoso", "", "", "http://contoso/")] - [InlineData("http://contoso", "login", "returnUrl=/admin", "http://contoso/login?returnUrl=/admin")] - [InlineData("http://contoso", "admin", "", "http://contoso/admin")] - [InlineData("http://contoso", "", "pageId=4", "http://contoso/?pageId=4")] - [InlineData("http://contoso", "", "", "http://contoso/")] - [InlineData("https://contoso", "login", "returnUrl=/admin", "https://contoso/login?returnUrl=/admin")] - [InlineData("https://contoso", "admin", "", "https://contoso/admin")] - [InlineData("https://contoso", "", "pageId=4", "https://contoso/?pageId=4")] - [InlineData("https://contoso", "", "", "https://contoso/")] - [InlineData("", "login", "returnUrl=/admin", "http://localhost/login?returnUrl=/admin")] - [InlineData("", "admin", "", "http://localhost/admin")] - [InlineData("", "", "pageId=4", "http://localhost/?pageId=4")] - [InlineData("", "", "", "http://localhost/")] + [InlineData("contoso", "login", "returnUrl=/admin", "/contoso/login?returnUrl=/admin")] + [InlineData("contoso", "admin", "", "/contoso/admin")] + [InlineData("contoso", "", "pageId=4", "/contoso/?pageId=4")] + [InlineData("contoso", "", "pageId=4&moduleId=10", "/contoso/?pageId=4&moduleId=10")] + [InlineData("contoso", "", "", "/contoso/")] + [InlineData("", "login", "returnUrl=/admin", "/login?returnUrl=/admin")] + [InlineData("", "admin", "", "/admin")] + [InlineData("", "", "pageId=4", "/?pageId=4")] + [InlineData("", "", "pageId=4&moduleId=10", "/?pageId=4&moduleId=10")] + [InlineData("", "", "", "/")] public void NavigateUrlTest(string alias, string path, string parameters, string expectedUrl) { // Arrange From 05de65d3a0c8bd39bf5a3bf00c7f41474d79f928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Vesel=C3=BD?= Date: Sun, 5 Apr 2020 19:58:50 +0200 Subject: [PATCH 116/265] Webassembly problem solved (#342) --- Oqtane.Server/Startup.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 46b5e697..ed8e8678 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -222,6 +222,8 @@ namespace Oqtane app.UseStaticFiles(); + app.UseBlazorFrameworkFiles(); + app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); From 02fde9cec3a73e3ef438c54e3571ad6254de3f4a Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sun, 5 Apr 2020 14:39:08 -0400 Subject: [PATCH 117/265] rolled back change creating an Infrastructure.Interfaces namespace, modified IModule interface to be strongly typed (#343) * upgrade to .NET Core 3.2 Preview 3 and fixes for issues created by #314 * Components based on Bootstrap4 for Sections and TabStrip to increase productivity and promote uniformity in Module UIs * rolled back change creating an Infrastructure.Interfaces namespace, modified IModule interface to be strongly typed --- .../Modules/Admin/ModuleCreator/Module.cs | 21 ++++------ .../Modules/[Module]/[Module].cs | 22 ++++------ .../Controllers/[Module]Controller.cs | 2 +- Oqtane.Client/Modules/Counter/Module.cs | 19 +++------ Oqtane.Client/Modules/HtmlText/ModuleInfo.cs | 21 ++++------ Oqtane.Client/Modules/IModule.cs | 4 +- Oqtane.Client/Modules/Weather/Module.cs | 19 +++------ Oqtane.Server/Controllers/AliasController.cs | 2 +- Oqtane.Server/Controllers/FileController.cs | 2 +- Oqtane.Server/Controllers/FolderController.cs | 2 +- .../Controllers/InstallationController.cs | 1 - Oqtane.Server/Controllers/JobController.cs | 2 +- Oqtane.Server/Controllers/JobLogController.cs | 2 +- Oqtane.Server/Controllers/LogController.cs | 2 +- Oqtane.Server/Controllers/ModuleController.cs | 2 +- .../Controllers/ModuleDefinitionController.cs | 2 +- .../Controllers/NotificationController.cs | 2 +- Oqtane.Server/Controllers/PageController.cs | 2 +- .../Controllers/PageModuleController.cs | 2 +- .../Controllers/ProfileController.cs | 2 +- Oqtane.Server/Controllers/RoleController.cs | 2 +- .../Controllers/SettingController.cs | 2 +- Oqtane.Server/Controllers/SiteController.cs | 2 +- Oqtane.Server/Controllers/SqlController.cs | 2 +- Oqtane.Server/Controllers/TenantController.cs | 2 +- Oqtane.Server/Controllers/ThemeController.cs | 2 +- Oqtane.Server/Controllers/UserController.cs | 2 +- .../Controllers/UserRoleController.cs | 2 +- .../Infrastructure/InstallationManager.cs | 1 - .../Interfaces/IInstallationManager.cs | 2 +- .../Infrastructure/Interfaces/ILogManager.cs | 2 +- .../Interfaces/ISiteTemplate.cs | 2 +- .../Infrastructure/Interfaces/ISyncManager.cs | 2 +- Oqtane.Server/Infrastructure/LogManager.cs | 1 - .../SiteTemplates/DefaultSiteTemplate.cs | 2 +- Oqtane.Server/Infrastructure/SyncManager.cs | 2 +- .../Controllers/HtmlTextController.cs | 2 +- .../Repository/ModuleDefinitionRepository.cs | 40 +++---------------- Oqtane.Server/Repository/SiteRepository.cs | 2 +- .../Repository/SiteTemplateRepository.cs | 2 +- Oqtane.Server/Security/PermissionHandler.cs | 2 +- Oqtane.Server/Startup.cs | 1 - Oqtane.Shared/Models/ModuleDefinition.cs | 20 +++++++++- 43 files changed, 91 insertions(+), 141 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Module.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Module.cs index 0760879b..c57bf1a9 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Module.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Module.cs @@ -1,22 +1,15 @@ -using System.Collections.Generic; +using Oqtane.Models; namespace Oqtane.Modules.Admin.ModuleCreator { public class Module : IModule { - public Dictionary Properties + public ModuleDefinition ModuleDefinition => new ModuleDefinition { - get - { - Dictionary properties = new Dictionary - { - { "Name", "Module Creator" }, - { "Description", "Enables software developers to quickly create modules by automating many of the initial module creation tasks" }, - { "Version", "1.0.0" }, - { "Categories", "Developer" } - }; - return properties; - } - } + Name = "Module Creator", + Description = "Enables software developers to quickly create modules by automating many of the initial module creation tasks", + Version = "1.0.0", + Categories = "Developer" + }; } } diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module].cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module].cs index 040fd244..630b488e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module].cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module].cs @@ -1,22 +1,16 @@ -using System.Collections.Generic; +using Oqtane.Models; +using Oqtane.Modules; namespace Oqtane.Modules.[Module]s { public class Module : IModule { - public Dictionary Properties + public ModuleDefinition ModuleDefinition => new ModuleDefinition { - get - { - Dictionary properties = new Dictionary - { - { "Name", "[Module]" }, - { "Description", "[Module]" }, - { "Version", "1.0.0" }, - { "ServerAssemblyName", "Oqtane.Server" } - }; - return properties; - } - } + Name = "[Module]", + Description = "[Module]", + Version = "1.0.0", + ServerAssemblyName = "Oqtane.Server" + }; } } diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs index 023e42a4..9fcb319e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs @@ -5,7 +5,7 @@ using Oqtane.Repository.[Module]s; using Oqtane.Shared; using System.Collections.Generic; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; namespace Oqtane.Controllers.[Module]s { diff --git a/Oqtane.Client/Modules/Counter/Module.cs b/Oqtane.Client/Modules/Counter/Module.cs index 5f509ae2..dd2e0e35 100644 --- a/Oqtane.Client/Modules/Counter/Module.cs +++ b/Oqtane.Client/Modules/Counter/Module.cs @@ -1,21 +1,14 @@ -using System.Collections.Generic; +using Oqtane.Models; namespace Oqtane.Modules.Counter { public class Module : IModule { - public Dictionary Properties + public ModuleDefinition ModuleDefinition => new ModuleDefinition { - get - { - Dictionary properties = new Dictionary - { - { "Name", "Counter" }, - { "Description", "Increments a counter" }, - { "Version", "1.0.0" } - }; - return properties; - } - } + Name = "Counter", + Description = "Increments a counter", + Version = "1.0.0" + }; } } diff --git a/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs b/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs index df585a05..0d4ad914 100644 --- a/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs +++ b/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs @@ -1,22 +1,15 @@ -using System.Collections.Generic; +using Oqtane.Models; namespace Oqtane.Modules.HtmlText { public class ModuleInfo : IModule { - public Dictionary Properties + public ModuleDefinition ModuleDefinition => new ModuleDefinition { - get - { - Dictionary properties = new Dictionary - { - { "Name", "HtmlText" }, - { "Description", "Renders HTML or Text" }, - { "Version", "1.0.0" }, - { "ServerAssemblyName", "Oqtane.Server" } - }; - return properties; - } - } + Name = "HtmlText", + Description = "Renders HTML or Text", + Version = "1.0.0", + ServerAssemblyName = "Oqtane.Server" + }; } } diff --git a/Oqtane.Client/Modules/IModule.cs b/Oqtane.Client/Modules/IModule.cs index 675378b8..f0ecfacf 100644 --- a/Oqtane.Client/Modules/IModule.cs +++ b/Oqtane.Client/Modules/IModule.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; +using Oqtane.Models; namespace Oqtane.Modules { public interface IModule { - Dictionary Properties { get; } + ModuleDefinition ModuleDefinition { get; } } } diff --git a/Oqtane.Client/Modules/Weather/Module.cs b/Oqtane.Client/Modules/Weather/Module.cs index 65d719d8..99922df4 100644 --- a/Oqtane.Client/Modules/Weather/Module.cs +++ b/Oqtane.Client/Modules/Weather/Module.cs @@ -1,21 +1,14 @@ -using System.Collections.Generic; +using Oqtane.Models; namespace Oqtane.Modules.Weather { public class Module : IModule { - public Dictionary Properties + public ModuleDefinition ModuleDefinition => new ModuleDefinition { - get - { - Dictionary properties = new Dictionary - { - { "Name", "Weather" }, - { "Description", "Displays random weather using a service" }, - { "Version", "1.0.0" } - }; - return properties; - } - } + Name = "Weather", + Description = "Displays random weather using a service", + Version = "1.0.0" + }; } } diff --git a/Oqtane.Server/Controllers/AliasController.cs b/Oqtane.Server/Controllers/AliasController.cs index 47f2d0a9..6e5f6379 100644 --- a/Oqtane.Server/Controllers/AliasController.cs +++ b/Oqtane.Server/Controllers/AliasController.cs @@ -8,7 +8,7 @@ using System; using System.Net; using System.Globalization; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index ad85e6c2..4256e17f 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -14,7 +14,7 @@ using System.Linq; using System.Drawing; using System.Net; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; // ReSharper disable StringIndexOfIsCultureSpecific.1 diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 1a2fdb2f..ee75a536 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -6,7 +6,7 @@ using Oqtane.Shared; using System.Linq; using System.Net; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index bb99f23f..c812c47e 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Configuration; using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure; -using Oqtane.Infrastructure.Interfaces; // ReSharper disable StringIndexOfIsCultureSpecific.1 diff --git a/Oqtane.Server/Controllers/JobController.cs b/Oqtane.Server/Controllers/JobController.cs index 838a62fb..6d5c5c7b 100644 --- a/Oqtane.Server/Controllers/JobController.cs +++ b/Oqtane.Server/Controllers/JobController.cs @@ -7,7 +7,7 @@ using System; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/JobLogController.cs b/Oqtane.Server/Controllers/JobLogController.cs index f2a506d5..15b2714b 100644 --- a/Oqtane.Server/Controllers/JobLogController.cs +++ b/Oqtane.Server/Controllers/JobLogController.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Authorization; using Oqtane.Enums; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/LogController.cs b/Oqtane.Server/Controllers/LogController.cs index d59b9411..9e62bd0c 100644 --- a/Oqtane.Server/Controllers/LogController.cs +++ b/Oqtane.Server/Controllers/LogController.cs @@ -2,7 +2,7 @@ using Oqtane.Models; using System.Collections.Generic; using Microsoft.AspNetCore.Authorization; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Shared; diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index 348f973e..e87a7a5a 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -5,7 +5,7 @@ using Oqtane.Models; using Oqtane.Shared; using System.Linq; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 5e4659ce..3f0f1e84 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -8,7 +8,7 @@ using System.Reflection; using System.Linq; using Microsoft.AspNetCore.Hosting; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; using System; diff --git a/Oqtane.Server/Controllers/NotificationController.cs b/Oqtane.Server/Controllers/NotificationController.cs index 47f0f85f..ebf7489b 100644 --- a/Oqtane.Server/Controllers/NotificationController.cs +++ b/Oqtane.Server/Controllers/NotificationController.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Authorization; using Oqtane.Enums; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 607ceaef..2b87c7e4 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -7,7 +7,7 @@ using System.Linq; using Oqtane.Security; using System.Net; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/PageModuleController.cs b/Oqtane.Server/Controllers/PageModuleController.cs index c6149cbd..fc8e92cc 100644 --- a/Oqtane.Server/Controllers/PageModuleController.cs +++ b/Oqtane.Server/Controllers/PageModuleController.cs @@ -5,7 +5,7 @@ using Oqtane.Models; using Oqtane.Shared; using System.Linq; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; diff --git a/Oqtane.Server/Controllers/ProfileController.cs b/Oqtane.Server/Controllers/ProfileController.cs index 1a3c0f40..aad21208 100644 --- a/Oqtane.Server/Controllers/ProfileController.cs +++ b/Oqtane.Server/Controllers/ProfileController.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Authorization; using Oqtane.Enums; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/RoleController.cs b/Oqtane.Server/Controllers/RoleController.cs index 61f58f4a..956fad8f 100644 --- a/Oqtane.Server/Controllers/RoleController.cs +++ b/Oqtane.Server/Controllers/RoleController.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Authorization; using Oqtane.Enums; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index 4c551098..2599fde7 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -5,7 +5,7 @@ using Oqtane.Shared; using Oqtane.Security; using System.Linq; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index a8db1073..1adfe517 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -5,7 +5,7 @@ using Oqtane.Models; using Oqtane.Shared; using System.Linq; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/SqlController.cs b/Oqtane.Server/Controllers/SqlController.cs index 1ec75d19..a884ab35 100644 --- a/Oqtane.Server/Controllers/SqlController.cs +++ b/Oqtane.Server/Controllers/SqlController.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Oqtane.Models; using System.Collections.Generic; using Oqtane.Shared; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Enums; using System.Data.SqlClient; diff --git a/Oqtane.Server/Controllers/TenantController.cs b/Oqtane.Server/Controllers/TenantController.cs index 83f84355..03e4b1f4 100644 --- a/Oqtane.Server/Controllers/TenantController.cs +++ b/Oqtane.Server/Controllers/TenantController.cs @@ -4,7 +4,7 @@ using Oqtane.Models; using System.Collections.Generic; using Oqtane.Enums; using Oqtane.Shared; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index 5fadf164..120e5d96 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -8,7 +8,7 @@ using System.Reflection; using System.Linq; using Microsoft.AspNetCore.Hosting; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; // ReSharper disable StringIndexOfIsCultureSpecific.1 diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index d2bc94db..d0f5d96f 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -11,7 +11,7 @@ using Oqtane.Shared; using System; using System.Net; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Controllers/UserRoleController.cs b/Oqtane.Server/Controllers/UserRoleController.cs index fa3d2d6c..c159cd0a 100644 --- a/Oqtane.Server/Controllers/UserRoleController.cs +++ b/Oqtane.Server/Controllers/UserRoleController.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Authorization; using Oqtane.Enums; using Oqtane.Models; using Oqtane.Shared; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Controllers diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index 3825d454..82eab80f 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -7,7 +7,6 @@ using System.Xml; using Oqtane.Shared; using System; using System.Diagnostics; -using Oqtane.Infrastructure.Interfaces; using Microsoft.Extensions.Caching.Memory; namespace Oqtane.Infrastructure diff --git a/Oqtane.Server/Infrastructure/Interfaces/IInstallationManager.cs b/Oqtane.Server/Infrastructure/Interfaces/IInstallationManager.cs index a6d0fa90..f34165f0 100644 --- a/Oqtane.Server/Infrastructure/Interfaces/IInstallationManager.cs +++ b/Oqtane.Server/Infrastructure/Interfaces/IInstallationManager.cs @@ -1,4 +1,4 @@ -namespace Oqtane.Infrastructure.Interfaces +namespace Oqtane.Infrastructure { public interface IInstallationManager { diff --git a/Oqtane.Server/Infrastructure/Interfaces/ILogManager.cs b/Oqtane.Server/Infrastructure/Interfaces/ILogManager.cs index 20808f87..bd52214c 100644 --- a/Oqtane.Server/Infrastructure/Interfaces/ILogManager.cs +++ b/Oqtane.Server/Infrastructure/Interfaces/ILogManager.cs @@ -3,7 +3,7 @@ using Oqtane.Enums; using Oqtane.Models; using Oqtane.Shared; -namespace Oqtane.Infrastructure.Interfaces +namespace Oqtane.Infrastructure { public interface ILogManager { diff --git a/Oqtane.Server/Infrastructure/Interfaces/ISiteTemplate.cs b/Oqtane.Server/Infrastructure/Interfaces/ISiteTemplate.cs index 20b8cdb8..4d987a97 100644 --- a/Oqtane.Server/Infrastructure/Interfaces/ISiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/Interfaces/ISiteTemplate.cs @@ -1,7 +1,7 @@ using Oqtane.Models; using System.Collections.Generic; -namespace Oqtane.Infrastructure.Interfaces +namespace Oqtane.Infrastructure { public interface ISiteTemplate { diff --git a/Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs b/Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs index 3b821f38..e90ae246 100644 --- a/Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs +++ b/Oqtane.Server/Infrastructure/Interfaces/ISyncManager.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Oqtane.Models; -namespace Oqtane.Infrastructure.Interfaces +namespace Oqtane.Infrastructure { public interface ISyncManager { diff --git a/Oqtane.Server/Infrastructure/LogManager.cs b/Oqtane.Server/Infrastructure/LogManager.cs index 47101016..a213cb10 100644 --- a/Oqtane.Server/Infrastructure/LogManager.cs +++ b/Oqtane.Server/Infrastructure/LogManager.cs @@ -6,7 +6,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.AspNetCore.Http; using System.Collections.Generic; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; using Oqtane.Security; // ReSharper disable StringIndexOfIsCultureSpecific.2 diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index 45d61327..daab95f1 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -1,5 +1,5 @@ using Oqtane.Models; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using System.Collections.Generic; using Oqtane.Repository; using Microsoft.AspNetCore.Hosting; diff --git a/Oqtane.Server/Infrastructure/SyncManager.cs b/Oqtane.Server/Infrastructure/SyncManager.cs index 7828bbc3..63f0b648 100644 --- a/Oqtane.Server/Infrastructure/SyncManager.cs +++ b/Oqtane.Server/Infrastructure/SyncManager.cs @@ -3,7 +3,7 @@ using Oqtane.Models; using System; using System.Collections.Generic; using System.Linq; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Repository; namespace Oqtane.Infrastructure diff --git a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs index 01eb92cf..5eb6f37f 100644 --- a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs +++ b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs @@ -7,7 +7,7 @@ using Oqtane.Shared; using System; using System.Collections.Generic; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; namespace Oqtane.Modules.HtmlText.Controllers { diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index db4a8339..a97992c4 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -148,50 +148,22 @@ namespace Oqtane.Repository if (moduletype != null) { var moduleobject = Activator.CreateInstance(moduletype); - Dictionary properties = (Dictionary)moduletype.GetProperty("Properties").GetValue(moduleobject); - moduledefinition = new ModuleDefinition - { - ModuleDefinitionName = qualifiedModuleType, - Name = GetProperty(properties, "Name"), - Description = GetProperty(properties, "Description"), - Categories = GetProperty(properties, "Categories"), - Version = GetProperty(properties, "Version"), - Owner = GetProperty(properties, "Owner"), - Url = GetProperty(properties, "Url"), - Contact = GetProperty(properties, "Contact"), - License = GetProperty(properties, "License"), - Dependencies = GetProperty(properties, "Dependencies"), - PermissionNames = GetProperty(properties, "PermissionNames"), - ServerAssemblyName = GetProperty(properties, "ServerAssemblyName"), - ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1], - ControlTypeRoutes = "", - AssemblyName = assembly.FullName.Split(",")[0], - Permissions = "" - }; + moduledefinition = (ModuleDefinition)moduletype.GetProperty("ModuleDefinition").GetValue(moduleobject); } else { moduledefinition = new ModuleDefinition { - ModuleDefinitionName = qualifiedModuleType, Name = moduleType.Substring(moduleType.LastIndexOf(".") + 1), Description = moduleType.Substring(moduleType.LastIndexOf(".") + 1), Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : ""), - Version = new Version(1, 0, 0).ToString(), - Owner = "", - Url = "", - Contact = "", - License = "", - Dependencies = "", - PermissionNames = "", - ServerAssemblyName = "", - ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1], - ControlTypeRoutes = "", - AssemblyName = assembly.FullName.Split(",")[0], - Permissions = "" + Version = new Version(1, 0, 0).ToString() }; } - // permissions + // set internal properties + moduledefinition.ModuleDefinitionName = qualifiedModuleType; + moduledefinition.ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1]; + moduledefinition.AssemblyName = assembly.FullName.Split(",")[0]; if (moduledefinition.Categories == "Admin") { moduledefinition.Permissions = "[{\"PermissionName\":\"Utilize\",\"Permissions\":\"" + Constants.AdminRole + "\"}]"; diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index cbbc889d..70e7ec2c 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Oqtane.Extensions; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Models; using Oqtane.Modules; using Oqtane.Shared; diff --git a/Oqtane.Server/Repository/SiteTemplateRepository.cs b/Oqtane.Server/Repository/SiteTemplateRepository.cs index c88934fd..c471f5c0 100644 --- a/Oqtane.Server/Repository/SiteTemplateRepository.cs +++ b/Oqtane.Server/Repository/SiteTemplateRepository.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Microsoft.Extensions.DependencyInjection; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Models; namespace Oqtane.Repository diff --git a/Oqtane.Server/Security/PermissionHandler.cs b/Oqtane.Server/Security/PermissionHandler.cs index 07652811..8735d0d0 100644 --- a/Oqtane.Server/Security/PermissionHandler.cs +++ b/Oqtane.Server/Security/PermissionHandler.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Oqtane.Enums; -using Oqtane.Infrastructure.Interfaces; +using Oqtane.Infrastructure; using Oqtane.Shared; namespace Oqtane.Security diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index ed8e8678..714834e1 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -15,7 +15,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; using Oqtane.Infrastructure; -using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; using Oqtane.Security; using Oqtane.Services; diff --git a/Oqtane.Shared/Models/ModuleDefinition.cs b/Oqtane.Shared/Models/ModuleDefinition.cs index 35a27840..33880e1f 100644 --- a/Oqtane.Shared/Models/ModuleDefinition.cs +++ b/Oqtane.Shared/Models/ModuleDefinition.cs @@ -5,6 +5,22 @@ namespace Oqtane.Models { public class ModuleDefinition : IAuditable { + public ModuleDefinition() + { + Name = ""; + Description = ""; + Categories = ""; + Version = ""; + Owner = ""; + Url = ""; + Contact = ""; + License = ""; + Dependencies = ""; + PermissionNames = ""; + ServerAssemblyName = ""; + ControlTypeRoutes = ""; + } + public int ModuleDefinitionId { get; set; } public string ModuleDefinitionName { get; set; } @@ -13,6 +29,8 @@ namespace Oqtane.Models public string ModifiedBy { get; set; } public DateTime ModifiedOn { get; set; } + [NotMapped] + public int SiteId { get; set; } [NotMapped] public string Name { get; set; } [NotMapped] @@ -42,8 +60,6 @@ namespace Oqtane.Models [NotMapped] public string AssemblyName { get; set; } [NotMapped] - public int SiteId { get; set; } - [NotMapped] public string Permissions { get; set; } } } From 5a6f939a98d529886e159a53be36ea1e34319d46 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sun, 5 Apr 2020 15:31:54 -0400 Subject: [PATCH 118/265] modified ITheme interface (#344) * upgrade to .NET Core 3.2 Preview 3 and fixes for issues created by #314 * Components based on Bootstrap4 for Sections and TabStrip to increase productivity and promote uniformity in Module UIs * rolled back change creating an Infrastructure.Interfaces namespace, modified IModule interface to be strongly typed * modified ITheme interface --- Oqtane.Client/Themes/BlazorTheme/Theme.cs | 20 ----------- Oqtane.Client/Themes/BlazorTheme/ThemeInfo.cs | 14 ++++++++ Oqtane.Client/Themes/ITheme.cs | 5 ++- Oqtane.Client/Themes/OqtaneTheme/Theme.cs | 20 ----------- Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs | 13 +++++++ Oqtane.Server/Repository/ThemeRepository.cs | 35 +++++-------------- Oqtane.Shared/Models/Theme.cs | 11 ++++++ 7 files changed, 48 insertions(+), 70 deletions(-) delete mode 100644 Oqtane.Client/Themes/BlazorTheme/Theme.cs create mode 100644 Oqtane.Client/Themes/BlazorTheme/ThemeInfo.cs delete mode 100644 Oqtane.Client/Themes/OqtaneTheme/Theme.cs create mode 100644 Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs diff --git a/Oqtane.Client/Themes/BlazorTheme/Theme.cs b/Oqtane.Client/Themes/BlazorTheme/Theme.cs deleted file mode 100644 index 4f95db85..00000000 --- a/Oqtane.Client/Themes/BlazorTheme/Theme.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; - -namespace Oqtane.Themes.BlazorTheme -{ - public class Theme : ITheme - { - public Dictionary Properties - { - get - { - Dictionary properties = new Dictionary - { - { "Name", "Blazor Theme" }, - { "Version", "1.0.0" } - }; - return properties; - } - } - } -} diff --git a/Oqtane.Client/Themes/BlazorTheme/ThemeInfo.cs b/Oqtane.Client/Themes/BlazorTheme/ThemeInfo.cs new file mode 100644 index 00000000..7c52e766 --- /dev/null +++ b/Oqtane.Client/Themes/BlazorTheme/ThemeInfo.cs @@ -0,0 +1,14 @@ +using Oqtane.Models; + +namespace Oqtane.Themes.BlazorTheme +{ + public class ThemeInfo : ITheme + { + public Theme Theme => new Theme + { + Name = "Blazor Theme", + Version = "1.0.0" + }; + + } +} diff --git a/Oqtane.Client/Themes/ITheme.cs b/Oqtane.Client/Themes/ITheme.cs index 134ec3a2..16d77bf4 100644 --- a/Oqtane.Client/Themes/ITheme.cs +++ b/Oqtane.Client/Themes/ITheme.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; +using Oqtane.Models; namespace Oqtane.Themes { public interface ITheme { - Dictionary Properties { get; } + Theme Theme { get; } } } diff --git a/Oqtane.Client/Themes/OqtaneTheme/Theme.cs b/Oqtane.Client/Themes/OqtaneTheme/Theme.cs deleted file mode 100644 index 4a9ab697..00000000 --- a/Oqtane.Client/Themes/OqtaneTheme/Theme.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; - -namespace Oqtane.Themes.OqtaneTheme -{ - public class Theme : ITheme - { - public Dictionary Properties - { - get - { - Dictionary properties = new Dictionary - { - { "Name", "Oqtane Theme" }, - { "Version", "1.0.0" } - }; - return properties; - } - } - } -} diff --git a/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs b/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs new file mode 100644 index 00000000..39784cf3 --- /dev/null +++ b/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs @@ -0,0 +1,13 @@ +using Oqtane.Models; + +namespace Oqtane.Themes.OqtaneTheme +{ + public class ThemeInfo : ITheme + { + public Theme Theme => new Theme + { + Name = "Oqtane Theme", + Version = "1.0.0" + }; + } +} diff --git a/Oqtane.Server/Repository/ThemeRepository.cs b/Oqtane.Server/Repository/ThemeRepository.cs index 3bb3f7e9..1fa7c813 100644 --- a/Oqtane.Server/Repository/ThemeRepository.cs +++ b/Oqtane.Server/Repository/ThemeRepository.cs @@ -48,41 +48,22 @@ namespace Oqtane.Repository if (themetype != null) { var themeobject = Activator.CreateInstance(themetype); - Dictionary properties = (Dictionary)themetype.GetProperty("Properties").GetValue(themeobject); - theme = new Theme - { - ThemeName = @namespace, - Name = GetProperty(properties, "Name"), - Version = GetProperty(properties, "Version"), - Owner = GetProperty(properties, "Owner"), - Url = GetProperty(properties, "Url"), - Contact = GetProperty(properties, "Contact"), - License = GetProperty(properties, "License"), - Dependencies = GetProperty(properties, "Dependencies"), - ThemeControls = "", - PaneLayouts = "", - ContainerControls = "", - AssemblyName = assembly.FullName.Split(",")[0] - }; + theme = (Theme)themetype.GetProperty("Theme").GetValue(themeobject); } else { theme = new Theme { - ThemeName = @namespace, Name = themeControlType.Name, - Version = new Version(1, 0, 0).ToString(), - Owner = "", - Url = "", - Contact = "", - License = "", - Dependencies = "", - ThemeControls = "", - PaneLayouts = "", - ContainerControls = "", - AssemblyName = assembly.FullName.Split(",")[0] + Version = new Version(1, 0, 0).ToString() }; } + // set internal properties + theme.ThemeName = @namespace; + theme.ThemeControls = ""; + theme.PaneLayouts = ""; + theme.ContainerControls = ""; + theme.AssemblyName = assembly.FullName.Split(",")[0]; themes.Add(theme); index = themes.FindIndex(item => item.ThemeName == @namespace); } diff --git a/Oqtane.Shared/Models/Theme.cs b/Oqtane.Shared/Models/Theme.cs index e4b29cfc..a2dd83df 100644 --- a/Oqtane.Shared/Models/Theme.cs +++ b/Oqtane.Shared/Models/Theme.cs @@ -2,6 +2,17 @@ { public class Theme { + public Theme() + { + Name = ""; + Version = ""; + Owner = ""; + Url = ""; + Contact = ""; + License = ""; + Dependencies = ""; + } + public string ThemeName { get; set; } public string Name { get; set; } public string Version { get; set; } From b15ff4732c369b3d87e6c9d537fe0fbe20fa4955 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Mon, 6 Apr 2020 08:53:52 +0200 Subject: [PATCH 119/265] Fix of tenant migration and module installation --- Oqtane.Server/Infrastructure/DatabaseManager.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 2d23f219..a8164ac1 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -60,6 +60,13 @@ namespace Oqtane.Infrastructure var result = MasterMigration(defaultConnectionString, defaultAlias, null, true); IsInstalled = result.Success; + if (result.Success) + { + var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); + WriteVersionInfo(defaultConnectionString); + TenantMigration(defaultConnectionString, dataDirectory); + } + if (_isInstalled && !IsDefaultSiteInstalled(defaultConnectionString)) { BuildDefaultSite(password,email); From 7421df2970a53fa7f485dfa0135b13e792ded139 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 6 Apr 2020 13:25:19 -0400 Subject: [PATCH 120/265] fixed issues when running on WebAssembly, made IModule implementations consistent --- .../{Module.cs => ModuleInfo.cs} | 2 +- .../[Module]/{[Module].cs => [Module]Info.cs} | 2 +- .../Counter/{Module.cs => ModuleInfo.cs} | 2 +- .../Weather/{Module.cs => ModuleInfo.cs} | 2 +- Oqtane.Client/Program.cs | 1 + .../Controllers/ModuleDefinitionController.cs | 19 +++++++++++----- Oqtane.Server/Controllers/ThemeController.cs | 22 ++++++++++++++----- Oqtane.Server/Startup.cs | 3 --- 8 files changed, 35 insertions(+), 18 deletions(-) rename Oqtane.Client/Modules/Admin/ModuleCreator/{Module.cs => ModuleInfo.cs} (91%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/{[Module].cs => [Module]Info.cs} (89%) rename Oqtane.Client/Modules/Counter/{Module.cs => ModuleInfo.cs} (87%) rename Oqtane.Client/Modules/Weather/{Module.cs => ModuleInfo.cs} (88%) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Module.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/ModuleInfo.cs similarity index 91% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Module.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/ModuleInfo.cs index c57bf1a9..81b82a6d 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Module.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/ModuleInfo.cs @@ -2,7 +2,7 @@ namespace Oqtane.Modules.Admin.ModuleCreator { - public class Module : IModule + public class ModuleInfo : IModule { public ModuleDefinition ModuleDefinition => new ModuleDefinition { diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module].cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module]Info.cs similarity index 89% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module].cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module]Info.cs index 630b488e..5252730e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module].cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module]Info.cs @@ -3,7 +3,7 @@ using Oqtane.Modules; namespace Oqtane.Modules.[Module]s { - public class Module : IModule + public class ModuleInfo : IModule { public ModuleDefinition ModuleDefinition => new ModuleDefinition { diff --git a/Oqtane.Client/Modules/Counter/Module.cs b/Oqtane.Client/Modules/Counter/ModuleInfo.cs similarity index 87% rename from Oqtane.Client/Modules/Counter/Module.cs rename to Oqtane.Client/Modules/Counter/ModuleInfo.cs index dd2e0e35..053017f9 100644 --- a/Oqtane.Client/Modules/Counter/Module.cs +++ b/Oqtane.Client/Modules/Counter/ModuleInfo.cs @@ -2,7 +2,7 @@ namespace Oqtane.Modules.Counter { - public class Module : IModule + public class ModuleInfo : IModule { public ModuleDefinition ModuleDefinition => new ModuleDefinition { diff --git a/Oqtane.Client/Modules/Weather/Module.cs b/Oqtane.Client/Modules/Weather/ModuleInfo.cs similarity index 88% rename from Oqtane.Client/Modules/Weather/Module.cs rename to Oqtane.Client/Modules/Weather/ModuleInfo.cs index 99922df4..0588c563 100644 --- a/Oqtane.Client/Modules/Weather/Module.cs +++ b/Oqtane.Client/Modules/Weather/ModuleInfo.cs @@ -2,7 +2,7 @@ namespace Oqtane.Modules.Weather { - public class Module : IModule + public class ModuleInfo : IModule { public ModuleDefinition ModuleDefinition => new ModuleDefinition { diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index 4172834b..9e4e1261 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -20,6 +20,7 @@ namespace Oqtane.Client builder.RootComponents.Add("app"); builder.Services.AddBaseAddressHttpClient(); + builder.Services.AddOptions(); // register auth services builder.Services.AddAuthorizationCore(); diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 3f0f1e84..6ed8b830 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -122,13 +122,22 @@ namespace Oqtane.Controllers } } - // GET api//load/filename - [HttpGet("load/{filename}")] + // GET api//load/assembyname + [HttpGet("load/{assemblyname}")] public IActionResult Load(string assemblyname) { - string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - byte[] file = System.IO.File.ReadAllBytes(Path.Combine(binfolder, assemblyname)); - return File(file, "application/octet-stream", assemblyname); + if (Path.GetExtension(assemblyname).ToLower() == ".dll") + { + string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + byte[] file = System.IO.File.ReadAllBytes(Path.Combine(binfolder, assemblyname)); + return File(file, "application/octet-stream", assemblyname); + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Download Assembly {Assembly}", assemblyname); + HttpContext.Response.StatusCode = 401; + return null; + } } // POST api/?moduleid=x diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index 120e5d96..af7af8e1 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -75,13 +75,23 @@ namespace Oqtane.Controllers } } - // GET api//load/filename - [HttpGet("load/{filename}")] - public IActionResult Load(string filename) + // GET api//load/assembyname + [HttpGet("load/{assemblyname}")] + public IActionResult Load(string assemblyname) { - string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - byte[] file = System.IO.File.ReadAllBytes(Path.Combine(binfolder, filename)); - return File(file, "application/octet-stream", filename); + if (Path.GetExtension(assemblyname).ToLower() == ".dll") + { + string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + byte[] file = System.IO.File.ReadAllBytes(Path.Combine(binfolder, assemblyname)); + return File(file, "application/octet-stream", assemblyname); + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Download Assembly {Assembly}", assemblyname); + HttpContext.Response.StatusCode = 401; + return null; + } } + } } diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 714834e1..fc5825d2 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -218,11 +218,8 @@ namespace Oqtane installationManager.InstallPackages("Modules,Themes", false); app.UseHttpsRedirection(); - app.UseStaticFiles(); - app.UseBlazorFrameworkFiles(); - app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); From e10015c11a2960c1c3e51e1d6fb9fa1282cd78ba Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Mon, 6 Apr 2020 20:54:55 +0300 Subject: [PATCH 121/265] Address feedback --- Oqtane.Shared/Oqtane.Shared.csproj | 1 - Oqtane.Shared/Shared/Utilities.cs | 30 +++++-------------- .../Oqtane.Shared.Tests/UtilitiesTests.cs | 8 ++--- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index 01053668..6a0c0d87 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -18,7 +18,6 @@ - diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index c828e9e9..fe7817d0 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -2,8 +2,6 @@ using System.Globalization; using System.Text; using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; namespace Oqtane.Shared { @@ -19,29 +17,15 @@ namespace Oqtane.Shared public static string NavigateUrl(string alias, string path, string parameters) { - if (!alias.StartsWith("/")) + var uriBuilder = new UriBuilder { - alias = $"/{alias}"; - } + Path = !string.IsNullOrEmpty(alias) + ? $"{alias}/{path}" + : $"{path}", + Query = parameters + }; - if (!path.StartsWith("/")) - { - path = $"/{path}"; - } - - var pathPaseValue = alias == string.Empty - ? default - : new PathString(alias); - - var pathValue = path == string.Empty - ? default - : new PathString(path); - - var queryStringValue = parameters == string.Empty - ? default - : new QueryString($"?{parameters}"); - - return UriHelper.BuildRelative(pathPaseValue, pathValue, queryStringValue); + return uriBuilder.Uri.PathAndQuery; } public static string EditUrl(string alias, string path, int moduleid, string action, string parameters) diff --git a/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs b/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs index ccafa193..c5de08e4 100644 --- a/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs +++ b/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs @@ -8,13 +8,13 @@ namespace Oqtane.Test.Oqtane.Shared.Tests [Theory] [InlineData("contoso", "login", "returnUrl=/admin", "/contoso/login?returnUrl=/admin")] [InlineData("contoso", "admin", "", "/contoso/admin")] - [InlineData("contoso", "", "pageId=4", "/contoso/?pageId=4")] - [InlineData("contoso", "", "pageId=4&moduleId=10", "/contoso/?pageId=4&moduleId=10")] + [InlineData("contoso", "", "pageId=4", "/contoso?pageId=4")] + [InlineData("contoso", "", "pageId=4&moduleId=10", "/contoso?pageId=4&moduleId=10")] [InlineData("contoso", "", "", "/contoso/")] [InlineData("", "login", "returnUrl=/admin", "/login?returnUrl=/admin")] [InlineData("", "admin", "", "/admin")] - [InlineData("", "", "pageId=4", "/?pageId=4")] - [InlineData("", "", "pageId=4&moduleId=10", "/?pageId=4&moduleId=10")] + [InlineData("", "", "pageId=4", "?pageId=4")] + [InlineData("", "", "pageId=4&moduleId=10", "?pageId=4&moduleId=10")] [InlineData("", "", "", "/")] public void NavigateUrlTest(string alias, string path, string parameters, string expectedUrl) { From 9fff8be40a9bd2835b9202e9db60f98696444ca4 Mon Sep 17 00:00:00 2001 From: Grayson Walker Date: Mon, 6 Apr 2020 16:00:58 -0400 Subject: [PATCH 122/265] update helptext --- Oqtane.Client/Modules/Admin/Files/Add.razor | 10 +- Oqtane.Client/Modules/Admin/Files/Edit.razor | 16 +-- Oqtane.Client/Modules/Admin/Jobs/Add.razor | 140 +++++++++---------- Oqtane.Client/Modules/Admin/Jobs/Edit.razor | 140 +++++++++---------- Oqtane.Client/Modules/Admin/Pages/Add.razor | 66 ++++----- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 52 +++---- 6 files changed, 212 insertions(+), 212 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Files/Add.razor b/Oqtane.Client/Modules/Admin/Files/Add.razor index 182f08f7..32e50352 100644 --- a/Oqtane.Client/Modules/Admin/Files/Add.razor +++ b/Oqtane.Client/Modules/Admin/Files/Add.razor @@ -27,7 +27,7 @@
    - + @@ -39,18 +39,18 @@ wpaI9J8gQJn0mtbZaGb~y$0{9goZJB?(%ftcyTD=V$MU-6 z)u!8izr)Ut>2>tBupKM2?V6CxG%3B1!=5#oPLbX!4nq|pOlQ!NxLYW)QZwcpD3LaiPA}f zD4n7nrX$W#Iz={0msoBe*IyOIJ_@MM-tA7?6%L32jcN)~XfqJ`CIuzP$&nK21e9G99!&Jio`TjmxhC#m%P{EkcBah!9aBKvaSN z(f9|520uVF@&Te@4-k!cfM~!YM4}xa8tMQ^<9zi%uUy8_7A%i>#m^j{#b1w4RZu+D zLh)1)#Zz4rPt{R8)kyJFse)&EDW0mPc%xle#jh>!!~L7}xZf;asL@?CbR3YM_bB8J z1zEg43@ci3x!jERNdK~4;b)WB5yGQNqL`yOT?4=sAwYJB0QNxyumK`~m5%_HJOWtj z2w;&TfYl8Fk~IQY&j?^4Ti~$!`O=)CKb--6q&>kz+!1W#Ex{>Z2~G)1aEe%hQ^pdU zLYCl^vIM7?BUt4u!6|47R!Q0*5$}d)T6);28B%&ZBZ^Tppjb@26q8AxQnyFUAPVIqHb^( ze1kg;4DO_`xK+g9P8fs7`b-lzi9j78Qm7?_5;cTaqlOTR)DU8o8bT~nLx^>12(eHN zAy%p-gi1 zxuSP1^)lDna*oSbY(IHqd7kuqU>+|$Ck%PnImwxqG0CErG0Cf!G0C)-G0DA`G0Dc4 zG0D#tGGXk?nB?%wn3L6e-HUw!3l?kil+;0v7Td?t67LvVW*kGyTw`dNWehFzi=ky^ zF|^DnhL+jH(h`ptT4oSK^X{PS5(|lkDO;P>p~>s*9-oTIA!nBUNLR*K*qFn>))WS| zW-zcdfq|{~2DYLb*otjnE3$=+xCXYO8h8@(-4eCX>TZVtF39<5xn1p^NlY44{>w+z zO_!8trPkMZy3wO7Awdy=3hh`cs`s~CJz*y9dW#JfH@K9ZQY$Xn;BZCX5)y-y9U+UY zO3~%g8`nfwdOjX2NUn81nZ=)Ta*D?$n|N&Uh{q;_cx-Zq$0ln8SoMX+CR2E9azw#o zZNj^W8j^V0u2v0(X@T4_hT88pp<8F&Qil*uAN6n%5T-qKpwb&7N zGrZXiBM7{5OoeXYvgAxMR*Sq}{?u!%&w%co5RlVnjRJc3Kl_+*@66Noj6Psl385bG2}tW*rKRx!kC#SrV2 zK%`g0BvQ!ajU(3 zl})<`df=zwaX1W^D8&hQOkkeHieAb*UV2XGec3rl{>zx;hRc{_j?0+jmCKl9oy(Zy zq|2COtP7d&*=0j~|fyPclEoXEm~a{`LBjHfuSIEpukqj+~XinoQM zct1FbH-e*h2RMqC-cy|Bj^f336tA)t6aC9-y=5XwpmsvDipMmkdPwuihcvH#Nb?Q| zY2F1P%{w8ac{hYK?}(V@ToKZ|GeTPGj#`Om-BQM+L!Tt%+%fq9iFeWiG8bhBq<+c{ zNS&1(ka{dTAaz@IKqw2^&iOPtH~6VdV}9<^kUwM6kU!(mkUwM5kU!(lkUwM4kU!(kkUwM3n4h~d#oar^LMN?1Lrywjz{aQRLfwek2Z^qQb1c8TOvo5*%F ziQ+kvD8@30;vSPI7BPw91CuCXn?zC6CbDcMQM56MA^<1Sm`!Q1n8$SL5vm+Vx(vgqeO!kAsWQ!&?3DI z4PsPi5E0?~Kx1sN7f(ml=?#+to-0- z(I0aA`h)Kn-?rV99aHZ43s=~g`-C@@$MNxmTry#eeP`*cS#k`pV}2a+dUhP?lwqV%hLJuQM%rW;>5^fj zNrsUgDF!VvjC9B_)<9My;>$$4H(01B8WDWf4_bWv5e@)RZ1&WFSSv5Bkk)cbqsPN68Z@ z%C1OJ_C<=aGg6ejk)rI56lH&;C_5xa$s;MsE=f`H3BEbLUt<#))`TN(Q;wE{2=^R{ zYM(-p>{BRueF{aaPoXIFDHNGLg`#0n5P?30p1MzwC#^LjFX;qD&83Dkzcm;ght21B z9FU$TA?ed3D1DlQrB9Q<^l1{BK23ttr%8BB8V9IPlMwZ39Hi6!8y34BpYFAi|3C=~ z{x2ujfxVl@$MplMYz?aPRJaw8A{@~<^thNo5#=1Ec;_fZK1WG|93?e!lyu2aQYc4B zs~Hrk_|^f--LQ}?1w@@ay@chDV&iA0}WKmL(G|51gBm*Z!G++I>#=CGQY7f&}1ai^{YE(&JWE#F|3{$Sj;YpkD z=t-Rrp7aUfNudy)Gz#HKr4XKUis7nM2v1stFsY^Gnhv?}`Zj93Wa+F^atvpl6oWz; zM)G7BX_H|jOoovv8Ag(180nE=Bu0usi3}qdGAz&lSp|#dd1Ztwzg<3jqm9v=7)*gsy6Z8ueYx^YphKwD?IvA zGGPVY%$e4dDbu+zV|p8AOz*#p>5Z2$z2h>bw_3*Z9?O{CTq)DJDr0&(Wz4`w<9O;m zJ7x@I;K(_i;w7cUVeb3rh+7U@3tSEG2M&r3BJDir3sy0WC7kl+dhRG0iC#(!7Qt&8r#Gysjb5D;(0i)*;QS9@4!2G0iz4qpniiGEU5%at=VxIR# z%=7MudEOr}&pRaMd5^?A?~;V)d=m4#Q(|7=6+lU^g-r%&uE6-GR*Yj9)q5wF8=wveOTyPF52Rb^u2FZ{3ulIf+o>+HLbCyO_SDNZw}2F z79UUJlU9u%cCxlhtIY%u-{c@NZwiuMGLXHJf$WnEWRGMZ`y&I{8yU#H$Uyc)3X&f( zkiC$B?1Kr^rVE`*zroO))`DH&4cG_12TkC6&;-5*P2hXb1ilAN;Cs*nz6VX<4cG_1 z2TkC6&;}k6M^;siwLFQ?p?&BLYGU7`Huybiqu--8{5@*p-=kK59<>VesFlE=MuQ%; zBJ`+Hf%ekH(m5%QP^i&|UCPTLv;Y;`doJQ+RxE{%=~2)sIf{fzAW|uTNTLKHeG-Vo zNgz@tfk>7Nf+h(>f+P^B0jTWExE~Hj^sOFt2P`l>(tiB&Xwe`)9t@UZjvaE>8L&M;Ka8Ahus$*8T)-1aqBAUX1a91$ORN#=-;yda;@ z_F%*6dV5LkXt|f<3Bx~|6MFu6|9AWgT;K68@OsC;z~LSL0$+Fh3*6lCFYs{3zreXY z|GZy2{sk`W_~gyo2Yk7gCY$lqLuqh~Q5t(K;3EAP^WmL{PacWV$qP|BX&$AMwoy81 z7^Ra|Q95Z7rX%)II>k6j=UCz!$Gmk;*&={{++w(VwZOIx878rv#S!B~Z0_P2S#Zi>y8O>mZ*^T zL4~~d74nK#$je+IuWg0ApcV2ePLPwXLSC;5d6C}o7fWr>H8qI&gxHW*#6`RyKHvrM z@h*rDcR_r#3*v)a5FhJ;_)u5GMY_zKfuUQ9s$vW68*1=w|9(Hpjo47`yLy-Q+Z;Fp*fIY&)mGr7KSAfJ1+M$+H# zj<-a`0v8l4624%O)&+~?Em)*z!6Fe07U@>7NUDkj>J%&zq+niyPs3Llk4Do`IYtYI z2YEK)SZxB1)5vqYR-WTE^Bk|8=Xec0$7|_1UQ^HU+6Ek_vFCWLJ;!SUdWi<5gF6_B4c`YWK8dojOm?{F}+_hrgu%sbl%CB-a#2N^HJ&S zoV8L!Ox=`_V?)K{#8V+TvsFmWoE4HYbA{y0Um-cOSV+!X7Lqfg#pJ|mAvv>KNX{Im zy}q-C3wWvDVs31=kej$I;$~)xxS7`?Zf3QJn>j7wW=4y+na?6_X0wo+xGdskCX2Y4 z$36|uJ51!A^;p15Jr;9gkA>XCV-Yv=Sj5dd7I8C=Mcm9|5jXQ##LYYwaWjvF+{9xM zH}hD;%{>;5+Dipo20&)qBu9ggS*Q?F-zXtBixP5&C?U6n5^^snAvb^$a@CcPOI--5 zzDmeNRf1RY^LG8er-6n@Ovh;g0tGwJs@H-}xd!yAHK13l0liuc=#^?fuTlegg&NSS z(}GT!2K1^lV5W#r&uE8HF=#6wcI9IMYYrOd*9cjTFvQQaID8z==`{ zXId#7sI_0=qcl@BYZWtaq?)HV>6^f1tOhd!KW52gkECjvy-9E7GRNNQ#v>zIM8U@T{O#B>MfzwrhzWIrP@Kyr=dr8KcWjM7D>r1PiVWar&w$7Dl>FVT^hgSO3R2`# zkb<9r6#o zXOb!9AcvG2%{Z-S8K>1QQL>o^|w1MPA8$nLAq2ojwGfuR@;zS!INi^Z%L>m`Qv;om~I;WzGB3Ke# zq#B@))&!V{Rlr8A0yc6Lu+gi4jbH_A6f0mOSpgf(2`~|@fQ@PeTx8|NhAyk?%ML!S z!bQ3XYe~0J)pQqBO?NTXbQe-hcM;We7f?-i@zit|&XR7Usp&45nr>rRRy23PEQv0f z4bX>k0!%zBU;|nK8_^2bkXFFPv;sD$6|hmQfDP*en7CHJ2DSoiWa*Rl$DtTlH_9n3 zg9z**oe-vo$Al*GA)!?uB(zF|gjSJ|&?*xWT7^PFt5is66^jXtav`BrFeC&@O75X@ zzGf6XbV4ExJJbo-qF%iQ4Ww((K(_`BL~GDMu?7v~YS2Kd1`UL2QLj>i1`;)>=<{v6 z`=R3urTb-*RFNQ=r0~hCflHR)yJU&FOO|lEWQns&mH@kCiL6VOkoshfrAw9|y5uQ( zTiN@hjb5A7M9(Cb==I4pdR_7qy)Jo*UY9&YuS=ey*CkKU>yoGFb;(oo`s5nDE_sSx zm(0=Q@vt(+MVm5XZ6sc}{LJZS@Xg~A*Rvc1`5cELeul&0Kf_@y&Tv?mGaOdw42LB< z!(si-a9GT998&fShh;v)@tU8o73TK1dHHnWmu)|sejSc)f1+;a&XlkM{d1;uLCSPq z$e7*{8PoeBV|sUFOz)A5>79}>ycTLK4-pQEWK^fEeh<)>Uf46&s)?#m-4)Tt% zu(64Otv7nu8KQ@s3wqc|-@{Jt9(E%4uv4~&oty@?8uqXgu7{mkrTyOY<9p2lf>$mk z8T~?%RWTwtEhCasG$J`&Ba%}$A~}sCl2bY&IlV)YRXrj(?ITj^0BQQOU0kVOPSkU9 zA2?oO1YLiw&*`(F@HEA_yZCZ&`lUBpiq;X7W zH;WIa5o-a^Po3sP!(@q21|d)hMDdCt4qFUy^kRsE7(*P(7~+t|5JxtKIKT-+agHGl zcMR32mqB3p1`nc-&-M&k>@)JP#)c1k;#tb1#yOfQbB0z_nx|F8=4n;Ed0JI+o>o9vd?My`LbpBXwPD2W?6SAWK9%qh( z>gkcBbqbQgDM-4eAgP&xq+tq@aw$l9kr(h+`DOpK#idNE`vXwNaa3#$tJ&|Sb9xJ7PHbV!DJ_gSnT0W@u`uQYR>q9F!kCj*7@e-C{btQmv--W8{~nrx zF#!7<9TOT+Lqe-*MCc@q2%Vx4p%XMBbb3aFPR@wXsTmPEF+)PDWkl$tj0mlgG!@%^ zHM3?;3v>zw;G~`h8|56>s^-8}F$cD4Ij~jAfvrjoY!z}~tBwa7WgOV5;=ooB-SkBp zKD2e-``v14&BSo0PD+_H$|#Le38mF5p|q+clvcZh(khrxS{)Ngt7bxJHO(lEvI(Wt zH=(pD|GuNIBjOvS<%QmlJCvwXAzJaz+wrvD?+)~0z;JwbzzbAAc1^Q4BHrBAZ2W%w za$0Y&Uheo*)3DDXHf7!)n-8Cs_MC@0XNBzW8|923B;TLQRs%BNwunua09cT1GmtE%PgfKSvO*AIUThjG1|Is+!XQ_*9S zgbr);vsf#h#aiVo*2-qFRx^vWf?2Fp%VMom4r_F>SSymnT7?W&TEdw|1)tNT;iTG3 zoLr;InbE3pW;Cmu8SN@(M#IXP(Xw)8G_9N&Z6{8yaplZtT{$zFa}zleT-wiVzs5MG z5K^g?kn1dj8IgrBqp%QW!)aTC`RwG-^YPCyQoq#E;Q!!}PnzA}&Q&uN$&T2JIS)I@+ zt5y5+_JA+)Zm?qoHNv}Q5n5UpJ6u1mhgBV0ezEoH9H}$R8RF!bd17OmSz_y)Sz>FR zSz_y-Sz>FUSz_y=Sz>FXSz_y@d17OySz_y`Sz>Rh+&QK8sFlOqSqu4MCp8CL=BVj_ z3!PODxYS|wfJ>cL54hBE^?*yAR}Z+ZaH&J<0hc2y?jgSM9kZ=>5S?-Ru){L=hvTc|I8bmx4nArE!s>6_VhtX2+Y;~%6wHLD z=P6Hl_0>Bu!E-S6;v3HhZB9VIyj?l=QY0l zNjnhCT)Gh7uea;x)AJnlf(NOFD(B(Nxj*dcvqeu|4|;{ofwx$~`f#FkYvmipJ_7ZC z8Y%QRW}mVi@pilVXD+}w;-5J{Z~B^z6zEcKM2R1EvV+@Y0jdYl_ozw^pAD%VAs4ye ze$3tc^G95EH~URoyxH)y&w0VUwDj205C4Qq!QKH2|@i+Nv8ecYWtEGv7pjHjZTx3}}zUKxK|9q*Q37dP9XW`U2xfo@>&=K)nG zJ<5QpR;#v38W-|pO@iFZ$0fZ!C4bk$=zJee&tJIK)>Bd1$KAL-(iA~u*7I?dkK%Yr zs2s9_US3=ww<1?AH}+=tATsr#9^jk7Y@^FdA+B(-6o~p z?7Q`0gw>a;1)3T3WiPA@-fb~|^?kjfCmenr-=YGg=EtJm{xz)&JTCNn{0KPu`x>o} z?nlq-f7z2kwY%k)g|0HAKP3iSA?uaiZU(MUKj5xzkHelWb~lX2MM=2&xgHlKI=5)( z;I6ecmbhxE*F;?ZeEo!3JXN3=gu=+Bz@!#ZB)4}-i|g_A({lS2^G493gQWR_)Zces zfwN9yiQK2*5gXnk$8ywe*Izcc;qs7g(2#-@%fkzOJacik`!;N)WLn7nv7EC02L{<_ z(h^(e(=yHBZ+lEd-tCB4fyt7$aN+Xnuz0gRh<7B&RSZdNt|bZh`rGcuhtpXtcKvRev=%$vnIUw_Lo(-c7>+&n&H0J>FG1=vqa{s)#fQZ!Y&wH~-u~|i(LBDO_q0dhivmbX z{nBO79H502zRsi880$@IeiwOaDxSDBifdX~Uyjml4=cY^pbC$iib&C3UG+vebU%*^ zxe@Xa$4-9MGjNJzGHerYO}^Tb-;uqSm3EPB`_PIurk{xcb1^MZiB-fcE8Kh>p_*_SJ7-z;ul z&_jOyWdg@vuBl}F<$8R#{z-l3e_-p|l!RkLbUGhSgx>DM(_y#8sqkgA;9L!xxSw`A zl+3W`YpTYlCz#6a1y=51bHm_N6XzEyJ-7SiS{c8+glTxbT+N%=E#5%#=jYEVZnOM~L$&nhr$@u5<(gMDLnelA>YFz^>RE` z>tR!IE`A@r4;wV?NF}r{FdnqHIjqqsUT&nOP|eW(Km`a?D*^?%*-=U^6eaj>>o^&P z{c?j+_<%Mat?%bKI*Kioehf{m>!c#um&jF7HSa(hR!6%S-&S3ZohwdX1?Bsy!2_I~-3M*Q^GoAvkgh_1=->ZR}s15!L}PU9L&XypV=f}EoqL50Uy z4wE19-_kIU%sL|E>y?zcQ=>?jSJVfwiLwE^5^5`|Ra~C1NipYXkTT+l{?+Lj`Da8v z-2D*0tz_I@8YNnbT(79RiehtERc%NH>C?fD+_tS{u{N&AWRGa|tmOCgsx}%`4OA=I z9HV}D;f)kqG^?rdExlOow4wva4egQoo^08$y}>op`a#-E|6yBfXw3cZFaO2o`OmaD zQcp2e3TIAqQR~gO@Gz^eg*|Cq_U4ys>KefA+}309-lDZUHJ8qi@uU}XbuhHH*78#$ zd-9UuOHNHp^tZJ|d%?-+fKs+xbOGty=@*w_C>B^sKuD8Fi`&ir50u$V?cgL>Fg0~SA}$x3C{{(v?oEIHPphTG%I1{4}Uppggd z*}ukd8|qci(Bp#hJGJn>4hNO442G6*GSO(EO`*~nyghvxhoj83a%CG^70tC#W}BLT1Bs$Hs8?Sf zMd_*1zjE6-*`4WXyWP=%{7Mg;D$WIZn=}z#kP%Nhq9rr@ieAN&RQ8&J_SWP2 zYguXtGwqJs;rM#9#+2lyIpas%!SXBK#QL$t2o@@HO=v0d^c3+n0xg3rqPNQ&cNgVO z%iN>&iZ+RPX z?)9Gb(_P4*jGk;EzuL=ck~yuWS{=&s7iG%%B*sllHG=w<4)q*R)Nab7eb~KTqAgT@ zN}F2V&?q;mC-{WfGQ6Sjy>d~UP#2q;YG?*q;O%$3!VdE-WElb8`Vg1udpal~N>W%RZlea8)xpVY`AJ8gaq2F0wH!9bh&fI|Z zq9_Q<$rs4)>o{)|)zFCl`1CTO6|iBS{=OWkHO2jU4Bcr0@mR~#?- zHT79hs$-C9_)0S{BX#U?KdaLNX4R06_R#5pip0m$hN_G`#%z$0xP0Ns`|-~;Zi+fA zTxLjVJKcxI9jBy`SnjITG6VBDG*HFEF_?LS7HZ4IrxR5mcpiK}f#2GpHX^Q$S9Vsv zuOBcxfg*gf=u$_N@kbpFr*RS%2IVCRaH;T!jaqEKThnuj4irR)ddo|oO`;Bs(LFP{>!o>wq&Wdy zNpyiOIvhheyRL=uWVnnrvi7 z54)Rvt+zGqWGamBqHDQ3pPT#j1HT#}aIPAZ>0Fd#dj;X$QVRw;A zxOxp=3EWg>Q3j{MWSo<011a}P>kO%noEh~7F-9Pn1dWukv(C$OJLM@)M?{&+?7uy4 z(A1^-tM4eIU4!f(Z?YCA@RGsuy#~aR%6OU(oEl8fiVEa%W3Oq(2{`fu{J&Z5M^s?Q zykaoA-t-sW2)b$I;frQjI_4Qs_#QCa$TpzcpsYuR(mN6x@$q;9ZMz{S55sE2>^0?N z%=>OI5yoxS@wek~j}8SIf1DfmFWiOpZcjW*s`+Fb@!~Gki#j?dPUlNekg$y9z`drc z&DR~4gFHRgl>(>)&=!4#%4M^;IlRTIyDx}=q|r@L-F0((s$CE+HSVuYM=bo|;Wu<| zP<#w4Nvc((pp|+_U$+W2&;lRa+7{18~hr{l0k6K54Mlb$wF~jx0 zoP&!Gau-#UkXpXH{S9kC9R`dn$=ALP`hgM}F@4v&#)fkh;IICgrumlz|gL3m& z7J+CHkDKF5)W!i#V$#HTcLNp`ht6}TztA(L?5#x`PJ>S3lG_vA^-6XMZbb_fjU#UC z%dWqQG!PWmzH@hS+kzDELtTp21Xs_D_N&OI_l-58li6YrHllSe^#s-WIx3AD({oM~ zsX!V*C+J8mjoF)+nr<`zGG%3XIz3c%{IDaxQQrD}{iVN+jMz83^v0B2I*<1>md?v( zOPsDSK`KqZ{s^)zE9A6wi~dl3JU@!G1MJYE>W&S?f*c#4b=VG zr1aF&>f}%<(Aq*y7*rB;#TX@2CH3C5{J!3jX)(Wvu~|hZFQT!{BS10-_-deHyJS7; zilgJo1p?hM>dnJae^UWbd9nsx%0W~aTrlc2)J}9`R~yVStsnrxVGf;&w7ULxikY5* z1vx|~DLdk#VO)AwWKz9@f^y|EG|g#))A{w`CZsf?rF5<7^v#1UpGsRbqDR{eq?kxf z)}wd<62#!PtbEFndj3CXfn#YSDfMq3Ki&KnRU+z5UUFSLd%e}02~$a@-Ra2dL{TMk z*;ySxG;cL>;rQ_qCzb9f8JHu{?D=gjojt5Gt(7e(4iGUB7A5wR?^@z8|HzL+wbqU^2kM zkTmr$QYo!Q;hRJ$${ISZQSuk1QgKA8>as7C^anIRQRYjo(cgUPLPeHn3{XmW{hVFar zZ@j8r#E>?xtf73VcG8n!Oy!|AHsyA^%2HaGx?;Tnkw-;@^khmx6_L@-OL#9Pa0QVm0UVrP>k!{k&kmlIJBOB){) zBjrm<%%)D=n_tVAVRH}xs6*G|Q*&~99w`9Zn`}~%W5s83kbba)#^Pm2SnsGsBk1-4 z{WhEyr_;R04RpG_+iz=LH!;-&e6o_vwFimOrSMV~y1~@;Cu5G{vV5 zY`5B#3+Vm)xx~((bU#u+1^SaN=;PxKNlTJG}OtlW* zU$^J))Y+q3XGq%*^jjMh#fN-&e4Cou-=ZcKgOm~(i${2ljYbnNe@-xDlDVh;2%QwHWU&?)< z$S6%MpJZ+h>*~?@#!8V@e+e2g;T6$3gmRBjXMT?X$D{kZpj(5KR8KS=I4wiCDVI=W z3}tt8oHtmTx2x~eHRl(A-!X2_snvdS^#G*-C5+>s?SjRn(=C@?I2uH6znwCqu5&*qH67}Mf)EHw9 zt#ocGQ<`;C=GWZLGYzk(Cswbj@woc3`#yZ8_0$5lrjkmy$sszD(CRgo6+H{evohpz zP1_D&y)Li)SZ-*c4hG&ZMa$~~ns+<+H|D`k&s%-#5tE}0j$a+hE7i*r$Pb=f(2f`; zvP31F!jl1`(v`%oAGqMdH?q_h8lHZUQ(zRn3?%B90gW(8D`K~ymsRL35KNWuix$+g zM!$!ar}NA|%@BY8*S~`EC&qSmKWJL%{cgL%KWLOubMbon$9lx8N4Lk*YE9|ymk&3$ zf5RVm)YH$!)fd#tc)fu_RGK4o`T$Lf@p%7gxc`;bKM#kyr{xyY2=pq`-LA6yxLfI{ z^Rnpy1F5UsGd+ZD-@M&^{{ydRnbwe`j?(y~wFlC?mhfQElZ@1*A$+(EftNcC5Q!IUPp-riE-yvU#}Kdc3GZ+g9!I_!X_1!vO@CdFH;D@Gf_srQQY{2O*HEZcCz~K_~o?BanAiq}5SfrGjIu6ZkNwcC= z;OhAMfYm%V+kvr#Fz7NgEkPR5<=}ym`6X|Z39#uEh+xk1(?APhz7JBgaCI=%EX4xl z$|Dv7v0_~yO04dwAfv3>xcmx3Q&hvCke7jO@g3hYy?zbnwjc|G(ngi&3IWGc(j-xH z6+V#?Fsts4rFNjg4r@!pD_-HWpv*1R7P8^1LGo`T){7spyo;P_XlyD+(c zCa4x$nw6T%YKmU-{fRSugL65=GyIGD2MqS0l3&$Jd_1goC?~KQfIUPZV++^O5dOsr z>OhivSjtP^O;0 zq~1@dgV4Ozdgv|IDjo(Iz^6AF#}P|tNwyWeqk}4y8;`W!1f90s_Jwu^n38By6fy~~ zu6m@l*_48|(Hd&QPPM0sa=#{`=A;8q!koUq!VL#)`8|yW^C(t*4+^)eUZr=USCx-7 zXLwT*E2HSmLs@~B;nT&VqwB4sIHV0~WDR6l&DbUCz1OqU$CrEU$Dbmg$jc z0duRg(2kIE9+$Xg)07q6wspMyhMLk;u6el^ZHfYC4f6C6rfKWcK_8-^Z>mZ8eLt z;5@vtw%&HlV;PJyEHr5K1=o5aIygzD!h|YlDoq*=w~>Fj);lBNe<_dMLuK27U#=f^ zTUy_mz|9KSgaOmh%C(tu$RH2)@t`k<*PE+%pralMI(2b5lnE=BA~~R{uIkY`PVTUk z1}?6~3+dr7j6rTTv`~-VR7N)jbIE-Yx|`VFNZzEuW^+`@Q9q*elo!8YG6usEr3?Lj zxkVw!ua2V!y?M7RuUPVeH&h%asxM?EF}lVdX%#Woh8PZ&o;T`D`J+BZkvS+KUZYOf zJ!|2h-qT<7(C9?NN?0^k{=%$&efSa2utQcu_NP`H_UZd_k_FL$?a7S#CQX^ULg{&s zJfjj(QMQ)leRb&=o*0W(dC6Z*qwH3OvFTk7&SDr~9oKmMWc{TbgYjt1$R3grmy%BJ zQPRSm=yU~hCo6!Q$`&*2sVr7cWdS)AN;R$-R@dA0@s`J!+Oq|4Jz;>EbKXH_-i?t| z3QyZH{Fytro}Aua($Oh%(gtZy)+EbGSxFo9$~_3c|#4{6l$npBcJmLP3c z58r5DjQ1aVKZq#nMk5&ZQwpoDz+t zzy70nuFIqc*O$GeN%gohlMx@$Z2F@JSckd{FVu-Xj;2gDl}@`auf4_aPZy= zR9e%{TNE97zXj`bvHJ&R?jC44F}-eCjB&GBl|M@n)u2?t(9QgI7#`68kROH5mm0CY zSU*x!TM;Wg6GgZ5F1U6=Pjt^uNrOw&?3qT-5fuEGM#!mZ{5k#9#s@3z2HdIzcw}`Y zEuT`2ax020Th}3F?E-AUGA{m`R=3sR$ipB1M%$AexJCOboHQIT1wilZU<8le(c#y1 zq{GnsXx_lkRC3vY+bnb&^fn(Kk1-LQzABA7>QJ|T*CELTq++m~0J(&I*Qf6r9@yy! zI}fe0XSFRvYTl{k>j^d2H`mjbCQFJEFgjlATD^(dN{}2&&E8*p8ykIgbgzNcd|pRF zXb_d$f|=$ey~sAbe{jUx4ZClr{o)RL9{oV!gHG;-SBmLPR3B2kL%khsNb3#y*q8rr2T4;EKn@zM(IP4Pb`w3!E#jhy#o7c*qOEmj-xAUcn# z=xr=suMhN2>-0%tat?AJPFU`lOGjFsR0*5eu*FO|j17{M$9ex-DrFdE?$le}Nk z9&>fQGrG-E6d=dJVB;vQ`bC|oZ^^}r?29rkTC66C`YT!`j8+c%0H~B#Sd!9?Skr4x zRJlmcQirj1DHl70PhHk_35)j@;dhAiD0w=$+Z%erlKN*n1y;{X=PK4NV=|OB4Z|Ms zwxkw@7&Fivmr(bWZZ{2c(I5(1{13Dl918fy-TlpXT}518BIBDbtzEpI<2vkASnIMB zrRhzg7}EsRP(D;qyymS~N(f}W0F_~4E_Gx>c|WL-sT zAvJ;89#F!RE;%qiu67UGBZHImNqEP&y!gwP`mobdqP{zsBu`6%n4lL`(@kv}ikf0I zv0Zf`2a8qo;2M)MX2hKKATIjCNVF_1vd;2q;Oa7GzF>o3hG?Hno^7>*ado&!KA=sO(&> zx23qE&Ezl|Kw|~{L8f;rlW;0HSO!oV<)C^tD4cO;yJ?k z*&Zh)05`k#7}%VSMv*`Hw;n|m0naSglzL(8z?p?};$Rk*->z#8=LSI0KwIe3ZItMj z7J5d|#-TKI=$&po5L2%8J@9%_85O)hKdB2ZAfiz&MR%DG()+BnCPkQf3wVc6I+8Z0 z)|Q_Pxxwo)g;oNtSx8HHy`}O*rPT-tibv}FUz=73KBw*i6um`$8!?PuZ-IJ6!-4Co zg&S){UVY>$NOzk$QsX1MBUf^4V_>)^rbrb=O2;NxQH{{WyMLS!z&c*ZcCml}MAsiia znjd;KT;Y7(7fH+19=*0yrbn>LBx+Oa2fd{}!5uD!#R(Yx&=%8+0sg#xS5tJfBH$`Q z{ZE~;EX8{NvosJ(02GyyrX-ftP*f=O&8!xwdsdW0y&|uU#v-!eVTmElYwVVHy+!A; zEIYcd70*;<{JGw2UZHF8tvnezLf9}~OpeJE>HuwC-mUkTj@S2tX@pfbA1lqhDi|$L zqK-E`ELyKTUus&;&*_wdu`dt3s@64{gn`Eg(QRN z*?FkkIb!2gdf3Xw+fgutbO+pZhdIFUj;$2v~Z^lu8ZIBTX96 zOI=ek$o$fm05m%G|FQNa%x&e!nkawJT!pE{OGHga$XgZrx-7S)a@Cy|FMMf-ms992nC5zPfA8&XB7n;$;k%dDx@@M zEopRud$yfV7YuLm1yN_fs{2pn$1UQt%TJi4sMwhPi+y8D3BMZFQ+SIHRc;TnLXpgG_+_$$A@O$KO7F1zB_^xWdp- z2FjTF)--ZHO!F25Z89i2G=ob5w=3#oz~3tYG%M8;V&X(|XUu%kI>1U%4hcaWYfnB? zSZDYvMw1f10X8ALIWgIKv*AzyAJSGJP1wP`Lo4mVGB+~S7Vv6WKrajf!3Ac><426= z@?(JuPCy*cW^crHj0KGm#bh%-%W9?vV?l*ch3`fktwr@@6TUSgpRsk80ElP^u3>1&7)(ccl7$s3j3 zm8ru9GnW!LIi_l7Rjqmnu?zmZSwqo$yjpX!mTSvWrx1>a=6a`}H`C3Tqf`p%K9BBh zMOS)X0U)>E3UUC@y|&OaVd1_dXH4>NPkygS(Y5l>#hD6zz`Ck-sz%@%D`TwCJ%xBv zM|22+R_;L}Vb^h*J#Qn`IU`fm=(x8NNuAl7zg=btAIHj=t`EiP2u;CBKb9XU~mdjh7Tgd?*3%7v8} zdv-7;5X-&_G?Jd^6TG3vwr{dnR7b22W+x7q z{9u@_kRaxlOp((`ZtKe39-yE`5`+H3hZ>Bh^Ov`(uA*-}Hk_a6kqcx`g5$ZyZv=Qu@c;6iZcx1nY$vBg)JcM#8HMenPxkz^Zm?9~eML0@c ze^nXL2FjCG6Lk6^LY0YzWKPB)3)A;gxIIk6orqq{ujk^CPVO<pJMR{&}vj9G&fGSIL zM)2BjOG^w~U8MN9U{VBXpjYt6*UnE7vkla(Ph&2KCbKs=BsAkzF~hm6!;?QEU+%$5D*EymynBO9GD4nCZU zm=9PuvnKkIEeFrCdY(=ga+w^k#I|?$rhsiAX{@6NNL$}p+haUO%&3a;st1+i;1_X= z5#67z5VG?t?ry6B+9GQ!!zW(ax(FEG&|h0^OYY8nN5K;fz9tB=cSkvK^t!i{p+yIG z@2#3EXaKsm#t^g*n6IIN0NNbA?wGaBN7(rLOAiNRQ^6e{+ zdAJx&)u_|9BWi|J5VcK$+5;|sc~s{aQe5exbOxzgE*(2(j48{}^%{CAJZN?+=v~x- zv3;qL^HdL3UJ;W#k#1#U76pFW$#jZ#WIOY_pN04Drqc8f za(prSy`5=x3KZy4MmlXE?U?PBJtao8>2H`Av|0a6w>!hg1!B zIuI6F<~kmR8M%aJ>Gd%wBp05mJPSZNTV=jhGED+kgt!|Qb=3*AEnUPh73aeOW>NE3 zUNgdQyI2>cm^*Bp7i?Q(j4I458YRaA3$Np>70UvCB?D&3EegfSGA!s#kaq?bOw09g zwj-<+slIKFs4p%QjL6yVLARDF6kJX_?l;EgLSiBqfl7I@KvfoWde5Onl#Fn)Gb&?- zN5eG%ZHTRPUa@GhX#pQsV1UL%U|nlz;3y-#yKY7u=!}`rfz?@lUUq6(78vn7ZnOzV zB;r*>jI6=o+mOsH)CyM~?i4YjJVl$Jt<{1`8-T#8%OLC%?GogWSjyB`1)GItsvsdw z*ab}|-zjxn&QLvmZX*D8{@Il?dJ0H+uq=-c*}w9k9K!ec`R?)-2p&YkU2uANAY(V! z3hKi{Z$^1+V~rrOi2Mk;&X`q@5@FGym#30q4ogG=e$qE#?g{{7OQqbI+|c(iXO6zp zVXUnqsKLlh-LA0n?~;sp(d}yL#mq$rMnEyfVZ4SA9@h-m{>$=9+*s68(7eXH5lf`) z(UWY2;&8P=Q@E*7^X<*`pLnv`w2X?$vHoM}vbrO6n5WBpUx61mEMRmB5ztPO`>Khx zmc4Y#^YroU<}I1ZD`=Oc-$C81L;%8gG>1oQvnb#>ybsV4i&DqvCR^vmg|jyEnl7W4 zuodKdgah$SAA3WDEeo&fnc4(_15ZGPC4zOz8j7KIbB&*Lt!-5pdfGccB}}oQvHLV7 zR&QWo22v}hdS5ADw>z5TIP1o|FLk6ePzr76my6fHtT*3aRj1aBQuH^H$wGpLQf%r9w&mU0)Ww}8#M9CFc#A#dRndUwIjpzdip zJG-Z#-d`H&-sW9`B8D>7uGdj|b^jExkm7ny#)p0mU-2Ta$z;}4&IKMyD~L6`QhJet z8XHOa_dKMmubpj1Q*vqBmZoyAE#7!efr}pwOqm0Ad+{Blr>Z=bs|{GELa8ePRc7FL z76XOrZh|ae?}Qo_=dI_9q97|Zbhi?yS_T)qcgenp;|jciPCiTAF~}mg{D#-(q2uzq zzAYD7H9uZVIx4uoJ6deV6kWH2ueuH(@z9tr!$~^`cWSfsx(&sSlO>!XU{=gLawt%s z+LE$NI0vF|QLYqIqi?IGv9T}T-E080#q%W;R#C(jTFt@`2d%>_AhednLuxQY(KIQT zS*l8#od_dFF?}T8Mm8)FyeNG@U_ilL)Uxj)Td+@+5z)WR#zt7+1}y+(JI<$SL>*?S zqME;iG5r{)dx0ec&Ni-Ps$0FC4wQMNi>9{0p0Ui>qVLU4Kd}VDT(c`1bkm~75Y>&R z&>0li{nx@GD|Y&c$$|vfQ^!STw*JMWZOE>e34DND>$2R-^$Ih4$G{{417`7ltG@OU zC~rdLbI7$Izy`0nY*7tMotvR9RYI}O`7k}%eAusgysNtp`w#2z!~Pvz?hCCONG(cX z3GHTx`?Md3+I=CnKx0Qc>~uYgkv#^LdN(^f+N)k++m|(q)=&RAoyL%2%!w}tC;w4h? z4~M6_OV3sU`~dD}`{lLBg$Y3Xbc0#3ou2grg2jklF26unPmo4)iY-BwE-vIu4ih5y z7KF_bMU(T#UY0b}=pN69Lg+>9g?S%aff!PGbPL*`EcjOoe82!uJ|INkphr>ERP&di z?=gkgVtP>uCp9=OA8Oef(VD)zR3w+_0STD$+ak9gWsEiDlghU&E1}O2brA;T#qWsY z27SOc-Z!9i7&}^;Vn^&(>BQ zw~hHWYr){kr6^jEOgx98`4w9@v@WYMwGW$eHOAPR8j&9 zjThHuc;k-a!DJwI&Q{tYrRu9si_OXBv#(3)rv5Av7W}5K$k|ss+KX|q)S^`Y0{$>( zoP6fEpjXDjssLoEAv{?{+xHa(uk$ayP};O$@HqKwqZs7tWPp=Ei@8pEj=)xnKN+8e zsI$fkR6Kb7cNp54(KhFxvECZhCDwg)(HUdFzu%mIU8}YYzVmxB-G*&6QOMm^N<8P1 z%d`!Cu>lB=fL?)!@4-847@a)zL~KXcRL1j*s}gE`HY!Kb1HZ~O zlu5W84$PL$EFl{95|IF8CeYwPiEEhwK38eSIb>H0Ox>stN{6AtFdUW8YzghBp|z_4^Gt(f3JgCK6t-TTB5NU>uB|>1 zlLvq{ruT2j%j>FDC3(q#%q;@kfcs@^lXHL)*MhtcXf@#M2J~1}gGOlb4IIL7JWqaO zF#k78U@q=g;ZC^zJma&h=+dG<)hrKBSvfVmb==;v){1cpFTjAT9tAKh+rp;nMKXZx zda3Ss+N8d3u@wYWESpKdtiVf6bZbT=+xnJDLmh##|7vnBaLZ$-3d-u^vxSyu;9MFP z!$h|sIv(3GhACJyA&GA2bK3`yONq=2MoUIW7th~v zodV_zq=A93tF0?s#f~6_;ZRRA93)l5@Xe@-IY`TjY9(A6a7Bw=Tf4VK!%AFkJ*O1J z-O!O_zRcA&vR?Quimqcy%hIXOz^9C=Z5rzb*AfEAY))L387_*Egb-Y$cLMLR}pZUFhO~3fgib(%w;k! zeCyc~tvlqfr4hoN)1AC~S9~-#JV@EZNsf^vcaSKI)Tsxz-hnHUxDXf)&%Gk`E!O6n zT~233k-p`O)WPw1vF3fWLTZX8UV&jRgFNz{diY`p(#o1RnkB}$7qE7!m;AxSS-3S) z2)j&;qgF0~JN80gluOn?^vjzI^E8b}>T&$Y5}_(DGx(h&XPz>_DUrbY^}O`W*IMYFql9U2U zuOESMVrTNQ^f6*mAaarbLoFH;RmJPGL3kJm{w)Y8;GX~_ocvE#hmQ`5=#-AUuR)9X z<`B2E-g>tP84_MaJ*@pUEcg2w8U=9tu0)Vh(UZAqX>2-KGI)b^Vn>COHFE{PjnrP2Z5Qmi02JB)SjhE{$?4-7o9Lv4A zSKkq;giK|5X-Kn-%xvhr>Xtdpk@ug#_$VE!3gnou>-9>_r!IsHLo5>LL59QvcI#t-vIxbp28=aj79h68tMXni;EDcWpQLXrosg;W5W`bF z3619Q2=cyU>yheGk0B<@BsKW$RA#9Z?uf9*b#cvyDi|It2d2TM_+Hft3_Um)_dMD`mCRlx7*>&w6~2UuE^>UQl&zjFK{r9? zMb(51k$kdBh+p2iV!X$7sGzImV>B8ph8Zr1%&*9l#~24Ffm-D&jU{%SsP|agge@_|!IRG#jmt)COC>DW z_gxv)!JDWTvsc2_aoBjd>(ZsedM+~qUmaal05(mTte=W3A2D?b*P@isB-w}tkdM!C z`&1c7htbCif2*2&S){^1>#q{>dLAr@z6-b|#m6(sO14QBWW*RAMlYilMFgKH4V}ov zx9rI%dPP#(deo&vS_H#$fmIIOB45K`l_CXs;X#2@ybE{!)%qRe+H^DB@N}e8ZLB$l zj3_wp3+6RizuA5@e$m>0hZJs(A_G5@7?nAccjIo=@x?c2&tMH1ErFGTsHYzI=pqj( z6EJX=Dy`vpxGnNy`^!ruT;95zUha9Vu>^`)#>RwVU9=QVks$P1X%V^*aO~p8YD4Do z2HeexC%mu|o**ocvWOoJ9N^#f6+eqJaPsY7m}cVc$J5)ZPiwsI$wN@77c>?)BJi#N zV#rDCRNQr->Q5w0*j2B2rx5BdSgV#-Hw;`|(juc8E^FEmKwyg<3XDmfrfP8leTUt4 z{TDcYfuU@A9Fh2@0HefP)WJe&2?9u9aR!zGXs$SVxdvY#h0(Wi+kAASYSpPX_+3PQ z`RgT2p5V}HS)8&^&$Z0;p)&8cPs z(}#qPb^p9sY`^LybAhNSQ+QwrKcPwqJ(9IKD1i|el> zRxx7=H(U$*@|4Kzvo>c#O$&qNTDtJ@jCA1Qc`uuaWxG^p$29cQ42{J=ecV?hFW!;F z?qdK=q+zw_tOTL{8!Qc6V=IM}PvZZaDg?B=MK551J5NSHo=~kAAPg0Uo<}xhYZCQA zIB2fgf|h5yqWyZi_V_lA0A{%TMWQ!A&6MwN-sH|FpQ2=mN20ptE9U*V4V z>Qo?^AY@&g1>#P-kF6FIm;5qgKK&z>UvTE}_9mS@)}MEKzb`)R zfj>NhZB1Ul+neDFNDlYr<|W^~=${-bcCsx8Mk|I#IZK4Mogx0CYE=1N>Jmq@vb@D_ z7TJ0W{19sciK-Etv^CH)jiXbfB&AI9lfWX~;)istN&-=R%ePpIu;V@dfjIYDBEkb} z0OmW;H$syfI-avy{d4c{ax3tM+q$Dm=t!XSxdD}3Hap{g+%UoV*RVmWXp*%N zyT;CJ#GJa)6~AMC#TNc4@iQ&Dd)yCN|L4o~C&bP8vWJ7l53g;J&tri?OF0%EX%@gb z!?DE4OPjMXt%lFS?&ya7ooBOcXj2NE_lQuBy2ud@LMKY??Vg!QZOaoTVtCDg=IJ0< z{y=zXFGe@$cy_txrZx7F&Hqq)95wJ+8izKJ3~siBGXm@JV_*n_+*TWn?`-ym1{n3X zd(hwgKEL(DvwXf@hOuQ5QUr}lrVZGZGS zhXx%jI6&}ka-=+T7oVYYwqe%|L}a7P^Uzi*6c0a^Y*%He(368J#pMPrIs7l&d|41_ zlV1=jfLaMevjV37*id|@%*=7*Hpe24oRm>-;pKYuP=*z<@xgvJ^1#Xpm*)AIA8zH2 z&am82*zJZ$EIkLZ6+}xV*ib~#ApXD!^R@KBsmM96Q)#n@L3{Q_af2QXx4)A__X5~SY?TH3n}uOx#K->B6k&I z;5{RxCRG^QtxxGBT_~vOUF{9E9&hIVy0so~+KCoii6#Yu?cPjK0z8%&j8H->;6-n) z&7r2z&du#?>z;d6xckUg&FRFfIxpAfP{mNv=YuaAX83_~FnslKi+_O;1`2111pi)p zLeCTql_oU7UiV@~VnB&H^&daaPeiz^+kD{2t=KG^_<#Q2oPH|Q#r(iEH+~&%W2$b3 zAqHz;zyk~PBj**-gKEz`|6$GK03O)f9mRTj2~`FB536O0f$a1r>=pRh|84Tq-#&l* zc)VJ#Zm-riTQ2CM|K9JUP9^pA|9+(Zd-{L=`v`vR;xpl)Hz%+D$(w1rTMH$s4kCN- zqNKTrtPkZ^P!lK)aO|fimT#Toi#Zn}+IE1RI=J_{nYohm(RRWX#R@Zxd0kVmLR~ZUP7A z6lQ=A%Ppwq>F(xYx!#jf=#IPDUlo{1;=O766D{a{aF^WhRzcYmEDUama2CS|YcZnM zFflRKT-2g8QiRDu<_X@NPX|!26BB?VzTX2!>GMB956>3gad$>6$!S07WS+jCpbx+I z=*!6@KX72w0`guJ++@^X z!}?~)0c8FlgK-SEZP}OH2Idjry8@-D1uwedXYTeGmpq#YBs7Z=yNM54O=DzLhER+_ zN;W^pvK1wIuhs1g>IcYFo89cQ8G`Lw4g?^Yh*l&x0n1a&lQL*G6GvlhxtYwwPW2AA zI{q8MYiZ0O-6gc;4wmFb+x}<*ED$&3)CL)hiwAqYRU?20pu3zUtP&| z7)$HVdRT7dt1E#9;1n{4%DRGQ8LaXX3#(iXyDIj!f7<4f&-I`1wC68ZQ8RM6Py!|v zCG}^Hph1&8r3P{%ZFwQjJ#r-wmd_#QgC3*A2mqlk zxNU}WA9tn4)16L<9f35Bz60dzC?11z(;uDz4$hB9Px2ygriOT8XJ?z~>CNb@bimZx z^5WGT75lLtosr$*bUoMWm^id`ON!Yh7z=Tkdi4!#JQxNdFxS?sB5E-6->tPrZw zf(=5dp0YAb@TxWluq{;uocsT*5|m<8NkOZdR6P)-1IN1D^pfn|(l;DfkF!Hq7|w8* zRh&`7%;MC_O!SJYW73-J%L~-JMAUdQsJ84EUI-Joj!31tKC?QBw~CO|cw*S9hwv%bb<@obW@p+@vXNXB`Lg z>jGXd$uc>1PX6w{+^?I*tEYh$@?FZD===F!6LoL0KdyNLt9}h_8|n%t?uaWN3IX}) zI4kHOPOt%y9VkM69}tZsNf5TEN)iq+QiKT6lbcK^Y;TJT zc1Kgd)o8vc+eR^hmLh z$9HH7Y@Lanip;XjTRdjAV6c|4{a>QAE)+VJ!UKA;4j66qH${p2qV9o zh{=Zxjtw~j1?@nW-}hRc1zwL zlo7ZEfdiqb*1vraQb38bS$LRpmts$sI@;sy_1ncxERh(F zn0p~t;w(`OmHhGyss@Z7O4ec6_}lgRTH~GSj>8=-xpmhosX5tQSuYjIiMxBjA8Rls zEDCvD$g2qA)UCj_)jsA9zPM3i*TM8}whlc=HYf6`Xhan9%7`q131$^|V0WU~nars4 zn{TXxhYoemS@{XP?RN09`XZ)tesJ27${(_@FsIDk&!Mr#%^JgIVkrFwV@q^5!<_8ZeY zZ_KYK6bth2%+^;Ex^wV*48>eD2n8E9YMFv)u{XFI^$m8|%Zqb7wByL&-(}(0YE<$G z2lmhr9v9Bu3LIw+tQjakH@&m*vaNv~$)N6BPh98!j z9gK(kEvAy<+shZ)EN4eVR1@1U&r$`S9E7-gx3Fg&(w)bQe|()`qq@3_NOFcDO9K>k zZW~ab)RH5+`7C>FSEBh=r(|o!q~Ta0s4`$+rhnPnHE)q9I0yGh3L)AsM^QzfXeri1 zc;CeZJSsQ2C?r1ONof~g4Y6WmYCWz~?Rqpe&o%s`q1C+=NV8wd%7vGTE*g6KQ-XH%ub!`A;MC+NLvNJ2WX>`b3; z#~lr`ih7Q``ccRnnZ&Zft+e7FG_yiCg4e>+pc)Fc!JUP1KG>#&7oW)V5 zIEe|(Z;)ykTZ&KRK$C!w1QCiUg^T0u?dlxdWSfO;I`W{+%EQ&U%4wk+_|50#g>iw;gBY?;NB$`h2$f(PG*Fof8M>j@ z4y;f$%R_)LLvOR|hdW*Bm$0<_iQyOM%EzFsu5DVC2z|{UKqGVpLz#+0wI>Ic;|V+( z{kH<2JiD)#K(Uyyf>jS>mZQ8PMp>$Z_}fS-w8-e0G{m(O+6Q5ZbM#vEb|__}r0BwZ z<@{1a1AZCN8F=HX1!BFy6!Z;x@5!u4Q3bV>Qvs5oA?Hiq5PW5AZFh zho0m`g2PVcQB@19r7SNjn4Z;GQc=bz7NCkLUaCUbqsScJZ>?ig#_wi0nnilCnFFP-t!KPG<3JrN!TRSlkQwJ%p-4jhh7X5+^tA;TX5Luefmt4_EL2f|XciB{aZ(yK=q$6b!H= z7xC;<$E^)D?b^*|1Dya2XOY!Y-LrjA4L>n?$CA9QKM*2+H(Zl?^%7a|UIL#|!0db@ zkp!uH?kTfCcG6TFnLZ|A14(OOlkmcS7)1P==b=V=6+h%!D73q)D5C~E-NeD@K!NAC z>OiLOTmT(IPI0pqTR(E%^Vy`jg~)r2B^}A)@iCHRJxH!gw6bl;>gUDVDxX4nY{>DP zk=p&5>P{3cAApgMCdy}feht`tI|2rgk!3meXd(($sn%;hb2fTW*n1x@kfZRch6PBK zH>NWuV)9tjamRpM@x`_{o>;0yuv$W zyWhcNn5BG92K1(H-xK!)4bWOR%QlOrhmW2eJlh*Rn4&~%(YUDtlY={Mk{pSO#19a@ zH~|JhZ$|QIq*ymiYhI)f-OU@sjYZUPTzgPYG3e~&(G)SdNQ1h2kKtBwyIpSgn1=H~ zqp=69m0OYW@DS5iK>T7vovPi4vVg;l(xUaCV`7;_tE0)h(uzFE!F5ISnNJYeMR|u- z90II6dG{3q1(#@$kTi+1)Qna#ye1osW(tL#Le3RxRPc#|YV0$3{o~PCYWc*%!Zu-L zvBZ?NLCzyJhT#B;oGc$nGDvSP#^niS~s6cQwzypF@Kx5@DG zJWvNl)=UpS>kCy zx>clM62b!&Ni?<L`siAx`-TgS5}w%fqBX?K#X0S6%fs_3mQF5d_iQ zU{v0D6puqTFxm8dxHy1|taSLp^ei(rr=z%DS+ZQYV0gvr1o9$-&}gWi|FuNOXAg*{ zCxoF72=OU6gDpcw=Q)VMQ{dBAB7|=v2kSj1ADw$oyow-S347}WEmjzHAtF%6{JgaJ z;Kb01mS=P$23w?*(OQ~4r%4r~qpOHG3eOOl5r$7}p0e%>)p8vFHQ!X})|&8RW%(GG z5wxSq-SG(H{mwGQD5PT z-SXVllsgEU12{uo*Sc0DiZ%4@=e8vO4y1IX4)hfvfcj~SgSOpSQeoMd=+684eSS%E zkN2!RwJ~F^; zyOV&Av3Wx=cvqg#?^26cEM_p^gQiDzgh^j2X$#dIjw%VUtwknKI|2$1we$Ka&RCyL zaVBUWQ|=oBaiO;HBRJ@RC-9)~-$a}*8*D>wd?M$F2L2QZ55qGyK7eyW|4%QTUZbb5 zG3?amPc%OcMs?{2IfAns?A90}s?UmoSPPN4he@HUEV888s=jyB7P8vtExjY(FaDCJ zu43cl+PZ8$)9c#9E$(kI$}t2NQ=-WA^~?1Yyv&QGu-&Z^L0s!4`6vpF_2JtTUTPQM zVkh<&0*BbveOoulx%Iq04$rIg&L6VkOE~U3TMWWC3inPh8rbONH8^}0h>*gK%*SrK zJ3uBhH;@~oc&xIvQrYBLge26d!TacqHXbN!^cWiLZ)uVIa;myxgaNddpc2T7ii(|CI%2Zj{ zO+27|c?(Ro(ut;6j?yGx`R@PMLYQ~jF~fp;$e&#HBF9lDPYAg{%Xg!M{Ok2-`|!hJ zhc^{9ceSL>F-pg{hexmvIbULp!hYx$E1HD8NE}q~&1-7AHOP*j{0#zIT+64-E?o?RbNF9N*E+;$Vkiyuorwoxqo~5e;-VrA3T5d zbow7po;-c=ABQiFrvLHu_0uQ+aq#5H@zKjC$Ip%)zxY3ezF=?Ii^i~M<6Z1aWQ#WS zpqqNwO+D(SKJKPI>83vIratSY&bz6fx~b>g)QfH^Z;TCL_PLun9SIWBhw!uy;%Oho z(>{==eJD@+V4n8jJnaK|+K2SC59(Es z!+XCE@BKc!_xteP@56ha@V1>F^7eK*eRMdTj=60o`xgbrlzyEildNVtID=FdkV%YB zZbqU^B)bV?oO8L+x`{?k+$`S=nOS|CKZA|b=t4h`;pb~x(X~` zfmhzvoq5>D-p}mYNYR7s@`((8&4+8W^-FpX{_3i~m7JU#Hvbb9&N9jivEv&Gk zQ2KGeKAdpGN+-KAHjXHo2lE4pmWJVb_j^GG?`@iEqSB9nZ&L6J8-h(b$mSI(=(W8#sSwifGgask+aHr8S+) z!iz^uQ(|xIrqT_(Uml#AJRD}#iiulFL}&JIeal`Weht&Ap~-+0gRP`S#C_0Wo03PB zaJ)EoC)4$i(iBd0G=tL9>9a>qrek-PMvIT2)M)tvydr1tA{7{PII#n1sB$!O!N@4p z%|9!FO{K}%cWEM(u=Y)^4J!1Bd=#7kR+2o=UcS;N!=W>FPIxdC&p;di)i8Sawrr*N8y@hVy|+*32Ud-t;P<(x|Z z*-#zW^-!s$cFUa=e2rJwO5oHLyvsTjRcU{W*-~SDfg8&LMIc5oc2h_j0u+pX;w>V~oHR2s-s|-;?9`a7JK&JReb>|pR%+vd4X!;Q#kFuy0vlKIK=~tk> zGY0QAH!FX$?rgK*gJ^Q6Xrtv}>o=tLbT4pWGqUt>2GC@Inb__PZ^OM>aui&FvO+br zV3fmiN!MC7k`h*!vy%V;a?z;#Bnm-#&%84{`bF;$1G0s}w&kom72L3HW`hu}96i#( z-AAB)ziA+eD%Pot2yPaLOGY1YQ}8#`u*NL+0Y>xz+#oMZGjg4}scIt&G6(Hhb;C_? z!gr8(l$D6-8BQEzv`KcX@qnsBP>a=H7^(Vz7itcn%Ql6HT_UK^R~N|^AC3mlqHv}B zqJs%&)`AfkI(lnB>F!MGn@VOuX?;)1n%S)NBM`=K=W4QRQWZIxCh3J_*oI!K&$VlA4*_Z`o5j^ETRJXkwRo!R3IYtp8eUPQN1Xow_d~ zMN75wvt+8qidh$_aO4!ndt%FGed!c#)=Nb-MT08ScT0=BIv~$YGjX_c`_*gM4QYxK z?vXb~FcjPX@QNL{=)!ve@ZpFw>|f0&N@P%Q4!51BU}2?rG9?Bl2QCL!HE8+&Stbf? zlmvVK1zN%fuzUSdEt}UbMHA5dJ<>vPe<>O)?k`3A#r>sd&bYr6tsD23N@Ir-E> zu>a)Q{+LQ~Q0z_yw`{I3b2KWa&$MgnSnUpz23MPTjG-m{sE8(m1S_wh`uqk|(sR0^ zS`m*@Mn^hJGFqL*TY#naM35W32@~?F%hsAaXi$@9awE|)<@{K|iA#yv+ncT2mNpWn zr)g8ho3&Sfg%40^NXW?-Z-bHClv7M1qBEb_#NsI}JwG zP&dUZf4ab&icPjil&8mS=vGj_gi2N^MSqrFVS@K#i5BBZOJef^X^h*3|I^WQpPN0_ zFF80~FqT)B1OrCWQ!LSOGpBu3C{ ze7)h20^$BCuIh4eU(g|ze8vdVz@)3|NUE`Mc@o2q$mP-k-erc=liKUTOZ%ug=)_uSJrzEj3?Z z{_H|Pas1}zllQZ;Uw-`v9n#yg+3VS>_mdZ|-UCQ4wKb{wQakmk%zuG?U;S}r3*n1+ zvc$9Uf@!f^nomQ3x{NJ2F(>NT9+ZtK=#+Jm~N!*@Rg(p1rH+ChKLCc`Bjncd) ziHPhJK6#}4gKrE;@mJd|Z_XkZLj3x$kC=Htx%nkc3W)F(j_lc7?kI>TZ~>QiO%dB& zQlbwb!R>(D+b$cIr?%<*>F?!bR$Dm%11gK)6yaB4cXYeuv3s32QZ3sDdiC0QI(h&3 z{9yWc+MxXA(#4%>d8M$E?nyI}zqULyP412vs6$|QrARCjuulHjgGRR>N&E^z z`HL_j*cg{sh#~fT$d})|8^D^rDeX05SUPOG@0U zb!Tt{2ixKyHyG3U`Eu*mb19+V2Jo9To^D|O{D&elWcTeAuHz zp-~VFBzZ~2;-EHoYQ<=Vm3Lj6@)84Om?bJ*t)TjT5Ctpc-$E!YO}g$Tw9~Z;I#2r$ zi%|JHjyK5>C|h>qR1M^uMv_NHk@X~2hi&rc>Uy(~&@;@$_RhYYth>!QYzHku&}-ju zAki?2+u~*p56U)o(20`@h!?3Ic{Ws^abxiF%Feu5r*Rosj7T9Ku_ru%Co#U zKZUg!RBsbZRmxrHM|*d=1|GndV@bhI3s+#z(fEFnds4iXOp}YjMYVSJA)!)=->IqQ zMMf}!JC;xdnI(YK%B_2l*(D+sP*VpA1>GMWxS+jJehxF$&CRmqL)QjR1cGp*l^1e@ zY&nNtZ<8`oP_*4RdHUr&*LqA;?mz0!PQX3jucm(!!eqsNDjMjT)4K$FGtl4Q(u81~YsZKqzN&cw$+i$eP7FSmhd z+g>a;=QoIzuXoE#145d>_e=a}jaP%RV(xejty23+K$~85G^chHjgzIpmQuOORNoK! z)>ICZIG!6v z&eW{MeY6$p89?j?dMYjX?o1YA3cM|fggU4}X4nV_l1$9*bt5QUlHc~iZLMW7uNj}I zT@y9|NIWncA@V4&nIdF{yf-i`U}~;}xI7UYL#_4Bh{Ju3GXW7LoQv}CE{#m za*SgU$1_#lJf&4-K&c#Jrdj1EbFOc8dc*-P5f(phO9%Vhvm#HH(huQ_Q zUfu;mSkP!~Sk6IG721jvRzX91LuqOb$_ANW$`eTzp9Z_CPWDo6%0ua&lb-QC$=wcT zJ7Yh@2fF)y+n+9je_X>7vcR)ZS5bkqaIvvy-ry@7IH$X9zZntaPI(wnsVuUH#Jw`9 z^TR$7`HrjrNWBvsgjWBq%xu9>x~P>xslVMU*+S(Pm>hQ)>#r_ko zyl(8{A2$m`1G>Ru5|NbL4^E+r@{p6bFC0G4r+$w0Y=+{8mW$z!w(D_t##{6f_qPRG z%4k0y_8(9m$HmzEKO97Tfn0BsD&x@0sq6N4q1Nu_HT9dFXf>PEx=xGgTBl8|>)4uC zC|aUTTU*<-T5V>x>3Z{2_3Ny}D61tHKOHQl(F&=xyrw(7yBd3BH1r<4TDRBq~4Aua4clt~B(6T`4KHilD zTJ*ZSK^*OXh4Vqa5=BjC!8|MKIJ7*YsAEulNrU|57L4h5nfqI*QEAwVdgMnBNhxd; zO@2}WylOexcDdNmbWs(?y3OwR(~UgLEj&#zuoN%CrsHh{^h#9l%%X|Y+pA9$FGUYB zI)e`fT%B@?esStY*Vf?;Yps07t(|$>8uCIkP(Jh?-ZrCXFPAb zR*T1z4sK)GU%;;Dy%`@O#zJfz;IgFRfg1!R9N;iCs}g=m$XL;kC9B9vLI!EsMI}eQ zO3(^H&Px>tvgN@bj)7hkSPNO-ztAtl1Fu*;le1N8py$b%S0^PP{o(_~I`I>L6zCPX z{8tcs&w&sUtW6<>anx#oJzeK5RpTpFTD$L4rl`F63h&-uz(Xrw3US<=ONOs0B=vW< z%<<~4=YR$b#In!w{sJHSwjoVsvx|HRd1k^VBiY%5d$ZN2_07r!K2(%2aWg|=`^&dt z*N}|>1WR!V)IP&mUh7FT)N|38D_HfefS*2$L`koi^93BX(4p)rOg^Xg`0x`86S2<-P^j^+{53Tnz!H0vyp z!uvJ`f+FAI5tb>KG3*A44q98x&oFcsrK)D*!`95GsWqYDJd3&zL$Ckhnigq{e%pL% zb%?wG%{wTQifmV*#N{_jz_#JF9{a)$6ordCEb?M~earJ?TiAqEanQ@rir6pLx3a}q z(-jC!q7}u?$963AH-#3$Yu;TZ8|qiBQnPS>Qs}D9$SR?=e{nfqUO_rjK%mYbCo9>J zNtI|(p-LST2JxUTLD_><$egTHQ|%a2oovOqy|!QHf5xg5308#R$Yr~sj3ohueC+bm zl7C&J=z1eW6Qq=ToHyJU((VbpcaP+p@Z#4sx0~ho*X@D+E0dBUH7_N ztDwnR$`h!wp;E0$_47@WlPv1D({uYGk0`2A{dV%MU$!KECW8c4>1H9fG+p_SZC-6Q z>rK(Qi9D$=j#B|aipgfnLOR{YW*V+?ky&GPRW-5H%a3AURSzur6_kxj@e-EJ0VYXG zln1MwM}XPt0`Gv%R@XPcbUWWX6=+Z<8&j0%g?p&qk5?BM*P3sN(3^WZe)hx3@nZeg z9R*6Mzbh;>{EUjBGah}iT)&xJi@7afIAP^2lX zK+HpcwAV0C(cUaFaUzHllqQ)PVL7OWH46Tc>}Ac!7rJNJhrt2er46;~aX#$k6C&=+Tf8Pn zQ03>hcE8ovwUTEizin|L`3nqSu!cc`bEG@@jG%BUT-RS*9ACigMa6?6)sGe31e@Ydr2C4WbF#jTdQSkUktU}Gyy|+=0paiV>Hu8flk=a zBPa*#NODiS5#qfaHhd`U8kWhaGek8K11NnLn=8>%2_9;DiCh>{6w$oboAs4NfyiPc zLCw?-yU<6ff;0Il30*>;!)T6lWCQ>pT*|5N@WcyW*s#22;fLI4wGQ$e%J$dRaJj_b zs$>rssu`Y|Vn3wb_5v?o=iS| zq@=byKi+KSxZb#wi<76gBk?~$on70?8^c)U- z6Ef#mZqWT5t~S$zz@IpesJMBF2ZJD{;dW}eUBX`z)8dmP88>Kv+TB@AyW^a8$2sj{ z$FvKVDVJ$51ne#VOULOi%M1#k^WJtl_-Hhpf#sh=jrAFhNkAVmeuFj+yZiVE8wsG2 z(noZcok79BBfFY+83=n3k_T<>>xOE#L2sS_R1Yc8Kra~p>?Mz2)*-3E=?Z$-7qHNB zkC7E4N6lcY-TwE9`s`n~o(?-!V(LqBz#n=s zPF1dwS@aXB} z?>-qS5}-|32{In9R%;Sd2t8zYjxl-i1bQN38IC_=7vOHoXSPD-;rRkQEN=c;3J%R| zLRqCJk2$YF>XXUeWv=x|jmm?Vq95ra*nmu%?lV>%F6RI(C(j|E=^>>U%C(U|48BO? z$HU{hj*drnW$xdR3Eq0V%l*T z;t{KpV$6Y%1SoVl5bTAeNWVKt`qV$TmIma7{a)%4P1+aF)e@nk4 z;*MN6{K+wV!IM}$OGsXIV@!U+?HJTF zn#X?E1$gmwasFp4C|F)7MG*7h<$AYT?D$%+Ovmd;X{8V#BX=#{6ioqDqva0WpK|PF z8GM3#1t#M*CE){DAXSyRf*TNVXQ>|mM_gssxgFm23A*(gf;?AJpy6v5z_j(Bzg^4E zpJ-TiLE>zEf*6@gTw@~zi+N!OoNZ_|qfl3-5?P@vbSp*l0^0~@Mco76mnhStI%wrj zQvZfT{q*Mi94gesh1Bcu@`r?Et!7A(VG8nI=Vd=wBx2%nmq~%5kwksft8eqm8}Jei z4cL3+a$FX|I=o(fCv30+YX49QFVm&611ATcjHB_Fz!LE>hk1xzyu^#!bI9z`Hjk{x zvX@4RT+C0AwnEjcjgcAQYM1E{=+VrdeRnPTjy^8%Iw*c@U6pk&bu zU{QW{$jd$#v@An}xLJ|r!`n!QKy1KZf&6AuR6xgMVGZ<&*iK47#88F+fIbG*u6Y-M zE+A17h~7;nB0DXHKgt|;1p%B_y!DKdRG@?i2r_#%%wwu&lXdLudzAn8cX zpvOQDtnDc<&H@aDx&1Z@V3A;Hs{#;39!8saox0q}097#?1sJAOx<*8M&?;3OZ8b*m z=(MmBlv=PGnrgm@uo5)jeuFamW(&_m2~d-J40FTqZ1HNVXOhA@?42wGEWxfb4kH&* z$*4tpu|opncE=!4tS0YV!Rb=(0glcP&FDcCzM6?`0X7WbSIqG(R8x`G*#w|hJ%m)j z-7$yziRebOhmF+%DR1imRz6QoL7`Wspi`6y`pe}nnK6ZlXrPfyk39he7D=P$vv2lm zum?ghz)hKonObU$47GXhl#D^7O({!LjSSZ?^|_rW)6C6{#h_MeK&Bv^_rL>on8}zI zWKB5Q2z?M&^^?`fsSOphgAQdefJcbKgWDAmD@j>RX$78LJJ?T!3)s4WPMT5ZmH9!7 z#Aeq>MSx~YX*=YmLys?H91z>^@aQGZ%Zye^A153*98I3VMsLA2;`oPbEgnVo5DZJ= zAZy2gWdgji2iP8{_7Z0gR+$g0@bqn)nSLIo9Y7VO3!CAD=-IFSNo870t4t*Of{3qv z<9^GHca15@l>LJN#26idi5*qfK8C8#?Bomu5S`&3cBXpNY4b5aHkf`79S?hnb0#`F z%b{T7eOc^2p59#3s~)#1V#1bN7M^y-(-Xrx3ylNdj($27hB!{iTgU*Q2c+quKdq}! z8)Dy_gE83dWD=Njy~)D)aR^EwUoCv8XM+!tSo1`3rSFYA9jy+Rp)cmJ&Q9faEp2_L z%N^Mg)h-=OexJRjPHzi&Bz|^w-D44zL0K>Ggr>iq1-kJWNvRAT4jmSM^l$r30Huej zH&NzvNCNP$aBY(N41`{%WCwdY4BDsR2s`K_=J1AI?bCOmO@rwH5`mLs5^-@3T3q~t zZ_dkfJJqJZyFpVOY}@bbzJoq4bXM%3GgonzXeM!V<;mD zu0G)shXN@$f&RY4cH?at@|&F&K`y_5KK|zl7ZR00z;&I?hXc+BzBRnJZ!^XYRp-fK zl_<1gnww$6T+soHdkB0ufu}0y2oKb*Ii+)yPdnHwjzA4wQ0!Irx89R}dz|>_!4Nc* zIx9rt8P2A&+wsIQhFg*u!tp5+2f*m;sr?R_IndqWA-7$_g%*Y8pL{mtqotWtLygqI zdG?k;4oTnMwF z)EgaKuz39k-^liu%TN(Te-KW^cU6W8$cXF-2t>WZ2^D{4_#ocH)F}9mHK5*+JhX1- z{ra1xUU_DB3+C<^O2KR3Br2W37YNVs)`_M=TU}6H@r6(RZgqv>oCY!x4xCQTRY@aC zISzuO$KVSc%PZbKj!9tdMGfxU$ifN~)#Q=g$U~Fpkcv^;qW%gTZy!Y|U_^nS-6GgF zMJLZ0K9(AmXac0Q$fsPcY<;e^ayQ8pCfikV))=tOQ-U^=D+ZjgJOj=eC6hgkkCHvT zkJA>%z=l2?cKt|>x~5oq8}zx|&Jhd~Jl1fq<)8FK1Ze7kGvk)5=_6QZ^zA|A-wU&> z+M`YvU*u_~VTUqi(}d6uVp8om#V7+zs`A zC|tE0mcTHJ63K<6Pe8T?JhDmJn|F6LERvV3(EWuWDUP%pox@H(L<2F3c@Q3lQM=Ly z0D=wFvY^~S2=;6mFt8jemsyxH%|NF(`7d!bT@00o0bJL|R8{?J)rIMBqgk|t35*zG zXmuFNx}z4()3I6@_NZgqVTYGH5;|jBWu#GY7jEbi{GK{w1gY3w*4mguUX6HXYb}S{ zpv`tX!Zf?42RK9wFyg3d+C?fx*&PpdjcSLXN5CEBZ(2}@@!>P?F z$z$}2&1LY6O}x$HmljP@hsVmd3`)*cSyotgDGYRJDdOX<`xR0<%Y#&O*J+{OE->(J z#CY6c3UL_qdKSN+ds^!(E4+aelzNJ%`dYw&b@H|K=NQ|4EI!Q*?mns3aLUPf`lGq?E`aqV)GT4bIqJeJ+@$5?mhVIx8DF*8IkYd=5Ek-;M z?+Ke`vrEPX)j@?PU2X-bJ;(}z*mxh$y&MF;(@Jt9z+e>9HeFn@V!Z#6wupUt62Rw1)-(N^a=NqN08}`<+O^VoP zwCM?o3Vp8i6p9avm_*kPO$5Uw;ulk#yllnM$O@R{I=<8-VU?ZKk64{Ke%LVzW`i0d zFn+LNLYYQM4m*kVh~L*6F{bdYhE^FI1~Gk3)2iuIlPZwoYxnWKa;h?p7WXL7Yj>8D zvQl=}p~wea!33$8UfVxXJGh5bOrr`4AW+Y_K#?Y#?U}w#`y8I%E4*n%mz()&``NC# z#4v@2qickaLXkw4qeg zfFyz%HCy#|op`?}Xwo2)ix_M^1&{W`%L|x|Z(;LTFfi6jMvc6h!ehFH z<^`K^6cLD3=$7$?V{_qnFrC1z@| zTq_L_xM|In!E$3XhIH+VSDB9Dn9;F|U@ z%sLQz94*fJEaJ)WyIJ`<6?;j{M=-6ipYfjo&#PAm3$mKl1&4<;ZW;R!6>&2+laryHUo#aUgkUS;Xa8u^U=3#^;K?eZGmz_-MZyRyw!6m{f+3l}VrNf>b zio#M{hBWqg41}R#NIEMO28J+5Q=pZrgeeRNQCAU!a}q69D}(O@&Fo^!xj}spZ}ucV zCQ~X)`w;G~0wAD+A+iN@0|H2KBkbAwm*tnQsf}gdSmBUb&(;k&22chd=w8mlm3ryN z#NR4VfUD< zb_INi0MyvO_PfSGqyhq6@Q^Da3Yl7@H+iLWV{s40$$*n54x3WB9MiH?r)k>^z6+YgBrgEL`aBrH7!uGAN(x(&NL6Cx zNU7peod$}BC&g3-_Hv0Ao-$TSQCeUrC{MzvZLtj_YQ5|iUvE~py2Mguy6csa>daqco8smA1o(k&wVgM3?;f`T5}FuO zP}7DT#-4D5U+lCo3G%zFjK2qQj zK&r~j90i~vfExufR|h1kJIn-xHQF*x!$z&8Lj{VNPAOV6)UqYkqd1F^GCSi~Yl5-? zOl73k@$q1}M(SXK%#mR559tFoLz5#5bSuDJg zX5yp)Efj|_YITK^RmDE)E3w7}xVLBTe>*+<_45st$W-DqMQmy#SqZCij@AQ%`FrPVkMP0O8lq;BI*eN5$42z_Y zxH^mrf;3`UMPabH@(L;25f7nk`fSZTr2&>C8{G`NPvjtw@!EiRL}rst#TMr`h_-RP zgpr?3LGuMFq34FOsRBby2owh>umB_5Fo~8@2Y*0L0HQEQ%eC@jdPF_9P_H&eW0wUN z46sO5wx)E6L9_J{H0ePp_w&vA&qaFMf=)^%uh-oy=#ekI4NH*^3Y)i0u+U}>E95x5 zG@BTfJ6UOn3Lr_#vvyr4pFwW-$e9EeLzT;z-3MYV!Pyj^bs8Y)25%pet{j-MLvm!Y zwW=U2$Pr#mj|h9-EDo14*IIN@#+5o=q@CO24yqqu2yHnYUTqo=eB#;7wFPrXQg4SR z*-RZbiG|Hb@LX`jFfj_bi|;i97Y#r&70>_&D~b@1JMe6ux+Ae}lPDK(9CVr&oZBt`W?^VH;of&$GlF-)z;%F43-OO4 z^=!K;iMDKC`w*o7qXIKSI}!NkQ1lV9z42EAV(O#^UNIU1c7tk0;_R zSVZ{*&DTS0FR$Tm_ay4Y@_eFC$06nbkG3z^v6Gq6Ik3#A`ub*d&bY2_a7WUt)5FX> z9jm(5EpDy;vk06znSt7{nH&zjCp~%H< zA3VM~v!2W-VXwCo2PW79B8HnQ#Eix&4vX5clhx&I`&|v!GL>CKt>euXjA8`^+vR0G zR{`ep(_$mGwHX30038vf)S!YW3I0=`hr?hbSO8kHB9Xo)Gz<_6W_>F~i_paYijN`t zw{~VH0ROk;X1x-(60$WireEh9Fx8jdow*(Ei4?KEw(Yr{yx^txYj0g^F3&gX?fUa> zPc!$dt_C88))Dv1;ap*#HVki{i-#c~ysF4%=)j6&=Ca&SY<^@wG)3E{S`My6QzUW*tqRdNPkp_E8rB1c;3@C~ATM!U0 z39gAzwM4+;bA=*I%Wx3??k_k2;5YHi2viB>m&^H=6~dA%&+*`Lw-t;!d9BGG?#kkL z3Tx}0&=ma0>Dq9SW|-kavJ3_`pUmJSfyYL%QX2Gt+m*xsJ;vG!F9aw0CFEkrj8^@W zEIg>{-n;eX@;r92}Ojem&t zvn)87og^j{A?)qVxe0F6fA!i}A03tmWlBXHybh3}>;#1RFEElxdlj^|h_`lC&CkRw zkY;)A6O;on+16LMj=WiZg2PB)ol6|7O>_j@=582{($dS@HEaTSE;c9U7+Z$4$6gEc zxB?r2oorYEFUpq)oxNFJljgi!e7eymRQ3Ec%%?7cK9SytFI005VE13C|KlIxrFMBgD=LzEB`{` zg^eDA^s3kfkdkAWiw-$gv54nG3wU=B8#m6t?gEx$#)gQBeE#o>wM3IwKNyiOIt5+GuPxb>M4}l0nNaqXG~>& zaAKf==!d_!>@ugXI9_47;^CyDOZ4ia5oiEBi6_5pmhI9j`7=;h<+tRgc{=lW_b}mq z=xGy7CLY&3`7AD@x%@^repy^zLmTLatvm+-H+51vum!%~PItFt!i9`F`?!bsFSI|1K!9OE zPLQ33H$hS$$TS?U@l=Maz6cM_!Xcn@@ygvSD7MX&PP%)i|GZRMiXWh}*jf4maq{rv zIf95U<}3d6^XZ?LK57GHf}MuY{{^b=c<2QyVCu`0>ollj{bI5O+m*9@MXz1= zds?&h_P7C$O~TMJabyD0q8R}vRjd&c5k9g?QCZ27AljBC@L6S(-Tl)W)Q>73Te+-` z&no-3-b`!?)XN*H+j?O;{&TP7a5zTLe~17$*ac0tFeLE#m|%mzH2fvc^b|>h4s5P~ z*%q;JfHKZRC2|#M7#R3$%0)#Y>T2}hd71j0m|v7aNA>7T(^nMvuwGtFa)f+<+_z>N zJA1EiW=?=eH{2m_EN&+FiZ}C5U_noCq9X>2NhGm9)ame7XLs7}Naf)5Sbn2}DihYjv)qWkMBEozwuUxI;XuU5=4QI7MWS1$~iO ztkBNz1UEaBZ~`y0XeJ{xTK0S)Ouwvpeu~qBk$JEd3_4Yuv{H^IE7Nrq zMBXNgK{Zbn#ZWyli62P435_^Wa(;0%VP@Z8THI3XgM&Q+5-E>H3CxeYtmbeKCYZ=j z5Zo$HX>KF|@Ck@y1mS#ewG_F_cdPScdzl6O3ic&qpKZ698r)lvgyk1e0lZ@NW|}|Y zYi5MJuql(7;k__(Gsr1;0V8Ms;wvwc1P#fI?H;e~3N|5|m91zc&A}ZDh|FF14T$FY zm61-WGIdQ^=(ZC{ysIrK0P=HK)O2{C9s|m}9yfw)QTqve1^@@o$-$hBKRYg&3 z6b!BrYiD927bGM9BL|k<$OttlNrj{dlU;Nnf+|F47RY2#xG_n2a{hY1hEJ96P-zM& zMuIPIu2xMZK9R`d2JQ!I;|d@FExxNFXG?Az^iZ^__(>5%lGqn^q5vG}bVrvPfUDi_ z$-5)N#S9^6_{QRJmje1^nyB*Ql7#|Hi$#ap-3xX$`D3IiNRJ7gf4G!n)W{e*WBq?S zd;QceIa-d%U1H+d++{)_(nd2>rbq7W=2L z;OXIL-qCb_G!Jds{Z00OR*EmwE54?qZA`tCmH=?!04$2S0K-Bdc5tWW9ON#<}-rFWN78_`D{TG9L(oKoCilJv(d=c9PnzuCJC?VL3EL)ubsjyTA zNg{uXb0!ag9q@i}A+&?4A723wA>H?(4VmeEW$-i53&5JCT#Yia;L!Ly5w~G2GX;|< zO0GS2Q#_1EO*iXd-#LC}im}Tin~3vz1v(noi?ls^S}?LnZ%rW+Yv9n9^3J;|DJ?Wd zSsdj=Sj&`Op|1Uon6pb`K7{L?Z)j#BRRGE14B@7PDX6CmlErikC$gI@8LkP{U&6u` zL`md!qft?#{~4kK5;)`<~(Te2OAw$q|7ZkkqK~;%N689V3sjJ(v45P zEsyaRN!UE%Mxa0pWlba(MWwg0t|WA4uM^BHvm48kv9|BU8Gj=GhWPseA-0!LdO8Q9 zM{rKv7hO4RvI=U<40a&*qZNI>(8GQEb$QK=k0nr1ViUS%;mI<+AT>FDBQ{{^r48r< zuq4743AoR95 z#G{a7;zSoieH_YB&>fcNtae`TU!v6CW42~(Jy21~n8nfmd|3$nyvw1_`$-fFb#o|y zrAMTg6cOFiwGTSNve@biZ3zwEGZ?di=YhALK1_s>f>gi()v*YFB1>#kxj9U-(D4`w+>b0|$l(Og}FvwnotB3-yy zZoeYJF>Pi#aZb3XDFFHk8R5`rv8H0y?BDB;*LdQeT(s*EHA`_ zA$>)GW;BtWBeH|IXekC`Sxp-dHW8jqBXmsAYfG1^r6^7{OQ2YiUby59#d5d5_t0sCyaZ2w+rKX5Y)~c{(fjZfX7*+Hvsxf-ge-#{X~ zSkAc%>BRdFgti0u)i#7*n#{Hz)+F@MHvWDpFPl**v07ZZF$yy2HOSm3nAzEKSL!fbisUTjtJ^pqqP(+xV1tI4 zygh^XmR}2vxi?7r%>_EnN{uK>yt@E|#9gn%;#>{DMtn}>G*5ozzi6#+=cfD+R#oU+ z27226Z3Y)6MN+6FH&E)(z-X?9;E=#8&Nnp3x+#R)q=0u4LSmnIx{YiW@c*i7flHgn zqcAjIaPfVlom37P?UJtHZPaSw5NN9gWOnX20*Cv*#CbBI8x%8>I8WU9BL_={?YsQi zX1znG4SVD0}OMRr8h|`D(&0^V^n24Oj=FyPt2Cg{?>c{~2}{ z?wTybuRJs}{l!|Mxn}}Yc^kSrmSiGxPZ&uDSA8<&nD(=NVKWM~rmt79eN}Rl0!Wf$+LCNdMqdm8|W+~Ei?s7X>iVQuu3#UgcApj0_ z^(~&=sNJ}-+DniyW9z}7WO`o!l}G=(Zz6K@1|u{Uv`QIK;FzyNI^|dr0JQ% zl_*ing3}cU0ymKBn;Kd(vFl~?0H6SxYTZor6O|l0pB9I)uyE?}v7YW0*BHwB=F8Wt zYMAiy@={0c(H3@J3nJs~tLJYV;A!p3_KY5Jm6Z+Zcswf?t}qN1-G7+Ll@tety$D*UNxg)j-I`DCMC&P zN_J9dW#b%SkL?=k3{qGw(nPrlMRtU&l&Uzzf+);BZ)QkS&@ikF5XK=n&n4N!VkW4L z{q!7|IeEGB=-t?x6IG?9%EN)^%O|~?$xx#s7+YzJri`fp&z;kLp~lreJ}ei1mB+ju zvfx4CH*IY(X5*DRUucW2i;(`gc--7L(5wa~MMI71($teFmA!2p^*tho0MWl zz1y!=N4_mq$g+?RLddc#%d#xvwtZ;Y2WXlm9PCZAmtmP^V~pDin|5q(4u%D$&8^p3 z*k<=`#anrZq9{*CQ68c^pQk)TQ6A27l>aMZ{A$#^ee+Vcj|c&4eP4}HHLHGO)OF4Y zCSPs*w7o0Uko}LWMfuU?N4Fn5#girdHlBPy7D3#D;~KTH=vbe`h7EgQpB}3spxDCN zgza`1DMuV=XEK%r;_ z!KW~qZa(LZ7VMSt3tP_~k+^%fd;9UugZtlwQ8@4T>8xmYiFQ61Y)2jc@%P8^B1^vr z>emdsL-(c*&bdo}bANM(-KXxBd+4_D(>eV8oBOT%yZaCL=^{Scb+_G)yYHU3J^cIF zJ!(>&M5@F1`*-Iqc%JLv*>U%P+G|n;u1|pe4&O4xv*z0l$IDOf&13f?(%k{Wj&ra1 zlH5dzn2!Jdj=AlBif`{bcWBqr5pf$oxflM?xl^0?jyyX^|2aP0Mafxeru`TxAK?GX z`29zeYBCr%(>Gs*z3sg zvHPT{TX!i2brtDvNymwGm-+F9Q=Kei$zl5Ln+-Fu_f5FeU@!QAv%}2ve$aTm4 z;I{B7>-!N}cGtPLd^@%2eVu*=l(D|O*OYXOjSTZC+_CxZucBO!P@W%9VnoTwZtBla z$1KNPEBiyVG-K%zKDm$2?t8@hbBHf3O{lGT_N!Ja1H{|c7q5Rlg*!I?{qXv`spGDH z4l%fzlXGcDum2F^bptKKI6{mcjf*nG#lcUkt?v0adULN=k6u4blpVTkDq@80F)H`} z8NWXO$EN!$;+@*Yr-%Bn?OxeHJlsK4P=OxnUb$fLbO&u29L~Mm>U-Dy+8uHGUOeKC zx_z(FQ63`wkNE!rG{o4ygIrmz9V!bg^ecsg7%OA^qnokaedLaKp=UyT9 zF8=y!FgJbqj~3nZ2PxZXlia>s?xpKU{}d>9;IftUAxh^Ce2MQL_|MQ( z-&2|HjmyAHxZ}%#^*Z9{2~)q_jU|4<)_>+YIigLsCdw3mV z;3t%K2c_Av+S^5Iu%6wktdr2zp(GB`&D8hsoyFZtq3>ib!d9o(V)%*w+dOZ>t8N+Q zBYf^-rW=FEO_AAUcW-oKr~3`kzs1O=ll!>ySJJY(w|xuy_-||K5>Ss(h9~G<$5Xgh z+TP_la`>6`_iz98x&`SjI_|#xAGTujLx-u_3LxSZnmkm&uTe=yk%FBCo8z!9;V>$j zT`H}YUCm(>l!FbXw(VflTx%Ha|v&lKO|(>;{)19t*{?g4q+ zu8jk!$4K=U_&t2)ujd4Q`w97z_XngukFMVTd>I3M=(p}PFl-0rF{~?g zZoUqDUHPHYaaf>lW`@r$_}ziFdEF~-cLp*ty4&x<@V~melm8|6VI!iV> z#CWZbZaVChYhy>nA(LJCR(@%RWeZa~cc85=-$^p^LjCZnuA$D9|H~YzcBeP)5U=ow zU8A2;p1`#7nA>;Yu6Ykz%)R8>7eAd~`P&}r8jzud+aXCCXYPe_7^r$bb|+XC4yF7g zbP@X=WAq37Wq-v;dx$@`@cW&P@48+pUTa&TPC4G%0QNhS{T^bHYQ{2LC#8GgFg$TJ zO+Qz6x{K*wO&DO28z|dE8B6Hy7539&(W+Bc ze&;=>MPSPm*XD<&AE2N1qlUxoAK+fy2>S!R``YlVw{PvY$DN4rpSj(j`?kmDXo#Ni z1ejIUBly&hrMl_-xbGIeW8XpT`pNrke9|x1akuZthN+07pLQJBPqmBjiaYj4m4kKC zYKQHFon!mgFAT9f?x3#=j$NSsm$=ZyKkmKp`tl1@#MlDWt6tTNoVHfo%l#TdnGTVi zb*_9~z~a`6hu!;_4saC2Wf*!9BO9q-K0Jswmh#tXv!g&BbpH;i_%HvygFja0*N3=} z!kr2~F)dSnft-9a@Ed>4hY^`i5$n`2YG9u`8Q6*Fdcdyb`wvJLTH+7(i8~gU_}z~4 zpN7xE|NQO_zHi@p`7qGbZxg5fg+n(z?I(8F)ZCw{UZ!e}e$bKZG za%a1>;A<_6cyJgW`7*A$y(-i5D%^RtTZnAdG$WZRWS_KWCF7`ub@Bjp!&vLr2bY+5 z?V4M?u{t|y@;VvH>q{8M)m;zgxSAzrpMaV?xY4O?_ac{T-JwBrVkXGCrVskXx+f?n z9pWCY53{$|!a@scfqD5wA@^o~zQmlr#8qo)YCmU(_b|S`fb_oAu*HI({1O}e&K>l< zgP-NP3vG=0>l==F*}r<93@sF9w_z#Wofx(T>w?N`xx1}MS2=-eg8QHOm7M-paNW?~ z&>Oiqz*z3L(+5anebMV|bDv^RuSY*dZTMA#J6KofYtYZw{J1&PaEQ2O$?I*7pjPMR zoi=#QkNtd2bv}RfI`R&+)x^)M-D__?MGLhfF3vm|*^0)uecP|bWK+id9Oq&#`Z_h( zjr1nP8P74iy8al}tA?)9DB*?Qz&|`TEDuuI{ivX;_gBZY%M>!bcZ%k3NFHL+h1M_SJ%3$Hz5+}1E{kz-D?@^uY$w%>o&~q{7S^G z`zLTQQbJ4ix7R#2r1c8r=s%1x8&q#Q@G4Sp37H$9bf9kBuJ(Pn7OaG9qfOBU_~~c( zDprLK`c;ZY_znJs&!6D`?+`u5@jv@-t`CM!Ji7VhK&x=KpFJCJulDt%THNI5&0iN= zqh0&#&jY-tt=6(w-uDqbWFRWc+Z$!tdI0;*FvIh?qZST@^+)eitO1T9pI`91vi_gGtB{W$P5 zf`Kk_j&Tsf!x{=Y;&`OUOqHJMMup?CQ{&_`~&1>|1gclQyOTpyyh zVWjcO79#IEpc%a$!I9iHK09vX?C7iIeGeX3SNr?R2ZJwa4w)(oCZiDi`f3!Y(rX7>sl!n4uS@6+rMg#bE(WBulu2k9L}-2-%YXl(ep63 z?x-U~n!C`w;^A11Bg)`N-F*AKhI{1++PLe^SP{F?Pv6((1*Ct3KU`O$F6lvZOkR)I zdzY>2)P4oN9X+`Z=koC`8X{mNl6r?#*a~46z~kD3JH2(aOTKU5$m70zxqo%MqIOlU zn+~^x;L166K^&i1;^`tgxHtSb=X;EO9w7P|L7Yu|&mIQ{EvRs>CO^BMNqzJJQql*) zYLxGL=nrJ&rY||rBVdhiK4lvz8poPHQPb`qTPSQh^EksJu=@Q?cZfYA*8%kV!7pqi z;mhGpciY7G`K`J_*~reZncv|;ytMUw9~3}nDb>sSi6SOP3cZ{e;eZHe)l!b59OYZ!7if9?5=cw8|Ksf zuI=OVVuou&?0-Y=&Dj?fMNM-w6ILU_IyU;ObKU%hW$Ws1tVB3uJyY|3)^i6X;<(bU zCHZ#mDfHWNjxd)UxxN!~?Cb;j-_J)eBk!JSl&YX}5Ag4CowPqY@GBMEVfY(PJNzB{ z3+`AS)%TL2f4KMOQun>V9rSr?UV0MmO9rrF~Gw)ja_%$f} zjxlwfk=P?&WupOS`IwPhRB}_1CemwvOxI%=q?TJvWR#+Gs^w zLYrf0yEExKw!0P5`8>nURp4}|f2~<6$B-Z3^vT!mpK$G;cx^C$Rw-9EdH?=K@h zZsN1cDC_15FF(J4&p$;fvSjoAsb5~QUIS|ZBjyL(DLIK~_CNj3xySSX-{<;6&OGVE zJ*#%M#B)qsPh_n4vqaq!PE<@gL*NN0;yGGj>i)P9qsAls=;djqu?Tc;b37FGk}e_* zBZspC{>%LUx8KLTJHwI5W6aXMANmnDdkA`@pP5k8{d^IPXXnPk!_>VI>Zy5TcO9~c z*;B*0Mt|fQ()k&~Q@c`!6AE3W^EX=`e~)v2KZ76clO5JcER5(*g!rLGc&dx%#(1{H zuWN7~%oAN42j0WC?pa#;z;?nsv%g+=0{L`zjgBEg*gD(TJ7m1{Q}4qu`tEh2ZE(bL zvWI_}^J040$M|dG>=(JG=w5)vk1$f&kvUIEECsba*@z^7MhxW7WZRo4N za^c=*Iz|)ME&N=~I}+zJT#M|izGGD|Hth!Lh~r1PZhzIUJ9qL?;&-0f$o8iY!irV6 z*}$D0@14UunWqEBQ@D-s&u?n}%Jpa-LZxS!EWbAutH$61^&T`$lD_zVm57sEl$rk6_>2uUE z*9_j?-H@On!2hJL{BA$%xy-mHTv?Ruqj7 zSM;&oc7R&D&vz1*)Ibk3!`v4wHi22K~B#E#)%{7mhCkLyC7*a7vc z$94i`|6z;)@#$L}@r2#Gmg$7{CEc4miS5g_i<0$ERl0ZHz&Mhpnjf0t=*K<}|M^*b zzE5dR<7b|w^ghP5l}?(s{m&mb{`(ykY9DsGg=nCic9=UyQO{Memk$vy?$1wipyZ$)M@6aIkH~ne> z<70SL@REbfov~rN6HVG~^fXMb@EUvx+ISgn-R66>&2+v02eM79dkRutn=J&{RZ<6o|mArhMixI z`t<#Q)}`FbeNRS9U{u8D@qK;hiG4e_tEEVHA|@Tz+_$}ZzyB$s>k>-Fxz?upGyeB; zq{}v|;f(4tD4O#tcdncEg3ZP_3;Pt`UPhWvkdkw^FYy~c|J>$d4f@bvCklH?-JZq` zPFFW2eC11&wLLXrUV_tZVbpEEV-&yJ+2!i@)(`1b+L##_No3e-;4u zLE~qRi0IPW_?DkPLF)(ia)(ZsA|4MkZhd>@{FZllY~WvBQ#jSr)WgYsPFy!x-EsBfAJ!#CPVe*)zK^_% zw!t$&<=gH^KMh-}yCc!G9`5_-=huuxwUQcIu-*IQxQuwVC|6NAKXx1a{Cs_F*$C^2 z^*oG?5I=rQG^n@JDQ%5sbRnj;a4Vxfi__1Y*A&LHyI3>%25TkmNK8tkd+{6hA!bnc zyPr7ptuNZYIQ7rbKs$o%+_?2hI3?2g>|pnHGIxM$O?S~B{YTum$ezHxbkhAE{_)#p zBphnG)k?qrVZHmAQOFH(ikmJuN)D%0+%Nyv(gFTW0JtE?2H_#szAc)q<2U-R;}Ur{@FS%E4i|X}7k?Um&*1M#{JDeQev1G) zg}?495BsuGj>9lxu$S&(#?*Nhtb{HF7L3sp&ae?x2fI1pKXZCfgE`3#tKTEwdC)Ue1f{S z1s5!wg#&41vd?-O$=&N=o?NIJ+=_#)WA1jWu}E`&4N=DbpG%~@cpNe24)t$93Q|02 zqU0@o5ao*}5uxs-rrYg3#h~8>`++ruNZSvlh&q1(JZJl9{c=AiaDLOcy(Nhq7~_`* zVXgB!8jEyC`b#?O8oReiHEsG989D9oF%b@yG~eTvx-p!;zP9^kXKDu^FQ0^-&!E%6 zi!iN3x_W3l7i>C`|*iugx()g+Kd|q4_L*F`E=D#4PM+<3wK)s?@o#o*{OdRA|NV5m; znNP!og{8LOxC43FvUPiyf*X(=ozPD?ZRy@X&gI{3Z1guP>3-dztncZ=(FgW*STMR= z%Fx7FB_>2;6()<>3QUUZ3QUUs3QST-1tw{!0+SS1fl0coz$EonVKR+ZV3HG5V3J=9 zJb;@8oV@Y~JSWBxz25o02XMUC9@5!>hw#>2E{Jkzl)YKl{?dW6_xkw$=&l)q4ScPi zsy|nAxZ$AdYaFL>r0&NlZjfL9+s2O~Qo(B$Q<;w}q>@K0q>}y@Qc2qjsifP5RMO-^ zD(P(@m9(^&%5<`jN*Y*5rTE5sXNR${g;?a4FvqFeh)B-bxM{{d%^zDG8517s^Dr*k zg(DtX;JCKAQTlXB>L%tgP>8`~l*MEsN->&NvSmaQ^O;D*eHPMUKLe@w&p=888A$0M11T+JAf<;4 zq%@I%lrFN6ri~1w^pSy-Mz9gawb{GqHHHt@;Fd-hw_HT8z}|#64yL4U^pS;wIx=vk zjbxmZk&Kfrl5tW+GESOE#z_&$IO!o7CpBc?Obf|4DIpmr9oX8&xDJ}w*HOALt%Nj9 zyho=+j7O%mxQD@dn2k!SIE_lH*o;c6c#KM`7>rD7aTk?Vu@;q9@imin zWo7i#w8sw9Hu08>LEI%`EdG)(io+y~;xP%MxJ<$*K9ewt(xyh**x}n(p+0{rIva69aWLG1p$W|k1$gW1xkkZHo zl`xLv&Y?dQ;cwKN(9GphhDJk`n4qaDOs264OwwEhCTXw&lQda@NgA!dB+XV}l7=fV zNz+xBOyd=pr1=U=@_=*G3SQpna+@XSM*9_5p!;GL(|jR|^j^p!trxOL=Y=fNcp;1Q zUC1JB7qUp##Vn@jLKf+{kVRVla9WLvCv`96V;db8b3ny~9H!wS4k@^ZL;5Y^ka~+a zq}?J8DYuA2x-H_6Y703`vqc(_^t% zqNT-5v{aahmi{u)QeGxnn#)8>ZP{qkSteQv%S20C_L`dUxoxYG!Tlsr{bwK3=qeiv zRb^sLQ(0IkDhn$;WnrbJEUdJYg_V-Bu+mW$Rw~NGnufBlQcxCF`gw;v$P=9XukK@JR7gL&&Ep!*?8$98!w$?7m1QAKUl~X#D+4J_Wgw-d45W0Ffs}$WkkU>DQmV;9 znqD%HQc4C=8ky+J+PJnIaBp zr-(xuD&mlqia4aHA`WS*ki#@q#38K}aY%F5<}}A1wS9PN|LHyxz4Yu$Dbx8HU9W`~ zT3-XN>AyO!JfS+Te4;w9yrVj={G>XsJf=FYe5X3Eyl4%)=1gKHmR@@ zoAgU4OFXeUUE+z==@L(@PM3IM zb-Kh8tJ5W(Se-8O#OZX2CswCRJksg8x&E|0UvUv5BDvum|W=tkoeSfKD?7Snej zi_~4nB5fD4NZExf(sdz=R9(m-O&79A(ZwvL=Ry{#xsXL#J`<fxx%*IQ7*?8&hbe!%6XA~oQ z-ae|)Tmc%2%SM~tGSO07CR$p{L`!LzXz45yEtO@WrLj!36qb!PePyDhu1vJFHM`;z zb&l!soH}C|8qHN=g8r&7nHDQBNtYFvq|pjY(rX1KX}1ECbXU2u#M)Mgcs5=>DI!;7Mv56>Y zGZ7^fCZeRbM3j`3h?0g9QBq4X%5;&4k^&M@VXiqIy&U7V=ldD?57cLFc{c+UT zsULSLK5e6pWDK;Ch%sd(VWf*Bj8u_?ktUKbQbZC)dPu@Z4M`YjArWIrNWw@5Nf@c% z?0AeuJUe3{p%1gY;9tAT<>*NLvLAQd$v%>8^l5 zDlA}-CeMs3tKC0fuQalUcs>u;=&=Y7H5TAai`jT7F&i%(X5*#8Y`ip>jh6zm@zP&5 zUg|5roA$EtQeHM*x;wd~pW(ipo9I1Tr4A8|)-sV$Sr*dtm4TG9GLX_#22yIuKuSj$ zNGT`-DeYt+rJ5|H=_LaxrDPzbkrPWA>F=_+#lEbczR^b(4(iCjnKqJfQbsaPx=6-J z70EbhA{i$|B;%xqWSrEHfio>6A4D*G+v3zbYF!_UQmTg{_x=^@PONR zBF_UjLI0^)7x3A(J*#KJ7cdT;!cHLGpGFKvpd3F=u`l@5(P?DA7kBSm!~NEKSU2Up zU^nghAK{I+_uW1BG5+QCyZ4vVH0~Ci7VZ_9*4!y7t=uOnt=uIlt=uCjt=u6ht<)ct zR_cyQEA>XEHFZX%mHML6N?oV=vD3cCU=#6n5Anra?(bdqp8a85@W~aA99R4KhFLy0 z>$XMgkBrR$|6JN8Mw2m!)kKWNY!XJXn}kseCt(!JNf^a+5=OC|gi(wqVHE3$7>oHN zjAB0tBMrRW*FbmF$eMlT+|i{JP3%UdM8rm=v^b4OspyPIshEsNsYr}SsrZXXsi=!c zsaT6hsR)ZoX>k>iQqdHVGR$K5%)r}tm%%N#=I}k+AK_EZVwh$RZaOjcxKhs1#9=iq z&SL76%gsSW{sZR@%$LN^u)=THv}||D55-iH^&q42`xbF+pinm`rySn54oA zOwwcpCMmQ6lk{4FN$Rb@BrR89lCrBXna(RPN%a+&oYp}q%fx`(!E{!C9P-L@6U z$5@-Zk6HMxb6fFlu*B8)LUm3!L}gC%imIG)jjEjTkE)z&q<`-NpU!v}fBE!N-V?$5A9$a`WA_*Q%-s@r(v7-&jW4Z* z7v8i6Uh}8wyz;2(yz;5)yz;8*yz;B+yz;E-yz;H;yz;I!@S1;B=aq+5=arBBX~}om zCp!02UWb2m_l5Z_cQ@SZTIW@a!5-lp19z7FlWqNe+b(|RKJxtf;u5-Xz1fwXI2L$Z zF^f4`A&dO0kVS4)$RcklWRVjUvdDJ|S>!T>Eb^3M7ITn77WqXXi`?OSU-SL*S3J#e z5h}h5GJK=wDm+kh1s>CMF^^PT%p+YF^GMmnJkoYCkJMevBYhY1 zNZ}QDOyk8oQh70tbbe+@r=LUNkK6|S@+9Un%vg9b%q{xEIIhuk5gvLjz?+V<@zQTL zUb@Z3ORw2@=`#x*YodNnp1BqOQXrF_1K`$ zYHX(0N^DYZB{pff5}TA=iA_4M#3t2OVv`3{Vv{3OV>5rK#3r|>#3t{UT@mQ2KHMqr zDf(`{r*PNZK<~}D`eS>S+h7JhJErjKmV8(F4jBA?3g-YLd# zZ>q*8cdEuGf2zhOhpNUWkE+Hem#W4mpQ^?ur>e+jUR8}xZdHvhW-WpDJ>9iCn|_`; zAAMnNT(X5Z$wSBcMIYEX>-H?Rzo+G)txmexlO$b^$)`5X?r6s_G=5fz2~Jjp$vmtA zliaHUlYFZJlN_r8lf0?|lU%9-ll-XylbopvlX+4FCb>}sCi&3pNi^9~kP3NGX@?G|xJwM87#>+F7F z`-Gqg#m=s4+ zq-iJvDa~XcrIBNwVZVt_HtR?HM4yORYkcZP8yP6*A{k|xNJL2wi706y5hWcYqNIUD zl;S@TrPxnIDejX|7W0WH#d{)3v5r$G);(4_on8EIPo^d z#dxW+8^iPnU)>9%Wgu=m?!5U z8)X+VK;=aYruYH|slR|hPEf!gS14eRLliK`EeaUq90d$=ks=0jlmZ61O96wN25ZFQ z%4*MMM7$3P`?F&#*Wn2~z2?uXO=uhYO}W@c-NhWxcOi!lIHdPN4pV#)hcsWrA=TsFkulAEVWSH^4Rb=P6C;TwT%*$>JTzK>H+^Q~ zrOj--beWBpCbRLVt|%*IQH*?4KN0B`!s#!GwIc7N;J zi+x!?eWQ;o9MqA4Gi@Z}q>N;obdijcDw1*1L^4i_NXAJI$vCMY17})D#z_guIO*UN zpFzYYdGIM{!{?JvjL$wy$25A##zGUBSkpxoR@%tIN*`HRX(S6Pon&F9l`O3El7*FK zGO?zcEUdJXg_VA8%xj^$lFA)1z6Irht*i2G6h7&TPyO1(AHTNR=wvOt(9jxqO<&b{ zrM>FB(q(mCX|_7A^jw`+TCdJ4AE?eNk5~h*`A2nLc}sQPn0}jkE*Ic5x3Q18jkkmG ze>{_8wyz(BRTr<*7@ioNF7w3c zbcrWcr%ODsI$h$4)#(yXtWKABVs*O26RXoDo>-kO^Tg?Ni6>U4OFYu)xw(;UyB~iM zBjb^0vTc^28;w_Bfx?SfOy7kpQgv1UC1Iu7qgh23t6P* zLKbQHOq?2rXKUQ)I9&g%f{S#HKC01t0UBD*Mw`Yn(b9G%TAI#8OUs#PX*d%t?Pj8- z*-W&wnvFJ%W}>CdOmxuX7JAM-Jn8%iR+_Hh=Vx*Eh@H>FHTo*TLtzDY(^xiMD$B-8 zXW4iuEgLVbW#gr`Y`pZAjhEsI@TR$Jyi}Ksm+mes>4$UK`7yG)QqIxnu^Jb&S&7SZ zT7^rRt->Y!R^gJCt8huzRk)<_DqPZg6)tJN5|{Zv6)t%~6)yS1#*(%d=f0Oq85(b> z!~|!k!eqWsfl01Vfk~cFfk}=~fk}Q)fk|#qfk|Fafk{qKg~@!N0+U>z0+T%8!*~U+ z%$XO*7p;E`v5oGFIiUDL4%2!Ohg4p~A$=EdNZCak(sU7r)Lg_N9T#y(!G#>A-69UD zwunP|JsYpr{;HEp=9l}BDlK4;Mhh6E&;kbOvw%VBEMSl}3mBx# zA_miC0fSUoz#vV2wS{%9dpJAQfAY}`dpKDNf8F;!8_TzSZa=$~&_6^=%5*7MT3KQlH>|UmyMr#S`pbH|a*>y^&%J)Y+=k>3Ol z-`w2ZMY92(d$?vk6@+og((pc;C-_-!lfE`yo~uCdeYJez5TAZD9{nSH=f?SXsy?DL z=u^j{*`LWD)89x5Fb1CD2+!iXWr`iaVvTMJ8%}8Zw|zfEy|6qt&~7`ZTkir~#~Q=? zQ{7Kcr}T#5Y3+#?o+!&T$CIEt@C6?ulTVL$Z@eu1(Evx=VSL3lPtVUv4SdGE5uo``3VKwf`#M+1}3#+kK7FMGyxZAWAx^E<# z&=UH;5`3epDm>6w1s>B|F^{xY%p)BZ^GK7$Jkn<|kF;9MBi$DBNW&F)OwYwU(snUV zyCONQ(}fW@#wNMgM&HF8enqmR9AQN=pF^51x)$lVh@)MR%)b`txTqZMie!E{+7-!s z4r#aOS~wT`&Q`ti%h9e#=5t7|v)2;8CNW)(IF3fYm{HZ?f{rV3nVzd~N!L}lr0*(R z(s>mw>AebXKCCX$Fn?c zkKu_Kx5x0rjN4;)V#e(;JTc?;7@nAMdkjy^xIKm^X51dfvpjB(;fWcy$M8t!x4UVF zYgO&q|FAc|jJ41Uwqne6(d$?T`q|bz8I`wf#j>;Z{KNY5X#C>22t|Tp8K5gGGBf!dC9wQpC!3)svId^gw~oVMX< z6F&EnCx60;HGk^oI_}xz`IX6ckZo?+_qPY<8a9BS|2(vJqCLZN3un)hU9G{=_)tYQ zI8ik=^P);@a-&LY@}o*@a->RZ@}x>^a-~Xa@}){_a;9o*=1rB@+fvlD$;tEqnaMju%?s3QYs z+DOJp8Ob>5A{i%DB;%xsWSkU{jFTRcaZ*DD&a{w>lM<3~(!uLnowu%cpIYU%uQ&S~ z+gLFPcYtthbhzWq`?4Nj$K)sXg{`OfGV$%}ciiXr{PFZ&$<>rx{F)8#|C$&%K8$A@ zzh^7yj>V1m+$PFY8x{$-t?_u1@FHjqIY>hGU zjeKWQ5lt_aiR36RCr>}hOGmQhaA#FUo?(=io@W^4r6a>AFC7_1dFjY7%1cLvQC<$x zkMh!yVU(AS+=Hfnz)XG`Nk8@X?($6_cWqyQ?+Ds}4{%3-pSnwB_(nffc%Y^VJf^K; z9x1JuN4hKKkqV1>q{(6)DYTeJdM)OWdMoglmWz3$>|!42{PeU=FQbo~7$uy`K{c8# zKtsvdXwz>dTB^-NORJe^DKrx;U1p-C#!R#{n2DD1veBluOte&%iI%obOsj$ykp*wzI+K>Z(Nh+VD<$dqnTC>aQcp52tdyjdGpv-P;-r=2a)yEP;geq%G!h0`Bj)MRTkv<6lvtU9aduQIFDS(#PZt;{NAS7w#& zE3?WKDznNnDznN_stPHO}nB&N#p{s?lx%8k)^Un^rT?(r6}H+RQ{tlbL8~F%vBfW}>CN zOtdtYjW(@iqNTA+w6ry|kNTyp0DouaGA3u0Gy6O-_(pG4c%ZxrJf^{79;vaIN4hNL zkwS}kq}5^`skWF$`Yq;>k}L3-ri*!`?qZ%;o%8G!PezQ7Y26vG=KGk-4&Qs}&j?Q7 zzMaNZIHz}Vnth@?o70GJ4wCnF@_m9--UQ3(X_o1+g*z348k_~Yuz_9fJA?aCZ`u>> z`NVskXW@GoxF>NN=VJU_06gpVBYyUG#Jcn29DNL1k=J|+xjpOf;~C*+$MM~H6rWVT zhf?g|f1bFz1ExF99reg?BIi23?dRI~e`HEHd{j#F@Q9QwC(e~cq?8Xwq?FS}q?DIN zq?B7mq?8{K0=mLh6q_o z*+$4x$~HolQnnGYl(LPGrIc-iETwECWGQ7EAnPK78{G{6d8-@6c>x>6cvl<6cdZ-6cLN*6c3B(6b%vSEEX2iDH0abDGsiD z1b5)c&O1$?$KK{IaB!aMypEoWZ++oAx_zJcF@ER0aNBy9&=0POm1sS20$R*B0rS%piwt->X3pXm4bpO}KTq1XO7}RW^wLapdg&rMy<$H)z2ZAMy<#{ay~SyCdc|UNdd1sW#vAVq z;JC9Ndson3Ji~LwIG^G344xw0own!5L?Q;WkQR*@NX2CaQW2VgRIFwo6}1^i#cu{u zk(_~4OlKi2x-*c9^9-aS{*;gST|{*E-Wa}+lZ}^tF7@@pmDcW?bo;Bb zJJ1c^$+-&+JwVE5814A`Cf!ylk7wpvCH{DI6Rk;tJAwE!?2W% zniCPwZxX^3n}Co;6A)5m0zx`WKuCEB2x%<=A$27nq^Bf=DJTIU%_JbCl8xy|vOpNUon$r-0We#J*%z?EKpc6i>a)TMM^7Vk=hDbq_{#BsjiSk$}41% z`U+X3z+x6tVIhl@SjZwZPWFsEp}>24!_$D4M_l8Gxe?bGVt&N6idY(Pts<62T&sws z5!WhWX~eaPSQ>GyB9=y6tB9o$*BD}c#I=f88gZ>66p0^RM7-U_9OEg*{Ph3FSaW)a z-?_5N?^fTD)x=~r7SWlBwK&bfDq^#+irp-%qBskyc+SEqva_&?@hq&OJriqjpM{kI zvar&^iM|%LFx%ic2(HS8=atc$oA^&kkC;zLZ*d--Ua=jWUhy2AUNIbjv1MRiX2M`ccPl&YNan5vv| zovNJjp{ks6rmCFss;ZoFud1B#v&x+2a8)_wc~v>bT(Ik3Lq8h24^Nr!T?O0t*IkWu zqB)kvgDSGYeX6mU?^I%w<5Xgk*HmJY%T!{Mzf@wAvs7Y}r&MB-n^a>nAF0G92dTs+ z?>IN7`r9^c+Flv8G-u)_(W__QIQn^FBcH}23efO`Y_xemCR&=$L`&nDXlXhVEe&U) zrP)lhG@6N)CbQ9|!A!I?mx-3fE_XHdF*M~*lyaW%Sm%B?KiS5+qx|kKcY=e){M$o6 zbHBstnmf~FqN3X88E}sB9rWez1s%+A&USHEk%p&G?*o6&w9V%_aL2=s2mIZkEDuHn z@Ar}2dC+w*@IKNz?waYkU%IRKjQiU6?Ii80hwzlEP7e02Di#L^$iSKQlW|ggGERC= z#!2bPIB7f?Cv_*|r0Zmy6rF)HEhpoo;$+-dAL)BGs-@M?r-*f)80S6LLAOnG_q-zw zJ?!OD(5t;%JbJ8`i${<3a`EV~UM?Oz*2~4C$9lPV^jI$!j~?sgQqZftTs(TLmy1VB zB`3Q*1y614nftSk*~NS2c5$+fJ;H<5_Y)CNLlVLikbqFsCmooMxuT#*gejSe<^Xqu@m|w@E z$NV}TJ?7W(=rO;JN00e+JbKKpQ_!n^9giOK>v*(O(p!n-du(rE{=hR&oRx6pFwFjW zp91et;4TZlZJ#J}6T2cD^Y9ESR}tNjZXZC-AMls&$$iv+&)~`7EU4XG@%>KNGigRq zi3s);DS7z5A|4U?ig-lmE8-EMuZTy4z9JqG`igi&=qut8p|40m_`V_@5&DXFgjDeU z5E0hQ`3_sofgd5V^>)my__POXE(bq;9m6fy&9tG?bW^bd-pamJ(6YQzA;5N<>Lli706+5hZ;kqfBFoDCsN_ zC9Rzb(d(ZhtpvnebNXHqEYW>P4|W>P4kW>P4AW>P3x zW>P3NW>P2;W>Z+Z&!kW*&!kYBzA@EuxNH5L>F=6$T}*?PT}WeXJDWzWJDW!BJDWx= zJex*sJex+XJex-CJex)>y^zM*dNz$(dp3>Q`}7|%UV4D_*#22dw)76pa`|}-cTeu( z=cbi2P#h(t=H*98X{aztN<)QFQW`3ZlG0FNl$3@Fqog!c7$v2l!YC;f*TTg7<6=*-!1vNvGUw3Eg;31s1qW zF^l<1A&Z=(kVW26$Rf8WWRX7yqQEK+?ji|M?OManK@>5glr)Yukh{^922b15c^(Vhqn@@hPmY)1x?K}CsT6N}oYs<;+)q<1XtKH7@ z+o$^`&M$2*jZgXRo{bH6zUAQ1o*DUBJ167R{>eDSMKVtDl8jRvCF2xd$vDMbGEVWB zfwMSG#wmW2af<7A`tjL4W8QerVj9HjLK=(B*))p7*))o|*))o;*))o!*))oq*))og z*))oWg*4XovuV`MvuV_($M_ye&gS}S=QnZY-=8+-34m?)1;#HAaE>4)brTaAC`3sz z%Hk#wrASIdDYg<(inc_Q;x7@Uh)hH&MiWtr+GLc)aUx2QorqGbpPh?dYQV2vY+;=K z1b=ukjA#Du;J){9B-8h20T|bxici}pBpCyJBw|b*Nf>D(2_t1BVWf*Bj8u_?ktUKb zQbZC)dPu~W8j>*5LJ~$wc%3o&BVs$8B5NXX;v2-<=reZNhJEP;^qt!n`SU#79Xw;>DbB=& zX9nEGZ|GUkivAwEd3^VtBexNUxsMfxdv?F|CFIYZo~Hx9$ItHDJcdgs@nhT{$a8^1 z|Ld?{&SU>0%D89eb(?a|UTL>Zc<;zXTv>RUJ4?5RYjs!W%F-X#wkf%LxC_Q`$8dNX zjJwjNS#9n1yCFW5CiF9G4L4g$E!VEqG<&4`R__n&9cL;Hy_Ey%Wbmp@}z9MoGTkIf6K`I%0VaneUJPP$0ONe{_5=^z=W_)o?u?vrtf_Y9oHc`{D%os3gl zpXfggjD3oKF7adhlBrc?Apq_YTGOs6PXOs7aX#;2G)!Hnyk&8j%lyou*Xb$9;T6z;wD=P(nzxpHF4 zow|m5=NN~#@ppBG-Q9I*=A$2`W11Mv#qdOPzq)~5 zhy55wxqDcv_D}BQz1&UI7oZXO*=URYOtch`iIxg7(NaPtT58BdOA(o9sUj0CWn`mG z9hqn;Boi%_oanEV_jh&L=T(LE5O*dmeWRx=9F&xSGYuudv42V8BtoH4i!7TJw)nx-154$aLeP?!YyxG3%5LNE!^_5wQ$SB*2HbzwH9u9)>^pb zRk!BGZ?S&Dy;`1n9d)cV=K$YYYYygKYs^6&w#FRfWNXYpezwLOy-QdLelQB_WPPgPF2O=V8=m#Um{l&YNakaP1M!Z()kNdy=1cNa6e z9r%Y|!+#7vahoOR#&asLz-5YA%vTCoWf)S z=Y=d%b|H(jJiB+tr}uj;^K{??JGr4Zik**B@etSOw+IjA7T`^@*?6fn8!w$^vn32cVoJUuTXW|2V$dB_}F<0Wfgg{M?!HE*fRDvznmDzB-`D$l9RD(|VxDi5m6Dle+c zDo?7;YTi_tRUTEDRbF*rZiLeRt~(>=Q-2@gw8Rda%U$WG>z^s_e>cU^_);}4xKSl8 z^PnnRa-J$&@|!ALa+xYz@|G%Ga+E4u@{uZBa*s+}<{4GElQ7b25=NR# z!bo>X7-=gBBmE>{q>)67=^zQCSWm(zo{#in^deRW*>e7z-lqE*Yw3Jqp*sU_Ob zO8@+$J3aMH`*fxII2Ez29)@pwx{db>^q*?@PtYFEJq7*V9Di2Kb|1Cs5q_S-yEH`8Yi1(hGg(OU zmkgwQB?Box$w10SGLZ6*45WM`11Z1AK*}ewkme5=NclnrQhqRj>?e7ZB1$SqL@EB0Q5N}$D8+mtO3{A7M=zs#2XmKg%^SF*#5aQQ zo_x+}xKhEjire_?IzHw1O%G7afygf8usAQ`P}CQ3NCQP2QbZAl^ijkiwG?qkJ4GB) zQXz-ws)$1>E8>vmE)6vo6xCm^{v+1!*rWKFe5=StcU5?xy$U?0zhWL~u$V_WEas6G zi+QBSVjgL-m`A!S=8-ll@R&Y}d8E-|9_jS9*J*d1zFjfn+sGc_51#;W5h~@A3U@F< z2d0Prcc3?q+@-{rQ+b0Uvl9k60@|ykm|0<|%9Am)ER~UmmnJetFZ{ z_~lt^+$a+*ugOHqNixy$iA=OyAQLT(XQHLxY_#b!6D{>+qNS~G=Ty>t4*&2T z?28zs>|m6_**s_V{>giTz0d7+vNu|N4!J7-15G)aLe)5!Y!X$3%A^DE!^_7wQ$SH*2Hc8wH9u<)>^pbRa(Oy zpOf{^jSXjJjVBdzz|ri(bF-9ir2Xc33B zSi})CSL==l#?IP=r<)Atag7>hXM$zy(COOPO~Gqom$ui$F4eDzU4F18b~(kG*ySN> zVwbzDiCw<4HgwVIr#$ny<|!wAu6fE|pKG3S-RGL8y!g51DTjW}d75uO*F5Fs&oxhZ z{Ee=Ur+fS-y2q9$VY#Ca{CxhTKsV3vzGI5F=_h7)GE4cIqonAf?onF2WonAVRPA}a?r7|o|^cMHg=@rM(=@pmf zx3H_U=QeO=|Gv9|pPvox{q26Ur8CE!H}4hU5sw9Ui_dJl;x!wu_|3*Ep0n|a?`*u{ zJsYq1&&Ep+1$fg(HePzk#!Elfmh^KGnz{?!JjJQrzRq^th@PtQL0=X5OmEfrq`zu> z(qlC~>9ZQ2^jeKi`mM$%Jy+wCzAN&X-mCFR|JC^92V33`_UxqpJ)G9KU}xzV?T?`U z`)~rjFZ`za0{{AvVCw?oY2Cfy&Q5o}I-G+SJpJRX?lV5xXPEHrKR#E)UFv@C*Ja!F zU4I8_xSy5JkoBc(?|f~>tW|j6Z54RT(~5cIWyL)5uwov0S22$~tC&Y#Rm>xgD&~YUR^gI%t8hubRk)<# zDqPZW6)tJH3YYX;iOV!yg-g1w!X<5=p3^o@?(=-;cNk;zo;;L+f?ktRrp!c?G?<8z z+7eOHRU%3XN<>L3i72Te5vBM~Mp>jMq7>7KC`Iq7x#-=*D03uslQD?gM2y945=K#* zgi*{UVHB}R7{zN6M$wvtQLH9m6sd_Ai_;{GqBIGk7{!WE`@ECkEQfoRKiVAbK4wJS zl+Em>2#?q;z*`(=;}z4{c*S=%Ua_8ySKMdgrGad`^pK60HVW{jlWe>+lZ}^t&UIBW z)K6F+n4E7EprM^?v?(VOE!||IrJ78%G?R&zVlvUvOD0-s$wW&l*=SQrCR#elL`x-q znpeqVcNM+N9`=mCZ&nGry;6HOeADk#W`vrmGMd6FGD>w78KuOEj8bPsMk%%;qf}gx zQOd5!D79B*GzX~2C|9V+D5p5H9p({?gW3QopJr^z^}F&QV#CF7*5 zWSsPqjFU<-aHfT1oFYCMr?{S7?dJIKo~>s2CuearYa-68@F4Ch@R$yYd8CVC9_gf* zN4hEIk&cRaq^n{c>8zMXx~srrIxOaqE{l1j)2+Vl`p*FGezyUQGIpud?z_S6+S!)} zI9b;8P1QM}-^!e(<*J<0byZGjyeg;kUX@eYugWPOsLCl%sLCmSsLW|zQI%7^QI%63 z@>SO-hHFCI?}j^Ai%zm8X86fkn9Wtzz$|ZB1G5}v4b1YHH89I<*1#;!Sp&12XAR8q zpS3WX3$1}!UbF^gIns@;>&!V)e^+wRkJiEqH(CR)c~NyCTxz8GS&3mfz%6Y2uruq(7KptWz+P^iXp6{garua@gZ<_DK@}~MuEN`ms z#PX*4PAqS#@5J(^`c5ows_(?|rut4iZ<_DK@}~MuEU$cLBX+z)-qDl5g$(eLA_jAf z0tWd*0fU^NfI*rsV34{C7^LF@1}V0HLE0>0FclUsNN)uUQr6iyW$~TA_f1cH3-B#_ zM_jWCnu&yxvXG{r45XBkfs|r0kWxwpQVPjHN*NhQDIxZ(-(N&!W}!|wLQ99)~KZd3sh6gV(KYmk%|ggq^3d^sj84g>MCTB$_iPewn7%E zu9(HtSI8n27P7RXG`=J8_TcFz{%%N~wB)|^r`W09vbUGsZuhFuPv#)~C@mcsMroB>1s@5<@O;L7R9;L7R9 z;L7R9;L7R9;L7R9;L7R9;L15j@5<@O;L7PpMf{~9l0MAuZoz~3d?>!*hdV#Khx-}s z0^!ryZfUM?tBwCEJWxXg9@9oKkCal(Bi$79NJYgw(o`{z6jsb5y%qCFeHD02i^V)r zW-*U+dS<9oj+^)tq>t<=DSVT%e=FA${Jz)fG7ASCX5dVB$vEjO87EyO^9T+$ZA{=gBz5_0g%g4)cO$E)ba#u^W}rqBSC=;xi(pA~GVSVlX15 zqAnt(;w&PiA}b=LVks)6MNdRZ#Y;rWr3e|mm&2W$ilt%7CZ-Y*h^-`q#aIGDv6g^P z%q1WcdkF}|U;;w1n1E1BCLk1>NeGM41cYKW0il>ZG98JWH~s~qXpXMkiMVu4ye6ej zIPO4-aG3TXYhEP3fFLNU8B^vF*Tp?r4%_eh34JD)?rxe(Xun#e3anPr zPOYfIop5uP}b~~4?&I$ci<}@u=<&>_ga!TV>Ii>fioYH<(PWeDpPI*FAPWeM+ zPV=e+C{C3H7j2QX!t@l+8iJgEv;vwrRq$y^qYy6QZv!g zWF}hb%S20O*=SQ#CR*CbL`x-D3!54d57)TDxtadTvAbN#&}gm_6ZBVw$+TF3NxH1S zB#l;Jl3pt?NxK!8q~i)q(sTtT>AMP(X}toIbYFo<9&jyQ!QCCCwig|Yw5#$#%@z4f z)7AK->}q_{cQrn#yc(ahUX4$Ruf`|cSL2iVEAp8KRO6EqRO6E$T$uBNcC;||B%`J; zFXn*m3pq^dMI6$15r;Hg#33CQaY(yG9MWqMhcsHmAzc=7m==pTq`xAL<=JYh#^DNA zqqdnjz!G|XwmOeqnyt>EmuIVU=;hh!9C~@SI)`4Kt`wk6xOs z&Y_oQt8-|n>KJMNA?}@so&B zG$o=GTZt$|S|UnumxxjnCZZIR$ta7^M3mw+5vAz8+D31Gt<67s=8?OLy{@oBeG!q( z=;j@5{P#A#W4kwASCI|TUX9IEP>D_IsKh4KRAQ5wDzQmrmDr@dN^DYPB{r$G8k?!O z5}VXriA}0Mc>{Y;JIy}!P3%c+L+Q8Be?7o%c6bKJm-vMD8*JnG2dRk06Ecy|e-_fT zo`ICEGmz4722y&>KuVh#Na-*GDa~adrLQccX(>lhL9 zq!@b43;Eba_r)Agd?ANvy@*39FXE8Ci#Vk0A`WS~h(l^F;*gGuIHcf04%2QChg4g{ zG2LfB#fbzrcq}NYjfZmE2Ol<`W(pmw7lvcnXofR-h zWd#hZ2lLM z=lJ|@896ooTSiJT8P_>A|69&e3dzWGYW}yJ=hXaf87U=Xl$Lj5eK)cau$<@A{BIel zh~FHJ-!5alQdc31lvd1Qsw-rX0t;EB#`8lp@^m2Y!J!WKa6cW_Fxm5j=fYBG>KVEpcH}CKH7{B}Xu>54-{eW}#xs2DazUod7(d<9{4)a6g zzh^75PmnA362p_mSM!WPHD0t*;&?PXC>w3wlZlq+WTNFYnP_=TCR*N-iI%5iqU9x- zXn9CB+PotZEzihA%PUq^ZTNOFJ`LBOY3iTj*oC%l;Tx{m+`+THhPvFs=MRyBcL35$ zzQpf4@CiME=}cPsMvGZEs4xR(`b)-1dC53wE*U4aCF7*CWSkV1jFYyKaZ*(V&h(Uw zlai8gQ~m9{4p#O)E~V*Wh;MXKg$KH+z+*Zp=8>+7d8D&q9_g-_M>;I#kuHmQq|;&^ z>9zuo>A09jx-RCC&R6E-^E&VBZ>+2-#UdMR7cxNAMGU6r0tP9$fI%8AV32wX7^K?* z1}V0HL0T@R6<%?_KmLsl-+q`fs+;YLS zaLfNz){V!D&nthxt|ctdpHB|y*rL|*k^Xmm8S!ayUTcL{Bbl) z|9*z26ko$h#Lw`ZJM%Gq{v7X0@ZYauE(3KP-+qp_2w%WYm$A;Y>D+7nl*ApZDDxd? zpQ8K^ab51KE%1GebT^RxbENzRIq_TsPu1_@?_H$+2H*0m#U1?q6Fxm`d3*!@?(>uf zCn;VXVoti>I`{VKwQqvCJ>}(YY`L${8oY+9$hn_~UmIr&hP88+uK(#bxE_E0quZwL z5KoQpRLCuBRd+A$%0ERpuH%Y7MxOl>V~1_+e$P&reS;dgh!*+=EBU|2zaQh%2l(@i zbMHR^j;($Rak34}W2?2>_}RC~k0>qOX~(%&-nCZgzU$e2xY{1a(H`44M|&J`@({UP zx3clH+g-5lSOjmOHQnVuqYgj8_l$;5k++YYtN4wPaSfl{#Ls-swBK!D*>>^WGh3~^ ziZXkBxJxliA0ho6@a^Hx4j8`!8&BPJ?mNB_!^0Citb?&DW1I5q&MiEldlR`Z5>Rtn zF}#;?rQs~#YAM6Mi$7zC{^?pOCtHed?)K-BhHvq9e=6e(n&lHo7*SAfG9!ES@V=Et zBP|uz&OAAN+v?q)XWYa4kiDuxH#B*?1ceRv(dgu91-T@!mmTh^4 zZMkE+ar8_^Uu?~{e0);$W2liQ&z_;?`+VpDzKXftR*|dmJ9XzqoC|R8(d(3M=(M;J8WA%Ip`@;+=1IZ9?=}y{#IU2AEAElSzY+2sNP0CJk`h%37=F%FY>*PyPcQA zYE1c^t-Y~Y|28iV_WyS*N_aMU@(MERsO=*b>nJL^SNqz2P175Lr@71s>N7HGsDOv~ zpZBD!DjyM)*}khe@jL=sci4v`k1F-ky^=bm?lP{H+imAXWxrnJ>Ah-eR?u$2hkz>+^SoLRlx$q?Q}GvkbzwQ>zk#dqLrAm$i#{@Gvr)W(*Xe%qbjw6(*P z@ZFw^;cR}?h-bAmw@@dH3eHqQ*`Hy~($>6tm>1j2%xsZQtcTsk{E1IfdyMCrmFLzO z_g;u$>yP8Zs5`{DJD<++XT<8B)go3iUOeIc;M@!U4nO^?`$y*v{EXh`f%R|~=ZheC``vxud8~1B>-u(kOJ=-7f=}+!`cNCxd z@4vxMK8otFO=iCwe zbP_)uar+J&wtBdO^$@rJ1lr&PYJ;mnbf?fd?qz><9fiDk0x3fqw9&wFA#V2nY_aDK zF_r(>KL7i5d#~PMl<#La6i0{t9=^jPT!&TO!>DU|4b#v)51U5)`1`Qc?qPh&_x17r z;n17mOYg21C;#)(sMP@-(j?i`gsVp{#x2`{Qel9 zpTJy~Yct2NZq%>8V^HC+T>IYQ6VrV=vsC?^Mf5O7--TAX#j6uvaF(T*+HTd^FTvsTCFXMT; znxiO7`29Wn^mn)KtgoMU@mnw?c0l(!_3|~o`w1cN4VwI0Gcg*I6Y`yOppbuP(MG~GfeqKHo1F|?Z#m3x0O2-KJi!SUbMzP(1wG1t^Jt~x@DN#BIKKGXT&93 ziiKSr?eJ%afD1HXDU@==;@j(gMp>EP_cm1XH@Mn@!P~1`TmbH*c;O-p2TPUve~Y@q z0`ZIX?~RLKrj1ZQK9|GznO#05&63iJ4&!%km|WOB4CWpDOGA2UqUC2?il7l}gWtWv ztLXE(U;Zn5)73kL=?{J#^aId%H$0A?{H{RjBL4&|*Yi0R=Bg)y|GxVd`|U6;mE(D8 zmWapr*N+Zc{9_dLFGx*yJ7Ven30z#8Jq$k12>36Z$gg{HhR^(tyM4d>FB`a8b{gSB zW=?9h)wub!m&olS}=)1aE>|VA(~BhVOlM*uTW^UH1}D%jjBXocL%_ad+*k>vI1HTcvKZHFX5Zy;K*1X0Uo`gDo8a@y-EAJB1^7Q} zhrzXj)C^PB_psw+2>R}z8`N%#3w~@fcI~0#?AJWC){Pe*%>8_OoL|kAx674W+8cAs z-yxz{6YPEyYhiP|&inw70}Hkz#wO7{J zIQL=fjM<9NNcNi(gs&%!HVYbBz zXNN(Y@21&pwZY&v{A!jTwsk8cR0O-umVdRIa+{yy%J1SYuhh@Cy%lr(^*}=eRxGV0 zbYgy}7zQM$%*(@Z8f(dqs8~~M4&QYzGF^R!;NYJln8h8zZ0@Rl;*jhJW^qT{AMxi1 zYT*c^@lZc;EO;C{@!daH+`fa|_Hze0%jf;KP`>^#v5gN ztj8u>?cIV>SRpI~v**8UX*aCHY2#F<9JS|{d~hu{u$a+SH=E0 zh{P|^wCZYg+bhS{%+*&XXBwR8@;BDPaD=kCGL~YP^yd;i?sNV)1cN#Z=Q%22cyckr z3z@T%5X?RF3phG*bG%NNo+X^S-ls^zr3b$R&6XS1BiihzasJh`d38?Fg;{0q+J5;T zHlSl)?Hqu!f!n6yw61Ngc|RH}YOEun0&ZMJHFdp+bFJ>vM7d(bW@eCtyZO9%5393( zf&Y0(JXX-|jB8j4s|5+GFzne^W6_%o4U8M8oMdv&+bz_-`{S)08%^yznz1)mKKF2V z_5Xw>W;Jgi4ryR~Al4X1R%P#JNA(l7;PS7}`*(&};btE_h<|P| z7J1+32cYkQX*hl;&ENgLTfdWRTi*wjx2RyP;5`wpB$Oj`h+J0Z`MI!z>z85OCC+0X zLE^NB`?kHn60nuIE%FRM`x)9p`;-Hn`$);q{uw`qeT01%zBF#XyzD;>IfphGnh$yW zP@Ds6l>PSSU}e1evzDs^Gk>O|jX_RwIR7LQt(teJ(AxWJ0KI|9i)4e$c{&#IUy#r0*wkQTgFsgYJ@c zyIsIqp1%s_%sT8Z=lko}cWatAzTCMva2UZ_X_uea0{$GopWIV3j74^@TuItS+cL7+ zo&GSS;dg#ZA-;XPhCOKCU;bxW+Bhl0m|`0XnJG#?!eiL`=Hkj6gPS`ttAXdMo()dX zA>6*5DouPNO_dDn@fS-qw(&Yexpc98mX(XOx$aoi`;@_RwypB~$uMrEvND;%pITwH zaV>1@Ky`{$!VU))~~>ZtauAs@%UejsgQ$^-5AZE9(^P0^0y;{A^iA|pZZ-KcZ%a)wus-hqE&YzAS}5AR*Rzd zJf@qDKX+NsmhZ9d+Pl)ayFl%{!QnJuJEI8kO$+i%^*(k(g!7u(0}Y&ig*(CgXx#Uu z{^zc$!n~kA9!44OZy=wr1RKWb1*UfA;XVU4&~#)$p-H%*jU>J939Jsx0%4U77pu`g1&% zh~;NYGPOTd<;S(0fpH&`J9aGF1LWA9gLwz%oP+oO(KG$_G_~S)`-T{l?j`2I^7AQD zoXzoMyx$JGZ1==*F6d8cd5(X;F5e%J^Xf3Af1b=QLi!r!RyNxCcI_NE7wq06ggkE6 z&Vv@{TV#*dP`#GgIAp6MhhP57b&U(VW5OC{&0bVL3rEH{!n)}3=lf6agpRE*8O;kF~f+bMi5B)+;`EP&oA^touX?` zxwRuzZz>xJ!-Nc#bIo(K6TKlzJG~!c@CF|8Vz?~vwX3LI1NLQP3s96HO*nuY zEE#q5?7B&mHOd8pwv}48?y#%fiSUagZThgf-&z54^TPyxtEul|{J~4)jHNc3gU|W8 zmZRLp()G6W7fR@_wVC{UINt4d+acIZII7?c3AO!t-wMA(33ak$;w%oYhV|yJzMBU> zT`cRtzcp=x(~aj@^1ew0qx;lMlRGRt`0hLUwj=g6;ZD#Bx?Ly7T3l3E?VM~V@KQJ| z;SPkKj-fkff9COteJ}TaM~YX6pK!*v@3pZ{uj2$L4_vtY{1-QB9Ps~h=fWLqf95S5 z-QT!?R&W=4?trI)jdNK`8%C~jFJTz3&=e{O7pTFtT zck7|^?ZFFR3%eF>leIBCc^e$kYux)+tv)EyMrycgTy*Tpvc3f?J zwzdaov2AVi(=r8@sE+VTP;&N59JG$*&#}SWP?kL#hqr;F$9=iwZp#8V>%y!+p15Gjy91;c<|m!vN)RDff~eDxgz%eK>2Qj5*VFFEix(7R2pd8s8nW zAxm}jDn1+D|MfABI{5k96NDy5?F@qcaI%{wT#Y+)897oW-l*LhUi9rJ9L{ibj*?%i zk#ieK?j((6+vY$V;QHtKv^jHNch9aR-1y+$pGWYzo3y9r)A|l$XoGGm_Zr)BhZ<|z zb-%w7_Xf?|-@C(sKkcn)hgW>Pf84ZpTz&H@hVw_t{uA)<+Wfml*%hEeJHS~Y?f0*E z?!xkQPH}hR=ql>_iTh7D2ICI$dVfTDXzcE-kIQ9F6uTyGw( zD*qqhHC*@Xl)tKm`ut^1IMn9`YTut2v9_%mjV(8ybC|FD)o8qko?1+IX--zIlAqdxlLZvt##+(Ui=0Q%YPr)zv$ob^IiKFZ)V5OxbfiNH|Udoi$DL- zoz`!^!*9RC@BTag+gVG`8*jgHzrtYbK{RwG1(QBr?5e&Ov3ujzL7+yeKoaVH*UE<}~$WMX!Boi(#a4gs!=G5r>tL_wZ zIKS{EZ#n(4a-WsE2CKZ7jU4Nj=K@A%thwFsa{MW+9DYpe&rD6^|6v&EhPGOrKXDeg zI+{hBzBxAU3w$n$d5i(fHb)B!3+|Wy^(rba{2GSutM_RS_XFBNd%$q3l+%yyG0E%Uu~SX}8%p9i%kwBlOrP;cyna2R}9C_; zi))N6#tjB+0iVb0mkJmOv@05u4IKV@)l8K6bLYu#5Oc+fEz*5_CnIciJucjGNi$x> zY=EO*_mJXi-DzcK=+*nYGu`~<{~jlA{kSWJHkc?2zc|SSv~Z_bUlJjFR~I7v%>&)k zei4%4M5+0WI+_Tp)9+cc9L=KPFaJGN?c>NFx>=3Hm#Er{<)^2@%B4R=*ewLtEVzyk?&@U`DA8a=e$&$vEN97};B6H#7l6L$m;d}F zs*cl!(8aH2&1FJf(a;6DHE|mkG2DZtdUtF|g1fYBq;_j*tOfQxV+?i=2{4uKrVnE= zqhDS9Jz+ZTuth@o+d!n^c*XvhEEi4~53G*=cP{Hq0N5GuR-_umXhUQV)ib9EuAtJ4 z{Eepl3S<}4jS*hP|1ozNb&DP+)Unv{NnXN&7kz>8^==>7TX=l)DN4yvTz5OA83Mm`2QBCKpg56U8OgkS z+v2HbXN^lAWE+&@@fczs|~{a;hLHGO0^fSK}&fT~h_ckkp0g>1pa%*3pq=StIMn zPu6*`EbHjVvMkFwvix-Z06+P`I)8!x4Szqe_S$jTd;cy;bx&J5LO|x;5i3@#Soc^F zn~i;{yBLC93G5s86QmyN3I4w+NZc2+6GC8W(@1O^OH2i#Lv3|~v+%f|Xfr7zx?yJ4tky@1gj&m3V zcx(wE@f5p3ATyf~HDRi$9W%|76HH?(}S#oYhwrwfklT=6VcyG1JyOrX- zA-LAoV~ZMcDrz=aroxDTm8CUrx|Fjd51QeN@|YQWqsi1c2NTD@cKJKw-##js96S4< zA<=hWllqIlf7hq82x?(p)*-bv>j3K;CNTQu7(5NPnojKbqHi_!*}OLrUazz1r?8y2 z8)&0@?sTkb`b=>g`L-!uU&EV*d<(5Rclv{=o9 z-xpWPg2^7_UfDSK<&`mL_OTeaTOjfpb2*Kf z!^u@+_^h9|YkE&!$MLg@^ndvuYZ*mjnvO9t@OynplF6pqmE>jirP?5X2}R#qbgIE| z5l$Wx-UzHt6(~%VE`oMVY$QyTOjfjkC6g%BQ@bZ+#44OtN~L?dW|Dlg!k_!p{zJKH z<Zi(%6TGudvdyA z-94&rHcQf5YW-Y4{BEMYLSuOdMzGPZ>(MAItd)`{S2MMSIHhll$e5ezZsrhSaSX6g zRwrcP-`v0W-*)vFe+}x|kY;jFZk~F0ptnXKOkH_!-U*ioWx66q%vP|!Uq8C6a+)@= zuHlBC0wL{ROrz@z!V<*&v^0T#axFo~m&=VQJ^K`9>V?c;@OXFNK=JNDKEj}((wG}W7 zx}|!~18K&_0~Q6?kt16(d)_|>xDbxBgF(43TFxLG`hH|%Z8`of%@W40t(MnNvda1H zOuoyLQeyB!X`*dy-08|qHgWR*Gm@xPIQPgn%MSjkus&15XBrPQ_(f4b6?vQ*|d_(@_c^jr8ZVN*Sq>yZjlokj$xsY{uJYr+HJMPaoR_;UYPmHt1x z#jXR2A)Ti$6WyM&nA2gL?(TRwt6a%;@kbzBSpbU=&67%^#4!rB( zKQ9yd?QUG{KW`Ii964luSn0&c$@KK^anTx1?3z4e6Sw|Zblpwy=L_0@1`>0+)mE`$ z{F31<4dZud78BFuMsXzEE$#8=Z4z_r!^{7>ltk>^v@u-*mG}yV2aA`E))lXEWs8b%}5ORexmX99Beg+O2{XZlcRn9)7y_ z%GFt0?6s3q76q2h%ew24r-0l=GS!*XBZjgyvJbW-8Qs0SlmYMhp1Gj~2X_>3-Pl(G zce8MQ&(K_S$&kruT(LUCpKxg%lDBe7__{a^W^wl92Ixlpd`_25>U*uQlF z!;`WL7vG5UW;K2*d|>ZB<>8Fk^!irkAN!aim*+Z?%Vo}_t3NkMw9l?tB5E$}`8Fro zhQ-=#0uFFW`UDrqiC1cxJj(UB*_h0y>Cz8LPRIO1iBT?}lD`kWtK@MGH>Qf?g8;=>61QmjOb{C#5l$><3^z^3zmE6 zLwUzK7izbeu#n*BSr6ByZj^nu-k25>274Q}#tL}*I}wMp!JRas&7W;pcHsEH1&qhP zh7Z5pkKQytCZ~+yeaPAX@+5DcYu_^Z9$q~f#XE=w*W^(Ri>tAAY zh%KsXB*w7}Tnf&&CuH(UjpC5p-|xrT1OIxVOtY~Zc?FDVzmy)ht@kN;l60XPFEl6H zxm;8Gy8ch5XfmXf?%0YI2N>%IgABD5SFvPx!`}6?6_v#M93OxSZ*viK@iOznhfR{m zjb~)|$aH(!v69Z1S_{=wHVo{GV98S@74!RXh@=J2&ETY?{5AmF`WEf?O$GCZ@L}T0 zy#=&FT^~DM?#FvRo(#v+!${-5?NkeubP|h>f0Vp5yc2VD+4IE^Iiq=zNw?aUG0JSJ zDS;GTqaZ~7&t_$tC|0U=Q#>pPAMM8X71*tGwct)dHQ9|ea@I2Ln#HxNO zAf$E8oEW|LUXxkbW$ufeY&)b+OrZ;kAzkn1t2KS(ZURbm@!V&dg66(&t-q7bwy@Kj z9GoMl;`@f~+@!d@Pk~Di?qFrpb}<9_J)<2%`DCD8sa!f-;*GYF?COVRgP!#=d3# zMHxijikLLkz3*ZyW|iL?mXEG6u3{$mh;?`4$}Kzf&izqMIGa{s9Bt>8b2T`yULRf% z4m&bmoU?h_m?3j*`}_^Kryp-~rV}?wTDA;!zvnU&qz}3B&o(u!khfHr$V$EWXa40B zd8Z-+_w~%vjiGbKn~rT* zv|CYKSYTg88*I zvi~W=5Hk#)bmJ91H$y6gO`ym_g&g^L}mAHMG1GC;IvjwVr& z-9>}}ONU-Ztqx6*8mC8YXl}$b?)W&bk2-E)SgzZqgS6f&T=xqOXaA}uSG}E;dE?LA zYmZd0-|sTwYI*5VOe`*U`J&TCHx-j=;V{+5y6fPk?f|n@O$WKcp}*BCL&b35AGOKE zzxt&8YyYUf%laG}4s;zsl`wE8(kNY*2YyW&M3zJjYhCGH8vMM{+oIEy{wT-F`w@0@ zKFy^&;c+Y|lnaZ5FZ9P$^Ep3rf5WlT&9eLJgYK8cO@6E%;hd$v%te7#Y@U`@wCQ!K zujN*Pv}~0$mJfZ>4Ito~C!&4*RSrH=92R(M0vfXlRFsDKlA_@d?TjXtXW{qztl}a8h=2Vk!|m_7{5TSi_)!#G&Au}THD~&` zxlgtii=R7O`G@X^+gU=@g86C2ETy53k9(!!*dq8LJHA!*Ojy1I8tXa|od^wPr*0pzx zy;qsRKF7SWCEW5X=n#b*zNG=;&Zcxd`9_;vIFi#a#2f6CbEs%hA{UsD83$ig1j z%)IpkuiU99Gvx$Io(IasYqfovi+MLAl=4ty6DWf;Cjv*7+8M+qU3G!6_p^fijc5^KJmEFJwzQt!5{DdF<#EBj-y1HY_Kzv#Oe!FbVuC#`kw~QXA3W3Qn%{A^5NH%3;bsE41|C2K*(n5NTr&p7L*N_C@D- zzgXCKZNtld-$(l)z=^J}whpjJzB^!tX$c3M#V4;xyeP!hX#WI7V;aIx>Ho1Q`Jz8=?Wc08SqjOQVz(=m-C3VwTdw;_4% z$$C?AK;CZqYy?U^$n2 zniH&NtASA_qGyFDp5`-`$&<2?nUkWFPkoL1M#sy@Po23sF!6LM8I?VfcG~)Rt#-8a zQOC^GJ{K0fa7XpkYPhJZEnvNvsohv(feZgqbd*e@uRSwojm$GDa%6wF+4M?YptW-M z*fJuiH&pJbw_0t{YTRkAmLdzdSE<}e*^>wT!2(idA1TVY(k_81gO^n5=zZX7)U!vq zTK8jci4_BoD=@hVFaKxL`CaxdQyzID!`)3QV(HuTavh$8kVF*7<&F`0)*XJ}x(Hgocl|1aa0e0GqpzUr0Ftxyht!k4%H1F{Z*OVYUg+=rSejUy4-tOFN3~?Pn{ew*lHV?6LyfC- z^?g7bA&=Erd^7u)thBu7PT0L=mg>BfnW3qBU#sneIUl}z?&G-0{iR07Y?97-!VGTA z*5B^NeIUnxZIuUdB-zF@oCWR5Y|yrZ!}IXh;m2Synyqh*i6G2Ki`Rue*<7-m@#e5* zC6dh@Ps`UuNj_}~H8uL>+3?>Gci<-Pmuh5c_S<(&G*{ml=Xpr%?;5^m6{eKyW2mWH$Q18mb<8e zGUJ+E7k{%Ash%@Rr|Sn!Z8nML0#f_T%`yIZ)o7vl|x3p4(`nIFxHz`}r zx$CeHZCIE|c}Y-?#WPeXY!8?E$N!u5U;ya^FtpPv7N!c?%22;F`(1H31$}CL)K-dA zxf1%tzVCP9Zf!l(2@-m!cS)+jP-w7`STDvX%tMb`}zT$S>^9FcX(uNfKv0P1aJBZucqSEtWW_ zuRh`8+hO_J-aVRl9J&sM&BMe4nU$vbcr8Wl_EV&f#?w&ebga1t`U|7@rRix!A(8bI zoeum(_zV5}58)p|kOk^$c@M7g5p#cCFrpNFCN6+0SPeM#p6F(mATh_BitwC1&vR`O z#%9mc8WW&q7XQH2a8;Nu#eaXM57>tTKjY`uex{#(9)Hp^c@@*n$wmTGba(VTx3^l} zPLTre_|O8DpUe<)#Q%M{{(RFVxgxkZCk9PVrT9m`k16fSis;9&+?9k1c;RIPw`3Y5whs8s1o$C>JlGQ6Q=Z`6N)(289eHj=5A&?4?yy|mSN6M z^HkwfGxr=ycZ}qGq`vv)KpIyN?-(`r1F@u30rlnozS~%wzOByGG1rlZY&R2V^=sJY zWX1YmmSnKP=5E<1F#`2=X}QzHMT~ot8zY5Wr$Btc-gEx_i3?FXpfBtvwxw({)wRbB zu|ztxHWCE$2uvA?nGah2GF<5@uOvR@Y{0p|^U{z#+|nJ{|AjR$IG+Jn7XEJ|0LUrvf$;zy+nKY?c~Xin_yRj5kT9R=sLi?DBQ7> znmfTI*P^wDokiPxSk?l$5J1?UQd! zrbCjfS~=VnR7#4!VdN>oYO|tap?t6@JpFk1up@Okp2zaBPwPT!oGMl<%{BK4pv-k` z`IM}Y8=(|J*>;hy3k+*1g~m18o=h>{K}?jZc?n8THz2%v%)z^HwebIOIESS;Y~<9c z$1q1WLBZ|y5H*Qqc==yXp3p3Mpd6NPWhQMf|GKQqhWdP<<3^jx<|Q_-oig&{kmr3J z7J9oP6NLS3WQM2GHLLpbb^LQd|E`Pd%;=Gx@|j)!tcr;rYoQU|RUITN`~Iy)V#kbZ zsTIheou0&#>b9GR_&!!ov)>*z0n2mO;kEB8lf5i2!_@P~`i28eZn~X+Zh^PJVJz%< zc@}L0VkyZ4(B9zxRd1=TWQ> zLeEL&`XAv-B{#P3W;q8h;(*D*;ZYQ5+~u(F3+<(%h@2_I(O>9yN~pgORm>*{i@(r& zE3Jl72p=>Lhkv2}Z5_h=FGOkEzr%0i_dSh3KfIoB>K753t39CX#P(2J)Y$i|`{TMa zdka%#TuTCO#7WrX4JoABgGtV<;yS{V_La~ZFNU1zk4h1OJLY6bM}!C z?ENwEfJJItWK-T6QYc=@mh>VEpKjZ6r_cmCvd_!^a`FUB3UY#$U{lGKMG_bZk^~qp zhAc~(OsC|@50PK3q?nY2{x5X7_%mTHkG6-Sx+HhCYv@YXa0leWz|_$E&}!6nWOmWXwuO4yg{8Zb3& z#<&i#Ec3C<9S~pFh_%XmAm7bsR%)x84PT3s2hzB{>6&;XH%Q}_6dB}wT)rthxTK-# z9Oo}(NVa7dfz)IpFOMRH^Wb<(qjlYtwRk|sw=1P7n^TY^A!W4A_p`bgLl`+5^YHhz zDmJHZ8V#0!X=rSG{$C|8~+TFc-%e-Zv^jo%}B zpia;HcJO0!CWiyx5e@K^x28e!t>W~|+#JCj*tY(qXoRgvu1pGR*TV9%xNL zOCL0Hn^b@&+&Z|t{4esr!zbkC+rytkV3dYWB^L{0N}L{;(v!Ak1aomR^~GIqS?cxW z|B;6V7PVoy&~2u+v4Fmkq#hZ@w4v+G^g&KLJjYIpw?g88KwO=pk48kRnbSw>(toow zqdV$mdJ-vO!NqrrIyFi#=${1jE2iv95jT^O43zDTbkyA1~nlQR98-5|Kk- z9{u(sE}l^&k9i%u$5Zj?@+~dN7wy&*O4F}Na1#;^u*~70?S_WeexO}fKARB^kWZaE z^Ml9+JSkyWvV@GYT3t!4*gmmi1J9KeMnG_}`9L^dQ|Brda3JG_c~#$Q{e5pm#(b{) zGCd#1^Wmo4{43W@|I5?$e>aZk(bOy>lka|_d>j}T{a5=(?#>zk&`ejRZSUCGnsjMD zp2p2+s!`2NLb-b5Aq3rPX33mS^#47r*7)`^&lll!Q;~Rph;lh1Cak>cO}&;zqG&Gu zWgoRzyCEmymzoJX2%MycCU(xeZ2LxRA)~IZ*kVpFN9fG)n1`!rO$g7PYYs%cTm|&0 z`dSrnAMSCD&ZpkAVLZ5%8-x>{&3arMYKtU6KVPMpQ`>Db&w>?D~1DqvlI`hEwBb zA5M**VK_cSbKc|{b4M%jLUa{h9m7Eff7~wqy+@wrc&Qg|Ywd99)qb(0c{}&D#0TYA zecCy$W5zjM-NGofT*Y2zrfC~89ahqW^`;7loD!~Q!xv}j=e<-Z=9qK$AhsvqH@nsd z>Y83a5~Tx9u!S)64jOU+k!hWx-`Oqo-jeO$@QHnV-fkcPXSg3(*CkBnuIqn(##*7f zoF8p{SabX?t+uE&q?0}1^1g8m3OeL`HAsh|hiNe!`jS(MF)o(xx{&+$Dx9RjA!{y!uZ!BxG$I*XG^A+Rd;rfiX32d?3vM@gP*xevsBtSbL#OrC61VX) z>GWQ$t zvn@(I7cKaAXa&QGyj3;h^ZC2+qlcnx&Rty_mg_Y}WGB^Ep>7=|_2`A@r?S3JwZiz! z;V@o+`4V9>Y3-7x*VURnzm=vj&m2t&c3(=T5b~sC;M~R#6nKd8i^vos|I z@m&1opbY0x8s|;HL-yZ`bRmAg#_q{VV2d}^n-#-5FGkEFi=h!W8z1LOL1y)e{ugg* z4Y3WZA~u6~4B6zHHKk@?=g#7_u%3ERap|7xg|-D@Eq;Y>FiMVA%t|o_EM6@K<-Efs zTWOLYPb1#``OXQx?i&3{{ezx`c6_S@3g3@DZ#$Q>eLZ&7zNBY3RsL?tbxsH(KeDiw zu|Z|%&+~eoU5~|TlDj=8%ye-SWv8BNIALiJ=Gu7|({(O^c`C%xY@|V+Mp}7(%$;}3-3j~Kv!96IOu#RoI(x&KcuhztE7)5B-yXQzSGBH zS(Uo^r=F5YyQ8ijW7_5NZlgu;lUPMh;r5MBLeWN3@@-&CE9ZE$qwy}d?ddnt?s!=E zT4XMobn(3XzuMeF77nh4zto@b#rGsb7sB_{(s#5hj{+pCI>l!-jwYpt1bfOc?Pj?q zA!lZvZZOShQf0J@r$;|9jy~JKG0%yj38xJ(EmHxdEKiYB9`3;tfLD?aL^WMM;9O0N3F|0&}G7~y? ziUXCT?=ZzHQQ78!`rIKi_)it05Mq3(5fQaLwS_GkC_0GR)^4j;Um%1bAll^T1tj z2ilhFYO5M9^~0qt7y$pdq#&>5h>k)<4|mly`k~X}nHBw=Yox$wmfENoNwlXrB&?Nm z&D$`;7b-UL|L=3w1nZlSWUAsl@ef6mk7;W+*JxY}-fg@%njImUS4f*Jihy4_mWBZ+%N zm3WhK(p=#%+e0_y(kjirn7f`hC}>?#a2}X;#Ej9xNgKo69QcLV+g@Tga$j=kR1OQ~ zO!A)KSHb>kI+phGzalS)j}AK~dwe@0?|?@_0TJg=4yagU!0%Qa?tV)Rg(V@E3>*17 zkaAU9LgQL(NJh+spoGJ|$YQm_C~lZRggo`Ouz++vxia4u)<&|J>?fg+!52(jO_;6E z%TN?Q9pGa4xp&xAu-v!#c}7S~9OjIe4}zDOJ@x~MM(}O$Y49q{ylEkinb_Yb=XEkS zVR)K}4K&keCNWi>mq=`{x2M2>%V1dMS}gQ_ zZ_6mJio*`4=q}7x?=F2m{GjTMN6ni1%!&D-aPkMwX+^*efinjR6`Af@sdLJ^Y6Xo$ zwa=GlL4CBal;YBIoo+`cnzf=mk9W%!)k-es?eg{55#CL@d(Is_r=L#i#OYQ2dp7<&cQT)A&cCSk zVTx6AJ&~YzmS|U$Lcxdg@cL8z2A#HrpXbxM{1Hwfh42S@f(*+|{rx&V(fY08=D<7X zSLSD~Aw2X+U$*ir^h%hOYwlbj3SG%FL+EYF2CRezJ&tpk7i2!v6AUR1SXkf)iM!eH ziQK6t!pJ1;g8o;`C^tBN7Ut1yE{)=X!zTAQKjFZJbb2_zi6eg2r!4fd{ylh0T3Sx> zMO)2Wp1XO4dZp*C%-*Zbh^<)Pw}mshoLmyKE613wHK(BAy|(px2D{9>mO7$bx7?iX zH8?m_a!!;K$qFsrX}HMrx~~~#KX+i#XGL6la>cyy7W`+9=qGZg+~LBlL8Hm-ID`|( zyRhKy7Oph%7ZwSaX!#(96K*HWc{Ph9=tr>mW{Cym$lD6&ei>bt4+Oy^0dBOPkS6vP zOB><#Kb+i9JI<`_#Y~)Y7qTL*srCup7OY1RKkP{^_j`SkbUx*kpbcevITdq#9M1Vy z+memLQF1-)`5(>x;&>i*ca-XgB<T_%bL3zM@-lc=jd*e3O@AaNpIO5tdYKjRvMDK|+2u5oUEoAnoeg|#n6P8OE@^*NS zBrotQ!U4`gkaLRvw;tk8Rkv_c;p=s2wq1n|oV~a$ZN#Ut`2BUg35PwQpw>tHX9Cx7 zywr8c=LxH~G^*8@xEAMF_Y>i>qQCZ~be@<7o;yT98hO6Tzv2U;LETFXYukd0k7}0T z^Fji;+EFWY99sf(!a@G!2C>K`3(l!6Efp@?4JUEeWO+>*eO-UKdyAhhHh(Yaf1a<) zb4*sp&xm^H*^?c=WHCW1;>Txv66x|>`T#3vR-IYmxmiIt_h6LmOy}L#K7w)1cH{+U zUhsN$DdAmdo0Re(6Kf$f`q*fXwBIjU?L{Wv#QQ4XfAkw;b$s>p9J{pMd4$*D(d_1`9E1Koxlh~Uh3Obtq9Isf z`z)W$T+$w1igsFWPe)M8zP5Mj__(0q;im)8pXwDrDattzmTa=8LL!(6E8S@s&Q+3}f2v;xLWfIyskQR2dETDs64) z6&SU->88{>Ku+{Pepy>$%(A~}AM-oHg7E}Eq$l(`+bGvM!M^5NKk02Ogi)-- zGZbrXP*R?P+9gXkRvuo@*jW2My)%Xq*J|4LC1l?eTsA0(xw#?+F9QEbM=%O2Sz)iT=T)m7O4>K*vto(QO2~CR+g-)rwucDUSS(Xh z5zR)88oo4Da;$Gy86E=)KwaaI*t*b!B}VURR3azy-YkYMJcXsck%|_Dg)H>|^Tm%d zoHEIHuY{W0K=+s6$9r-8m_JcoSKFDF$a+~MQ0iO51?y%Nb-AB2#*^F1adV#_$ye=- zc?+jFI;HW%3d+z9Jq$;ht#M7u8ntmiAt`PbZ8Hms;2(dZ)lDBQU?y)0E}0xzz3m+P zU5$o5t$EaG%v{Velw9E+gy0ogGf@GYb8nRu&OOF(wZy}HS^7D};Av<Jfn;Nz7 z0m@OmHyYQd`9hpAz-OajEPTBl2!}D46tZ(iE8w~h+M;P#lalLQlZIj~>%O-7aLa?t zOV4w1q2QmozvEx7Tv*L#Y<;k?a=4R^m2wZPC^>WAP$I(eOo~@Y`dKD!$1bDGa6X6nzSe>*vcG9`PNg@`V-dcL zYgX%7R;{4S#`JAno`6jr-~Ec9s^Hwmv6<#Npz${^PuI@JNfs{Lbv{DMOUp&fMQ)Vr z=BL8rQH))$>&bIv4X`FwY*2ELSVx7QRcBRGQ4UG$Kd%}(xQ-xa4F>C1>qtx=Z1szW-hgruQ##NSm7N4}d*v*37 zmjC^F!-Z}8mKTv(B2?fzSKJWZNkcnFhl@^|GB*#U@@#BH^LDoAA-oi8-9wiar%5fL zKAKh(YWcV9W87VGWw_(`^FPw(Gb{J7&8LSRaJ~8YxveSRCk$$+JsP0Jwr;zv`8=1* zJk&aw-416RTe6^J`E5>RW*RRlwaa(nZHkHq$A>S;t(p=7dQonNiq|-_$ zCFS=tvrG@R7?-U~hk5qH@_cZKuTr?@GacX0u7k4dsnHDiP5eAI!unvEF-l%5u`%73 zc0YL=GxaTZm+i}0qbag|@~i#W7-w0}r_H)r)SRriR(R)CdV4k?_rJt#(Pm@w%2`Z@ z(cP#ClRqbTTm3kCO?;^rdHPJ;+&A;EHUx>)x+e~(Gn+Ym@+h(N_}yD1hsoYHT7k?- z->G2DCtI-`=5+KK!f3kQlUHnZjCxiqqjL_=vOK+XZmqdT&8c?8W>#p2=zGCoSX|;a zli`vRl4UII^0UYrxn)kiMBdV1!bf>qtzWBkt@U%Yc$RS-`V=1}Tybt2-ws~#v$$vm zt8(e@n|mBdtC#TeHodUh)hYWy=WY2)?CIfslAVT&m-=$DXZ>w`eeyh4 zE(tl@{3DpcjoaV>K^PfUI)H-K7!V!^;67Nd0#ZvG`;xBx$UVrGJge(~wq+Kc6$ zDAClbNI%ObrY+gMVETgfFPdK44annt z^;w{Ri#ZP+IBWfIkYltaNhN~5tgL%wa*y3eOLTp>GTfR{8`8Bec^e0#q;0o9Yl;TB*OVibTY#lb?zMTBr|(~C>!39l9+{QsVyoF|?T35akt=sg zn#HsVJkW&^#?t&(k!L)G=vu+8mr0SZ5Y_VF|6#N!pT`uZZK00Wk*rx?{};mFkC2uc zHs54JL(bM4{5vI0qGUf5T6?k8I87gRh9Gzm79Vdy@;?oKFPJr~UaWKo_?hrRTh&(8 z{5hA;QWt7X^o95R^kfbxaXyl~q1C_ET9Sy%A>P*WnCgA}U5o^L3yUU4cCeE8`?|`=O>MmS*FJ;XY}TM3*HSXVopEi`JJD>7|wdOeBdNLC)cr`}%)UO3wwyVsw;QMW7SU($;ehM#SNt9zB2-#ylB49@|V5=nuwyLXX2LD2k&r+ z@hdCI+K#k+Y9+mQB4<~%IC45&5%=c*d3}dnRZWS%}x#qIg+|vndwNBR}*? zzA45LE8Bp9-}pL6iT71~kl=#;26_4$^8Rl%MRR0q#@G*l97A!~)NO^$)NO^!3RuO zgflFBCH^UHJURGFVIsOOmbt$RnjIe(4#$&LVbcIjU`5|S#}ap>X>UstQZn|2*8FY# z9uBbzH~8tO|Ma?AY)Sj-*@8d2r4`!Mig6!7+CU1&^50r4POgd)+{m#is-ZWqJgc#c zNVp&2J`e8Wh&O^P@Y{#sH+p|TpU&1DA-ZqH-f=MT2YPy5@6Pe&kiQ8BxCta2h!Boi zZ~Fcqw$$fp^hW<4b=bewSPgWIZqRua<_$hS#}O8R#cu}9o(s}zzY$(sjAZ;24z)iW zWIIsup-+h00SnJ9Xf;-xJM!zs(S=xCE>FsDqvt5DX z^}!&|{)e|H4&d0bV`Eu)^aqF49_U|oz}Qr_ob#^M2QJ#y)6EDA$sv!7S69LUU;H_1 zYsDohyDCbN^H>rn<^kAfx8q32@8edRh4ZDqTh9m|<10}PiiXS6x8-PWrZwUo1l*Ah z5|GpOGE6JV{8D3KRfpSI#Fb^kujJN)`Qp(-S+iE;O7RwrGfHnaoU^{>3*pmwHV2>I zO0z63AG&hyP0gp@6KbVngE5G|$_N*4c50h_6mEq6T)=HP?=a80kauc+Um&Myy1&Uu zFF-{&4Vj3Z<06~y<|`H+<@`fI!PYgBu1i&PsQus>c{Z1%JMb~B*yMuf0!N0W{Mnvp zjdXCi499Vyf4H6>)8)u7d%mB>_cgRt@z=UqAgy?+eASQpp|FwZV`?1t1&)7CEyuvH z@-cw-Bee|cH4ans`Ka!L7s_>}WH8WL;ANj_Mep(HwTHq99%CFs;VZrf)?>&ts;y?rYY3N#(g#N-G_2U4ybuHGlC& zbN$3#w+Qz`8MdG3=WF5T;Yj$;!p{l>O0lsPT#xlLO&~{nN6YQb-d+QV zcGlLAJ{=9y3oZNi#11v1gQIOhk&dBFyU* zU&@*KE{18_{%q%-mfUdY#@`9{Il-0>cgXP!hkX1nkM2n<7#7h;b#$+Yt?|9SQ9NzC zF0^81;b5~w@m4+gxK|RH%>(kNq+H|+ai6;yeN=lV9LKPdCG5U?JB)zO!co9y`gc=H z-drgl|AsPI?CvLOh1E9uPW+1J*Bl1awP1BSqda2N+v8%62{}05yJC0yq5i;rZW+y6 z5Or3EdQs89@eXu6<^1^j^7WU7+JjSUab~Cmw7>(19_n^KBX32DJdRZz_>XuSsJ}Z} z$y|JH>3Pn#<&if01o(stlHBU|`y}O|%PsXYWyNf}D^MQ(x)CwLOW4(_;f2+7-qAbD zabed*?c(qk#{*fBta4mZiq-ZSBG1BY=$Xq>Sn}Ke9&zu! zL3KRJWd@q1V=@%5*Zx~raxNK!t&hHV_JsV%ZNbbVmY$VWg%jF`54SK2d{6jvC{ z{6!CkSQamn<1;y>kh)%KZRERtK0C+AWdN+-ju60eTi9`Mj!$eL8DTyByw}PPt;jJt z44<9pfja8Q4NZM5LAZ2Fqomp?_NbEP8*}tl7yqP;F0zbtdUOT1$7nrX)l=_k*A%Gd zEQvZ>%Ge-BXqUa@D|R3)0`Rz{Uzd-hzgsL-A3+BFYt6y;9?M>d&VB);>D;j0-xFRV z5_uF?$V$1B24dXgMdqtRKF7U;EJ4J+_ELz7#4D>4PV5gTZtltydXTqbF4O0dbyn2?~q+)U#i=xTzINSt^&bu;3%N?(lKLguNhE8}6~V7!NSVE-dnUvZ^i2 z2qCu|&WS;n`4e5?f8>5qIP^p_i$2Q{EOG0boIoelK3e0ppS+jxfX~0hVHy5JI~z6X z1G!Pty|%8L5}Fha@ayNngx@|i1p!`@a4S zOLi5J2h>=voB|p{F$hw_TpsH0y=cSa8|&I%xE)Ce%jDqfXV+OB`8!h3Im^r9v<|IxK=6k5LTc>`pisy{5Xdv zIxW<#!#vR415(y=wOk*|QIA=hQ)CsBJ*fSY=Xw4d=#PYn*3k)6M>OMIGdp z7&ENde#4vM0xs!rOicQzXfVB+(RTqEFJLJ1pRXek7t}$9J(JGu>v@}s!qr=_K>NV^ z;>VZ`EutjlrI^7`%EYo=wHIbLch^th=BNW!NlcY_-4t8U#&WamWhlCIkmL8-h13o8 zxlHFaTHJHWQz|*lE=10SwqY%eGF>LZEJ?B)L09v-cEHE8z1fj8#HJ)8=S_UYku=8J z(jeBVr@gZM3EG>|B)fu%&p?ki1=IS(-9?=zNNsNkF2A9dc7}TBQ$nc+dh52}b5xIg zp6xiwt|0KWY<6F^6^(9+M%{dcdq1A5z;AjUvku5+(D${={g>`u4qgR&J-mTau~}M= zDSGzr0)_R-V-okk$-DBVkdm-Dd!KD#S?xAW0p+IVn&)ze=bmWpo5?2YvJ%_xKzt`iA;bs|6I3*Q6uR^uXBD+brjG%ukLyMU-pKZn@ox7&=11b+Tl&2jVX=IVwX>1R zb}a^L?`u@_D!v#MLbpVDPE72GmPwPq1Hh4nJ2zdAbxCvCaw6oa{SYp;D<|%s5|bZm zw|p8*TDViSepmE@LiH?%ux{Hg`daxKjI^H8%*V(nP*GtkhL1(nh_|-&z)xVU$dtwR zC#@G22pRRaP4AYE>fT|ozhBOIRuT3v;-jnM4qA_J#xWw_LwwYC&LAAN){o^=g+;7P zx27%}*wfRXq`XOP;{LMEFICWm17Am)483n?a*$S98T}7WF&9sJACl!#|?>!P{c%n`iUh&T_ka!otQu=D7H}>IsLa)~{ceeSewpjNFL*e6RE)Vtw5G;(~R`+mo{qAxc=IwdQQ}f1g?eB+;Z}B3+VdG6KRyfGz z^OAMdYo!_juF2O0u}eTF5`|m(u3-p={PPImz#_c* z^HuRrGe+}4YsZrsy&tNcFue9xqObkjSKr%I)xLIK(uibu4M`h!$NDF(TFC#?kjgf;_K)VBP?34>SL zSn7et^%UH}p5w<9o`XI0ZVa|sm%*NTH>RhkJJ?h2#`M%$4EEH!<$mg!hXRGwW@&tz zG=lR;>a8_U7Ov|##no9Tb;Q*epJT(5E(mAH(GYF;2a$TUpf1w%d?251n%sj=tngVW zbVJ%*u&Uu4j@JyT5?j(ynZCn}3A?oh>LQ%(Gj%@eP*JguF6YokxT zEO)gJ{z0Lk&O<@7CuorT`aRRn*m?Btvv>mk%ceNuf@~4GoE(W?1Wn|GUuls8=i9?_ zx0US*GPkB$_~KSUk#3J({l7Y?SGm$*y=-4wuI{M9ehJ!Ocg*@wmCEX2qtR5V={=5p z{v_6`R&Rz)leA=FA}T6w*(VJy@?q?yf{1pEOW{ZGi$O$DN`8^ zHs8uyqPFabvKGn5sAv}PAkRu<&Hg_pV8XGd`af0JHc#iVK2WRqeGPole(&2FD?u9R zXMQ7}jz7T9;XGXSmHv`%ei}Io+P@y#frEJgWJJj#6{oG5DrIuLU`l=f+G-6|D-E<4 z!kG*i`7FL1YF>zci=G88CV z^hsn1U2{{Ndp8Fh4!MTnTm%)5;o(vJvbPkjbVt^BeuF*lEwRiB=87nLw@rMHiqu z;75xe$-R`dj^^)v7FP+beWc>WLkX3;4pq~0_Y_7uOMV@DUPwdKyKB@3#w zrhj@OU2-ztC*^&TVZA+NJ#Okb7%^tidl9`!6vn&sCY;Ehmr#jnEOO(v0fVYMPqy3n zYgr3)Jnd9*dkig11$K^Gn9p}yWu0{L0r`&6$)THyb>CJ#mb-bl0|z|~mB2DAqW#lO z7q*Ky%g%SB)=th+16UO{BcZy90tNdM2%B?PVo6Ej-1sO{dT~`aw zWd*L2TbFLvoId59GhmoU-N2BrfW+hoF0_|Eg`>PJnK7IwP4eGs5q+t{ktB}#vg3y& z@+o%!GO0*dh0nVqh$fEq&c3}sV8xetIZj3>ekry^RCrH#gS&b)Jdnj37x%n{PZVea^@AX;fcNlp9OYPu@RIU*e1 z5^g9kQFI=>xZkw-nx|-FWdq!-WF8o_)N*c z$MH#;P4h%s75($xjN(jq!kRu0b7zKHCYb*a<_>+O)rcva)zUrh1~z7nT$1>rZ|=@_ z+k08J_sEP%^OZ(_)4GS*f%7`hV$Dj^Sv$alDrF5RYZl;jq>yop>+4C8_tdo~BI_9sgM-d?Ju%CQsCt-_4Do&?j8UUQ8qGdHs%jHMQ@maI50TwCP z-qCMp(f$%Mv@8W@&$TM2W6+8Q3G?s`PJVN+DVBrry9ZPu9#@4Aa1UNYceI{KP&yyC zjPf)J1OridAS+w0jh4_8Xbd?6c(7Nk8KMgoBE7sC@c$rPWo zHtFIteJQ&sE`HH94!JJqA8}VBJy$=`RbQYi+mk9c?jMVt3OL61>tJ?%QjZ)fto>J! zx=^aC4F+hoHmP41t4TdFtgfPK;5QNiBscD>r~=(X!E9Q;0o*Q4*SmvZb7WxBs2QFc z-I4GFko~>ZrF}zq!vO=>_RMt{MhEAfxeoWvFkd&!kq^=t*pzgA&;X_@9n4mz>6K%Mq@u6UXOy}5f$1F15QfwA?P54huMERA zv%YNQZcESYMXFiwJ%HEw;T|1q&J4HBa9E(s318epM6QeZL`Ryr+#~lDwAs{bADnN3 zy72A4|4nm7uyiYau3Ynd-No<&{m)rNTqysE=jR0Ahq?`k`1xD|0mF~DeXJf2R4uil z=O3!oN9y@W_=o1_@3%kkZbj#axd)@X6JkZr&c}9Zu|-~PWt3ui<(!a{T-VImsjX31F6>Dsc`#M=L9 zp^lmAfRcRa&glK>j3EuOlFG=3a#fwO|e1!7) zaFjNrv{n^kaTO7sc726G>Dh*bw>{}ii&*M5f)X!%!|V1ozkzY<6eOZ#F90Aw)7fz)ahrM0oGT>jk8?-?pA<#P?C?$4@WHf^2B;9<-hf|Kja?+2#25*`H^tu2lCDz=?_>uinFi` z4yi#liXKQ_?HZplMrbZx*P(4@{@d24_rg3b9%`t>IOntnZt7bbpns6uY&LWQi#GFhDu2465uu|=0PyTMLmE%(c9L>4ws2Vk+ULdB z;b;!e=IR+0%dj#!m&$OZ*gZjlO5reLA#Ei3dnMt(H=;pUGs=>G!hyv}(P*yC}0~=xqa(b3sBg4jD<%>H6y-wpVtT>hw|Iy=?MmMareuv#A>E{L3gjrgiSy)Tbz+Q#FBXE17D=hXR&c5}#BKp+% zJFcQ5SM*cSpKD<*NDkIS^?b#Fmb5~vkEChJb+C)hg_22sY2B+sVXupx z9Px*)ZN7A|yRSoO%A5+5xPZW8K+|Y%s~_^o{A#=y9q$DxW=l2wwyU4n^b5<(K9zRh ziSQ;5v+S}5e2dZ|@GVQzJK>n^Zsps`@c2`Wo-{fdN>8hZQBTg4IE+M%`nDfheHAft zeX=2nnGHu$fe^nIdMw#ZJUsN6Wa*^kt`nVeiJ<%`d51fKL!aoyohxUMzk4Ij4G%P0 z)bllOj%h1iN@G-TF6RfqekHBWA+z%E+T9o!|6b3{_h>e#5Bp`9JEA;Ca5(sSLuD*9zwp)o3ct#@YZq zoH0w|Hrh0w`Ok?z%?piXQwz4z?kfnwJgxBz?Go#7qF$xspXRqN_2-`xzxeFY{bmQr z>vC8b3kbKN_xkw`PXb@Tb}>0DSNqNHhyqBK&6lx~N4l-&tcNu58h$PNeMA@=dIC+5 z?@dXo9TJEBNveV_214tbJ%^9MC0|)|2hZG!V<+gvjMA5OH`UYpA~HgF-!&X*%uD;} zr4>5`EQ-eLtIp6AES#gl>iGAp_Z#}_W$|TuI03%~>OYF@%8LLremo%U<3oG)qv7!k zhf6sQ{cXsgW8Vv0&W`Js;k=%=+tnI+Jlf66Z^f0-UhoIA_eo#PcF&Nxl>4Hh$ku)! z|57VxWq-bMf^=g)kX)wCW6hv_v>#|zl-Z@DYa~C&AKu#!bo^1%H0GgVo$~CkXmD+I zxMpe587jn%yjRPtT`vMJq&=LgQLWYTmgPJA1MtFMdLg~&pQ;dM#bsJ( zjW3$)k?m0C{;@@r>XYGwCIPBxl5$kFTuL}0Q>mr%Z#X;DqJ$M5fHnHqF7u<1FW2dN z#oFbxWo>P3JR9(~prFaRFv3~KtJd1Y?5wKg)WRum2N&?hSgTzJmHpykgU zM?Xs7`4DtC9x&L3r4m}X8u+c@T-l(<{;?*yT*snY8B}V1QT|S`-DA!ow zEr&BSlJOr{mp!|%)3?xvVC5V(1-#8kxOF+Mi)Z>WcDHpoqa+iPTDt6(w9iNKYd${} zpFXN6CyUflJ@IINOZVx~5nL|Vlb^eCLo1qRSGu00>rUvOq8DjLeGsMdbOKK57Nkgmh<-;{q)! zThOx-XqIGMyM0>wd~MQrxewrcMO6@5g?x0kzj-A+`geJ#ExPtgVXU>d-fh#xey_Qv zaSV6rbHz0s`{0nN+iWbkv}_MG=#|S*DF^0Dc?C}&C)NGyeV7Wuqkg;-25*ZOp*(i? zfwGLY1*Ns1tt>C$mp50i} zd6)~RIk<{cmR{l{n<_57Qd*L-)*d~RUu#N+DC6*|Z|TdIRI1k~oGr&S9bM}Y`N`*| zYljvuTSC41lY=JD)UmR^M@=0buBf1oW#_&HS!_Qsu&JM#-Me>)%c+ufKdieZW>*F>CDwzNzHUMMmg^qF!hu%9+u0nc}N7v3Z{ZT0Fvurn*RzR1qIq7GHdvX&Qp zm));_Lwm6Y0DjmjB#h5SBT2r45-#DNy7Vvt>w~p)Nik|!E&i5Z08O$TFV4;TF*9}r zX;Ev9c&K{09x3nfp7tkn{mgpZhIL815xsEZhiY?7%08ymIqsNW+goav5I?t*F7++n zrd36YbSM}#%H|Kv%{%fIQRprNK^G2Y$vg zUBTCTf!Ps!2PE_>;Smlt*XteFk%twsOZUhEaoMq7!pHAOD%1|g%CkRywO9ERyU*E} z@+lu--<7QZjv$@rzBMmHyHh>y-WA^1;QGR__Oh*NGkgoE1Y8PD{xF@L#7p-dgzNlZGh@w`a zlzc9SYR=CMe(<%g4;M61{(M@TiF7!h4uAA@R;(_?5%$wdRo?el6|d)`TE{AjNjyB~GkN4?0X_oT7ONnOL| zasFVhlg3eXX0zb{eYP~+HPAdeM-Oc1a794V@(8`3`ixK2{qSMPL^iFLcn@!;Y7xi2 zX|V`Ad~Dx2YMw{Pa5OOr@dA4!)HyZp<1B!jjdr{{TGKjW-AYkxXR-oB|FxRh0{4IXjEW!AEJvkHc_+}zgu%KS2S zIz-(a$8~9Jp6L|X>6|y>XuYd>o|g9oJ+FhPZ_Q|+M!v4w5}`KlBd7Ag?TA-wZ52FF zDoK&QXJp$;>3{B4xvW(M(u zi{<8!@8)T=v($c|7yQc6=3V5VypE#K3-N8j<_$?1Hn-^;wFW#(9-(;_Y5Y)QnFRPQ zec}z{gQ8E=m%HjQQD2^=cdGE~W?336A{D!DxzOHd(6ozONmj~#hx*89^ZQO-y}6;R z*rsA&K6}CC{d{78T24M({;q!K&F$z%Y-wdS^^{Xsd`Ny%($7`@uj^+j_0Ys{E$i8T z@zVwMv~w!pOB8D{b#bO>p{^XYdZ={&0=&w{0$9_}ghO{+DpR^oEka3Sg`=hkiR8?B zXb+^lqRqddNrvEK`RwGBRy8;OybbmF(47jE74aYR=8LCb^;l2HTo}I_JuLHN^*q)d zNO1q$;T5$dv#*n3N4a31JTsaI?Wl_CC7wMIWX_fV4Lysbdo(_GoEF{L2%au`HH^qW zgeAN>`r{88E-A-FyRgVR_RgL~45%Jrw4gf}%0J=Gez*vpf}i;4J)Ctn{>z6g?uhSp zRmGF!fR6%oSCNbIpiS-%C=0c{skW>KpSm3B-}+PFT;YFL+=|@$XD2*!+4G6a%eO=? zUm@F_MR&Vr${6qsxfpgkoW|nm(WIVz&uCmzSd@$w;5^vH6CFQVogcQ0WWAyjadJN~h^GS$g++)Vf53rbn$7b5FD5D!OoOXs&$}uji#NBP-`>A5NK@ z#|m>Y_*(qaJ>!6;DPKL57fC0$y15ZQx%zuDB>H@tomC@pfD%vu zs|&BD3#-Fn^a$B@oo_n~FEl@oxwKo?qz_)&t#@O3>VB!go_aUXv$@EApl7`BeoRmD zOz1lHDxd`G2|e!XRA_n!HzS*N{;V0X_j2$!UIVf0Wf|W7E5+BzWNwuj=vEvPoNPy~ z>#y!f+p!)W(Nu z(2KsqllM0->9e@^NUTtf6yC|Pm>$Y*Tr=b03S#yh_HlTX(&+nu;lt_h$;a=m6YJ{! zEEd%K#qid2uy4rnrjr;YO2K#1$WwgQdV8f6=Ce;_3>oH`CHt*N zpJ=Gh?0}J7Kwr}!99j{k5A|p6Jbo~qOIf`^4#4jBwA*0gCtr5BujzIDXD&1Xl^V%SsqZEGdL2MBm#SY50O zpV<_ovrp9ax}Me<&&nfTD(S^+E4kKfE%{t;Y7CREaJ8fqQV)H=Ws2(iwLG7|xI}pQ zo`UgJip97A2%niOk6FfQF1p~`=X^o%;%x~h@@Mdjd$6L><(dc0?f{iMm+3LM0KLSl zd|b{d=UlcI^AUaul~o}!=OY>3?}66KG_iS)MEy1PRLmLu+>;B(SqrO*$oWvXkHz4& z=tR~f9JhVnbiUm=Bpii5OgFapDg%?x3i?0L%p3!_Zoos)Iok5R0&F8xeBS0C!%_)7 z!QItr!cpO*W5-zys0b9*gxXGaCP)F0d~ecE!I ze3ifw9omW4&JCSn!Q&=3d(x6n8fgf3 zM-7LV22Q%ebdkp?jt@1;@Pz9`{3h%8)%s6~<@FTrKp`lL-vKTA zZ4hf?c0IRMVQc?zlBQ$CaN|`V`B^+5MQ9(HBr+Rjmm)2EZFm;FVi_SkU-YNjw{9Hd zv1)tjeWSa2$X0&V*Xy`V&$2iEuEt4w*IJj6IaV0~T3x=sxrj@MP?INO48N9#sz4ym!K zc;=3LpxyJgoSzz}2Ym(oK+uEfg)~E3Lha zG}$8APE4u+Z|QiJk;)Y?D^=1xEOp!EG{g6#Mft2pV$XxCW!grPCYL1pnnlHl*&bwx zO}{p3=d_MTfN%Xw-n8lUdWPUyoQJBs+>yA?v`>9&1e^u^TiQ7fZ++;vK3Ki8L(o>h&Buy}H(;l%!~XbjBywdgW>)?a-lAMdNY(%JmKvjfZ+t zPubOeh3-F9;@;r{ouJC+R=+p+*o?<&hnCK@vZP>je1AAI?@`aJ4)0 z&-DzwQ22>!csGyr2H&@~c`kQ_&KOP8V{BYB6@-$)B}atygah=h&*${wy)j*%v6#mb zbbF#!9s^-R>l-Inh1^DjYm9C^jnesBvmhmpmh%r0`|96cBRZD zWm6WC$XT_WSL-UG6vY!u64RPqfbMnvKb)<559p#3JbD&4<`1b-rL$)2<^jXYp<7aEiMBJz_umf@y- zp5>yZKE+q;IX3bAm0ZTVweVKoKG64SDG4+7zEt*RAM;5|!xCTe^DV}gi7BkYjb~Vl zDSIBojwoBbv>AOf)}Gu7Hia1UnR52cH{Om9wFvQxj;Tj#7naRNr=6!R?}x=F(n|P{ zrI$cGT+Va&nvPRA`!uedePr6Quw;Qt>S@1GJ4xmKinnkrXuq3XEcgEMu`QB-f2b!2 zE9iHRZsAwoFU$Y~4hK93eVm(!+_ z=UAy@;C8LUg5SH=Qckn-7{{ybr7?cLE8bxI)8esP8iO-hDGyxlZm}cnIRXFJE@yUp z&woqv&2hv}7+RwXs)6`hjmEx^$D?s~=nQL|V3zZ1=kuQJWnQRLnU3%YV6l1pM%4LLiAsu0lpyBy*#A&w@GPYa$%S zSXHL^@?Y&A5kDiB5V@yyp5>J0ceN(#?bMtnraQM;jQ{a8yyjJ{UY+RHcPe6UO)~-+ z?M=xa`2yR$=~=el?SIw!_%|P|%p>=Vt#2ix4c+!izewNZ{-d7EFH0(|N-IWbFaK3u zyo9QDRfrMilcG%4wxB3xDhIGbQEaVpunv=!R2HRUg!;gt1**-GJa#10Na$&8nZy$m zKK&q+2y%o-hrSPn0`CmZgcu^BpeXnV0_J$BzzACFn(cyV_~PY&ZKE6ewsUY zNk!5slh*%#tv_KAb!MiHe{D-hJrCkM>4?@5D`3X>=&3&EWJOxI0^!)-Ys{xIw>h1y zK$F$Nkx=2vrhnd~{|FmSAx!Swvs5a40d^nj2{ZuTm56sLH?+rt~7b@Hzifqk@~2(AiotM=SHNPg>*Z`PIx& z!zobDkka}eYUNj(ahR1kU~mlUsgY0IjyUwbJ3jO-y!?0nTnNv;Omk;HYH_KFNOb+7oilt72)AH%<0sK{~YGYLZK3xC`RA$vZ`lVvECq-;hqk zi@LF|reU+)vk9Y;bNRj&>Mw~AqP!aEn|v13!Hn}k9R#=yF{N+VDf@ga1NF7w zeXY;8rjbox8RLCrYTs5)B&h+;=6bwl`K?7}47s}BL0@LbR_ zG4ydbLD-B{?@6pIcYQUZGd7Hw%9qiFC0M}=f?$atwjMN+t`@N8*k7y(n%{jl0^pP+ zh-q@IO8h*jJ_pEL@NvR|B^WldCs6Tg`D|^QldKZ~F%%~3HuZ!Lq@+~!xhQOxt%IJNaQ;@o0crDv zk}y<7Qi9>go?sIyaI_-mvsEvIA&0ZE>E@+0?^cnDIR~0Y?qM}0AUJN5coMok z66~G0D)yTwvYB6n3s>aZ_0XQx@Pzjp@jWYx&;25)Mt#1>C6*O&FKt8Kg2_xua1`6Y zqe=Zz*<7Mt0YR5I*vNO{I^pplJ^nP`{PJQZ`KVX@{VcuMrvI2E*yUMzZ$-|tPgI4C zS$bM}yHn0J6YhzAMwjqbKL+(Clea4AXXSCW^(US2im=|)%C)i7JIFh73Z5SDyHf&p z6T28+nJY6=yE3aot5ggZ9(~sk4W50XGjU_M!gHk0q7AeKl9mcmi1NYIHc-)$x78y? zVDa-r$3BEkBq8$5>wFf!4aK&BHr6v_1%w2*LkGjXm)TT*eq^Yy5Gj6Q17c1NUfyeyQXJ{d|xX5y%hh0Z@xFge zEb#Eymf9A^$x4)zB$^1B<}2SCzRu;*Z_hMe+16VnSuUwcalTtEc*yf+a2ENx&nivi zK7SM2Wt7T$BRQlqus}$pQ#!Xs#<3sA)hKcHF%)}bQuixS{;9HBKW++(@UO~Z52WrJ z&44%)|JF44NWYbyMybC<>Ctrk%p1cO>_E6?$r_@BREl6*-FB&ep1U&-2ydMzHGb$& zpL9>TV5gSzQCRc0(ADjINwac0I;`hC{OCvbn3MZHrs3!VXZlC4kt*NR?#Rfgl8=!6 z`~s}{gLC!!0dK7Od>wlLPw?6AmZRZG-4pe&nYMQTCqPd-Opd-QHN@TOchl`}3Jw=E z*PpRNMm?KbZerDkHp-T5JiJljTdoyxnC#NT!jqB^P(I_j>W?INWpH)v0 z6}CjvAF(OlVkc`&9X^0!R(w^qo*h=nU-`lISs*(-!}c&{ zCA`u~l$@9RhH*2k;dSX$&aJXw8^f5E*q}pF^hOKzeaCBn_0O2Dp?pGKIc9!^;N+3Yz8Su2Tqe-w=r9m?@w4%Y%z2FK}9=&4*fbK>8R~KcfI0K$Z=U= z%cV17*9Q(dGKPdvjkK+mBwzecxkAq{+Fr}ESUH8pS1#kme!Ta=J-a*mftF@OBxQT_ z4< zd_{wfyVdrgx-yLfQMqMbS#sDT9CqPg#bb~Qr3=5fy9if)dD znMd%JMtdP|3~sfLi9^d2z4%UZ*qLtZlJD)3=KHO7FV{QC`}p~Er6A?beO8gutvro~ z2M_hrDDlo@2+zX@q7*gENO;sYA52FhZ0)Lr_5G=FME@*GDq{)q3+KD~ z;nTSG_0Ax*-e@qOamhP*cOqEF-g`X|d~nXKT-PR$T_L|^^2;G<@G>7f=EUBb25$d+ z4Q6IX)~A70QYm~iO`ms_DQ8#dU0umNJjb5dwU%uh)qpzX3|k9330^hJ_%T58Oq2Q< zP0{a@cY89Ax0QfN@NI9Z-d`GHala_nCzsEDnz^LxQ{R0!>v)Dc@ff?GNCdjGhVqu8 zlkj)-)1=M&=VGw?Y38t!Ho-EL{tzF2Hmhdql2gxCx}(dg9`8vTkRP>AMkM=?_6);0 zVq#>P-4&O2yAHwZy6=1rwEVO!_|9F4 z2m5J6cAt0-OVGomx}LS(`_FXC5-^>I$Z(CsjU0i)e#AQ3i**Y;HgXjY#5>q%yqwOd zqs_V(6u}QI{XN%BaLq%F?&n0zT8H<5LiWKbL@aw#Lg-Q{d7fqMUXsrcFaAcV?rBWZ z+rLzz1dt3z{h4QD$& zc0s!Q?#-e0U7o<>7=W^#pzvv^L`P)8`;@*)_&D$5ZFV(=c`Rfdtag$0)mtpX(X&BgSBVNUh4b`Pk@wtLy5YpF`Uxue~?@_@!N&5UcJ?50_Oe zvo-_q>gCyBekHm!k>=Oa_he@uxwnr5Y{_Cgjl2hr{gU98ZWjLUuI?C(JDMK*0SWrM z=^=Dwp?}sBe3Oq}WyB#gOh*s=!+5Or zdG&D0ekPlZpJzKx_}Wdi<}q^I_zUQFpyR8BBVWX4_@S!)iPzTQUv8^ERg_TG`@ju( z9L2df|7b3&Fu)_M;e+z_nSMuKChz*`G9)qcS@TZT60T!;)DScHW7} zoZ{IkF2HSY$$gEC+=a8h2_LHcilk+ve}APDl`G+L__a>s{aQb*>L~g7@cl4X)tm9T z^)lW%9jhLiPZphbTy2yctr<~N?oAm7YweG^9A(jzKH=+>m^;T2Wj?m|6IDn5Q2(## z&w9A1-!8}BF2w(J7sSQsTCSrF?j-Tkv@G>6cX9T(nv; zNq;Pg_MJ$RE?8!T;VWAKu7yHZp%L1uz(uHfG13A`u16~CZjEIq!Vzv}%g?mo?b7@Ane)5)v)29&ib9lGU^%OOP%M^Oo$PScb-A)GS9A z=WruQX^lUh>`E19e4zf5_QL;xdw$~QxX5Ul4rJ6@>W8Pz1%}a6h?nqNPA&h~&nIz) zPt=z@B%|>uduaJYdW$_tyQXsPusyq{XW`UuwGPO^NPS9vi(?)4)kbn`wT(OYP|hdn z32tV+PiyV@>~{CFZsF{k+I8F2Ojf1E?5CZ+cE^)8X69Mt*dfPrHtmG0O3&!pw!51B zCylHIT~@*gyrOje)U6h{hSuiMZ=^Npcu-O2=emlynEt{}nALEtWp6aEi|~#4 zlYHoXT^xglm@1l9#DQMZw4%01`P@Fn5{1v@W0e+QMeC5Ny5?rKM4E}t=hs@V^}c7e zhd-9K498f}4xNvgZ;PLbCPdnhC#0&o2ian9^c&fYAId(bIA*-%U?qPdul=;-#s7p= zThC;V>NQ@A+)H0PgK)IAYnoN(ne0aOYz^62dKfizIwyp-haP4}Wf3ki^As6Ci76_| zkNG+kGC1=Jts%2#9hTw59a(*=FxPsceBXEf$65K_XTx0evDp+)*&1?3hRM7CPCU$6%hIo> z-EBprqexNqDr}|dGY3|!m25h`XU+6b@_I6w`A~x!!Xv4?O@HF98DsQ;) zBwZlS?__oGe)#wlH`!Gcx$AZRZ9#`0%xCKDvkI-yB*r@)|0FtBW31GXB))*1hJ+?n z>*}0U_rzV*{z^&pBuC^8{0yXHQ1#uc`yvzk#nZ!=>c51&f^v>3Cr%>Z)tUT3KYX>S zTKlquDIisR({llNLC(CES-s~`Y4G~rCb`f$u zO0@<$%T@Q<6-MP~1N@@eXdkPOySg}5W2@>Q&Aya0lL>Ais`^|(f{i=MecF-+yv_cpLzR#(3N^Fu5Lkb~!k=PK7jg{zD*cmxZKWvU|-sL}!7IJ9(1 z|6(h?+=7FGBi^C;p94qEfpfs)g$~DwI%rX$WAk^CPGS?9bP^}=CQia6HgOW0IJr*J zNo?XIPQoPKh1pl~)*yp>?>$U1Bd{XEM<`LY-xIqUlJFJdvK%=hjTL1wJ(|g2Q&j-OJP5qq9 zM!{oj<8utvR@W}C+#FW5 zr*c1Zw(Q*AyUoIWv;23Pb(ikuY(e<)rXsr^5;oa%r}_rwT((#wr?J1 z<*>LGzqu5r2g(;_4@u(g&{+Bz7jV5pvnbk7nXBt5toD4@p>b`yyeEWToE~|?+6aiL z)e=NcX%Df;t3gyRfgd07g(~YsA+Wwj6o>9y(3nl65})I{AGTyW2bSZl(fLN`(J9vihBb}dD4YbhGRQc7xNHnuOe0`daR z&f(3QD2J8QHmyGypZ0a30W(?NfHv@>e|$&@n;9<5rV_&omvynip!QRiqPaeY`*+ag z$a7{ttEn8%bL7I4ypQ8n?vwJxXS03wP`g(%kCDc!vQ?Ny&KK%A3nt3x)#!i^Z%i;J z8`n=<@-CIybphqjG3Yk>Sm1>3rGo9v(NaC9S3FboOt`*i%%8XhkkMqCPuEUdYMro0 zVl{4`xC~mQ*E9+|;xKzvrz|rY`qVJg>Xl5Z_KKS4ukSJqKSX2Y#X@q6!L2kTWQ*dm=(LXjl6bwhnW`q)&lqvnVWO*mETVHfWmz)E!a(H zv`zm#@ijU)Ea(R$^H*uyv5*R=aQ`Z`$X->FKeLGUY8<~YL0X!dm2#vBlS*2WwUspY zk2YqP+qlcxORcxb2`SWCC$-w9PHMYNPQu`tR%mw_za=~-%um>yGhdXLFh5~7VgAJU zZP4iDqtvFp=TD4Zk>4Ia&z~4S&u>41%+&THNKX3^j2vN88z-OlrVZQVw8Ox0YllJ6 zYw6Ysv?8aDf#Bj@^VyXjHZ?0D~Ts?96P`3_ZD9*=M!UFGA8TYLB zN}Bnut492S$~hIVq)L>PT&!4Al4JKsb`~XMD=Cfll2}j19n{$z((t0Op1}vf6Hf>B z3N+hj+;Ddl`qWjjF|fw7sozn^_CjYd557{8*6L3eyV0Dp*>1X6pO~EDYg8_VXZI63 z<#Ev>n~KGbP0F3xcw+h7H}$fajCawGMjlUmqO%Rqe5YDZDJoG|{lX%Tr~DaPZB5L& zQudQ_u}or}O}s+MJf3Rn3VZrnl!1fSh)%23N2up-j%qKyi`1eV^r)sVlGPNAlG=l! z^oSdnv#GvL)M#8?_xSBj945%K!fxZVp`SaC%2&+N z&${kV=|VyXyQ}Ld<&{UfJb1pmmZ_Fe?`jaS4T{xKy)=2}hPb2!A{N+05IwIIB0RdQ zu+FF7A-a4)RMR?0Vb;fZcCs0S@$B+ByKE+J2Hz|`f-JBG-+vJ36tR<4|jjP?ujV8c5jjxa0Pqb<_ z^-l3N7HHPBR<`$g(Q1dr8DC~&3jQCxai`~3C1*3;ei#yIy-6lkPSONA2}B&-DJeXI zQp~h(YvuvfPb+XTZLQjBOeX~?y-h2FBFx$lO}AF~M(PuS;X4$$H6mQh(=n50NtY;n zgX(A9U$q);^W=JD>Q{{}cWG{86Gc5_bU7{=q&Pi+U%sHr}}_2nM@CCjUleqWJ(O}YGif{J9v zhDh~hlaG8y$NG&~kBQ44^{OV;6}BCe!>)&#jhT_ZN^^3fu!}fe z8+KZY5_sa}W1Zn8?lSu#Q+aeCHj6*A8Cb?}{v*t1!7utMd@6BKBaf$Hxfh_JuWhiP<_vRW;NJk)r0lj44ur;A<)Qq>MG$Ay_{VQJxg`op4DEj6`S zOHKWl4un2YS$&zsYG<{^qBQ$k(wJxUavJBBG2 z43%x0UdNTPbzDhnhpT)9E$z$4){=Hhn>6{sr`#>BWu}DW+u$@-yfTl4H@iGYnPR@G zKPp#v?U?W7csbu`SmY;qA=HKl{wrLrP)Al+EPcbF5w7HuXA%pCIS{>Gw*e`RUVa=Dk&VOutX^6}hUn zLb~r0hQ-=6>6MxHm1;nVa$KXXov z+sS+&eWmt<85Px;N{uy0sjdye2(7VQCFv!%8C6qywGYC%NjPnTW*7dFFY7N!rj|7I zp^}zHRB629P;iCDZ2AU4OPr+q2s>=JeS?h{cIMM&Gs)6a9XRnW*^(Z19hs?5cL{>} z)a|Ei{G>1z#20mbz0s1ooO#kFb-Bi1L&7ulQ8Zy451rUS;N&V&3LF}GpM?{oq_2_; z#q#x$wYfQEgLaHHGE@4LtEWCb&RDUY@)K3QAG}kv=u=&{8MAJ@-1mtiH)@muUSn$b zzO$?Ql*1^Xx3`I|oOBLc+vTM(n8$eqbg+Bqd&LW^{Nd!qsxNLkPYKcS$~p|Nl7%pf zbZ{wljjw}D_B^r(8!laG8)w@zN;(Q*0yk{3!I!SfyKAy{a;7}zww4do; zFTxjtjmu1Fi|r!c5j&q(A;(u9aZWTKA=<&q%jx7FCn_ElkE0v#m4C&!p83{>YV!-t z(3+b=wQnRmW!64x6JWCnhBs)TwPr)#Tv#V=$DV4FJD(Fo6M01PR(sKizGLk+eLGlr z>1!wUEL>O_##%hq>;AM}y*t8LT;9TP9o4S&M(?AN3_H}zhxNNnY5v{ufn9qA3H z^xBr0UtaOydqs=m6Q?{giS>PB4nEVbncQzfU*-Oh^3c|8a&?-E)oR;3-Lo2Yey=nE zYll_{xfEKCO%8>?Emp~RUXe|#7FgA_gV2~?Soagbm!`fg?7pu`XTVGDhbzv~wu7BZ z-zBWlkZ6CRC%P))%(@AvzY42|lFVDImaph}F8>|8g%;zx&|kD?V}a8xd@ASY-$E~0 zP;R2K;>&CAl?*-Cc!*vLo)orxD-kYfPn#rJ7k#}&|FkckX2tcbn6*8j)jz^U97plk zLN8t}<6t|AiQ%|O>2Y9=7t{RX+z;y+ZbpBpi6ggs#vOARV{E6wh>f{2f#T>NuQ;J{ zdEE@ArZgghR(Or$>k^uMDx(jDFK~xL9Q?gcEyJEJ0OK$f0y~@Mj&>Ju9A;Q$ZJ@*v zNGdTO(eAw=gwfZgl#ZNHI$CUjGW(%4MJUB#2PTXi^`A{!#X;XVdM6{{%JwQ=#C){K|jtn*FI(MADqwR&?*i`XIJ}%n41kRE3S<7=*yRqY`8K~s)oUJ#5a&`-4M#+Ztu{q7{!aj%&y9gC|( zvxV`ITAYaf%GpiA@nPW#?%&Zo`R@ut5nd3cPw`nzaDCJ+1m$XI#Jo6Y4kZvTZFAEr zD=0W-O4j{kI~cWgTKQCbKAX9~*2|Md`H&anxF~9qlh2K{mAct%t~_9w=^q`yaG}3~ z)s3WzSNiiliiX7@aFy!+exDeV;VBOlH#@laDi=o#A)>BNCugr$82wEejdD5^fd@21 zAHpq8EyP=-UK-YhMJexXmCI`FRzaR=$KF&X(NP{m){ws6PfmF{am!Asb4|6*R}(|z z`66r?W}dEZQRpVMev&n>nw)dz^A$-yeB-gu@ z=qK@_L;|Jy;xvBV{>l z&MzZ}n&V6hz9QbJXS|U&$mdWB(1v-<{E4(|DkI7$XYz~I;LJt^`w4+Zq>hn@Zh&Gg zQI>iBnB*O8JfJTXv`dW{QqJpBO;j!?qx@}{;P8?;*o#s5pz@&&Toi92B43MBv+%O` zPiMxVAd&zI7cwoorkc~zOP$F@7ISmHf1$BL%h{MzV8Q+~^65!Ejb73@J2Wkyc}*+Sb3u^CqmT8y9GuvD;;o^Y?Fs z{q+SkihawzQfcoC24Y!jmrDrBbYHtOBeQSf!{rfb!|+&HgAC$R%o7ViZAi9WpKtFHk!O;SDrJWJ-4aF z4XPbhsQOiV!?)e6DvxXZyPeGO!q&^_dGk7(UFEx3kuxmOVjegT#B9!PIbV%xkHT)v z?*8@ag%qZCStn%60^VB9tinoNtGO2%uGMNQuy-u|WbSs;L`9$RpZU8?d9QU!0&g># z1HKOla+gZl5-hz{8~I-o4GTSex9YSTuS{P#EJJOr742j*ZW1^6l3%HMY%phVe#vBvVxgY|@cM$7wLLS8Z_ z1AeK`OD3XN)NWH}D{o1%RQ-K9Di(=4&Na9|-0|Tu`zG}d`CTn>t=Xi-oMbehEijma zSijsdk$vm8*-ClL;DPKrzfI_Hd3ojSwqJJtZ?g?_XzVN|{q|cOht`>z?3>?k99rR) z?VxPKZ$_hb(6hC_8B8<~+%JhUtG@bC+O^@htm>AXWk;s0P`9zSX%0NBbprF;?A{qz zl~_(@cg%ZG&x^4VLuR=A$)?^USwu_ORDXZsDeX++s$%SD6K7o5%CgyHOTwvf<5Vs8 zOLwXU^SYYuv$9yP@GUymBiNbrK#-?mZ_}^r`a;ihlQ%LBDTyWStI{+(x{;@a1+r1O zk7_M+E(}?nvXVfqXbyL=lE;n!YgPkU7j$=N^oNS8jBLPiD>Vr7qsJQrCDvp0{$}|) zG)}*=f=4gMIXJWlqgre-Ky_B4#l3IZ1XvU6*$apB9l=1e(>r)ZP*w#Lzv1BgxU_*G zuMu=%?BEg{!rL@fK|Ag##5#71+r2JXH+09r{g~NkDOFZ_q}FKr@Wita%Myz^e0KO0 zcPY&=U8Ee|blEM2rdf8l@|-CrI)v@ zR-4@ShJ3^Vy{Pt=T}bVgHm`QMy-0VsB4d#8`G(9lVuV{Qn)C50Q2tmP$=>T?or_td z-=+G6{@NPRtu@uQF0Q8+QoE(6>siaX{=vEme*pIrRi#!tTw0+0VO7sv2u2ff_u7kt zbq*fCqYA0jiY(;$Ip^E5axOL&I;>8Y7MiRqV}{g>o1QNiLU(XnS*B!q6C5py4-IeO z6AB^cvUy=O+@IDo+-m85qqKE;OTcpueoC|JDM{3)q^k=%+Ahx zPxVm`Pg^Q`?H!^!erDzsH>mmS_Vcl+%pCY9R^uzpcLxPhuD&Si7K!gVRoh-EJir9c zH8zC#hixOgEG^M6?%B%pmMdJsY)9f`>WkZ9YZPPUXsNS`uPx#)w6rU$GYFfHy!tnJ zBYma%T)ihYo5wo-G1(39kXsVfwS0Evb!Pjqo`YVuCGO4o|MwzWhR4G123^T>d&+c% zhG3D~XO*|BI$N1v&;tAV?01Yn;T~*td3f0+sVry~JGcxi=#;N+1GS|p{X0?*v7txW zvXsVj*HsAh-u=0~lZ4AphE*>VTu?4qZ3r?E_STI$YoCt8(nP#qYYaAdZ&z^q#8qT5)UfDTUj8Y56>kBAuRQVjK;ueyRo|MP@+|ZUxAW1J6+9kT=_(~*KVGQ@B#a>o% znA9ZA99|hI92&e_%S(LjTxeWV&#i6~H|E##p{Kjknf<*cuN${rD37;wysW$!?rsa= zyo)i;%5h?g@oX8V3s{V`nf*G(?Mu`ch4b=*wi?d2kZRM;EmiJ=R_<`;btVilj;B{S z5gKKys`Y-Ywsiq$Stp!pF0~z(TK$dUJU6IYx4qj+>Wix1hIU`VXv8FRhX(E-bY3gH z+GvUE?^EfgDmGqgJ<-`dLj-5JE=J?Kd$4Wkg`~C`)@{q)5@p$(!WzL`-=Hu@AZQ0| z(@M##msHv)*Sp_h)tWoCLByxJ%Q9uHQHagjb~v@noiCNot5&(?+@meZY2k`g^jX9r z;(`{gw}IDo^$yFb+Tu#l1J|ivY*cr#n8xXf<*CmcEljuGDM+}Cu`XB?(IYUSl`o6V z$XqV={Ec+M-DRx3-@g$&xikui_S?k`*WPc0+m-O&!!7*7i&GY${w&$oWiDSpVJY~?6z$_kayLVfxQ z^}yZD*$mu=v2M(+Tx3ogUq$i~h3l+_O5x1$bm|cEtIV?QMYT1O;i)p@^Rc4EdI!N{ zeBj7qe8{MPM$BKKr>l zB%aVm%Zv9Gs<3M<>!qmrAsTcWv}|*DW*P3J(5P!P136`nY_RE6(i15O7NL!h^~^U$-Le z*J=stg5!+1DAkjD>$XIhJE~jF^-pTlYbJSeM?L*q5W%NjKEYYIMq|tdGJIVx(hF#I0_v9<%%yq?yGlMZoaUZZXNCx$7Hu(SPf?41BM;% zxv`eX#U?u|biEESubR*CCO8UBpGwRwO4R7EpgNnX>t%W$FBfFD0gL&A zMFdylrA6i=!~8P(`}4dOuDPh%eS-VP<}q}SaQ3YWX&d^*E8(%SjiYf<-}&FS+@l}a z5~mWJP@T0!&+6Qi>BAq;zI^1cUdG!h4(qX@?v(RSqSeb+C04d5TBz4!mB57nkH|(S zww@<$r+xY0h>7d@;ZA4(N1Xz4S0jJQmnLexH<4~tkz^U3HV8Be*1qk^O&~`Q5lkA75YvXeJL@oN7H2HT=3BUS=aD z>Sy;BFsnG#y$<(l6v~#ij|0{Vdk1ibTXcL*CFC@e=de{>Kl@=Xm*o?cVT3cip3lzA z-Ga_xZ+hgG{7%J@7hIc<_rT?bucXWl+FBj;vZluhX+3ty3wIn`0#ewkw1- z8B(^U+DcFe2=z5@SU7DcW}o~CPiY)}qu+&XT(v8akI246ajjyCHGcd0K8LrdFFOTS z)|Kc}u2a_$6OAsC(d|vNATkr$0MToJC#|sirB>!uTVYINn>romTUjkpmXW*=1tsAj zFYQ)RmFC&Ti6Nlhi*;zZTq|s39KH>#H(BcyUk~<%>(nQO&(;!5mo!z3s zu+m}W>+3l#ah;Z>ncrJ27uK)8#KR%9z|W}u_4Acx_7~P&K*QF0Lb77Q70lCBxN9}S z)Tvr}<$1}AOK4g6^a+(?P4<}0<~7#-1FFv$p&9Uj_$zA#MCLU$>Z7q%^=KJswoQ_N z`5cE+zphrCiqWs2l8;p;-+r0%Y1_SFz+#q{`K_>|vf3=AUZMHch->h;xb^;EZixWP zc_ypP(rW!`A7XbSTi%Lm(PlX>Y)m*2iJR3p%Q*@y&NY-SS&Ua|EmS$VE)^0EI zJ?!8@LOIpTxMnw(WA~@MA-oI7+HJOKu1Sdv=dMwmaTCcdbjcd=p|9Msn&fePS71?v zdACM!mpJz!X-+tjT~>B`%A8c4RZeR-pI5M;BwXsam8_R@!UzubnK>bh8a=D?u`TAc zu|t+KQ|r189mZj0w-W8xtTbM^&lA;bm9G`)dcRXU4oNW=(B;fP_bI5~*rIJRRUmwl;%}>R=EG)@vVQZ-e)!!Y$q43ALy^%?#!Z!tfAjcio7-?h$bIU&w z?bd&livnZzOwemuvx}Z}xKP7uS6_I7zp7qgxQUY4vi494SrIRm@tM4!Akw8>+eNLg zXZ)C3ffv%{+%-3eYvjR|%&eDNZ=>w1GyEj}Icvr~j{Q~VXls2uc32+FLTJ*?mmI#; zOuLcihCm6R^J=MX~I-X_B_*~z_tD%ymAnjI%n|3D z%Uple5lITyu{yP0+ztoTS8_3<+aSg+seKWIJKH37Uo1s`lP zKjz80+$|`#BHE8-t6l9M8-7Tf?UrWMzKGXEtFW{Ff&a+H5yUur%iYW7(ZP5F9n%oQ z25}e$PPV*W0v2;O5Yr&sh-~-PSp51wksGTlK-(__GnHF%A>~$8kL|EPF1BMrl)$>q zfX-Byx&BU*vEQYfWV3!KT^OC=|Y^!Q%J3YN6y9Tll-$dP70`kuT2e zKB3Yq8td?~S#E~F(1=dVK<*kZi&7nmoaN31Z^b8Y){Rk67>!YvTsP}oU2vHb&~^xO zcN)@j?irv>l$2ujJ~^hH9~Mx`OpbfT@=nvKm-l-x$hWIs+=jo-LH4qR_e*ObO`;S%J5$AT8V)*G6OeS4KK;P z&8w8=r89kfdm@Dl?!kgGJ_q2lY!S{ZQ;wI0pR) z3eTH$#ALS)!1&D;+8~?qn94krWtZHocIp&FubE8O?dXhZ&^DX(kmA83P*TeRy-CSZ zV~3!HJk9j>B}M>c8AmvYnahnMHjAc4Ji8_I1^uEN@4Gt9=L(0igspJKdFpX9dPwF< zy|q4P;SRKw>j?M|iaDkih`~ienh>WetGxyntOJ*h#eA&FM$M)xncwI^U&=v!dcmFc zEZCT9@NPD}Fs`yLA5nL)Gv``mFjh*b_9CDaM~}+OQf5cppse5eQ`3WUuqvRvfA^cV z3fS2~W*Yp&xEIp2Y%Nj4+(xtE6SR2fj#Ua#Kf97K<`6S>OITy->GcyqcZ_n#x?>3b z#2x0_Nd|QWuh-;G*DjZ2>(mB1Mx!M;1P?P;;Q4eXlZ_Gm?GrxCDYqrFncR3=%-cqn zYpU25&?pLCksrO_Z7A0{^!gUfW9HKuxv$IV7};p3!h%xo^$i*1F7p{lYu^MHrxb0d z%yhTmcr!WE*A^;yx=6B!w|A&!*Oa+~qvW{UX^#=rcWmaBVXzU%=Eff50(CTq_0++NtCAkjPF5xNTv)7k=akqfq0nTg;n?bh$$SyBt zV|Ftd$hA{w4#wvpqXsQ7|Ejl;!ST4m`jo?wy0SFM78IJ4eADIGBamqVj5|&Lgc{%~u=YbTG*(n<@0O+~Ped#oR#)7%yBp zSNHP79gYQN^>Ui$GZ-hggC#GgKVZ_pVAOcuS8cxina$(UUgYW`wfY79R_DngZMK(Z zVlnreUXH(yIPxgQqo7=-qqcE*tYM=oZLw*&AKc|BbZzw3Upm2I8}DcK*4EMu+vN4t z;#}haC_7iuZ{DS$kc}nDpC}0fvubEevb0EZBYBz zm^cMpSHYFooqXTR4}E5{;Q4SUBW*>o`IIO-T&nUXm8#vk=Nc?t#Pe&1W4-|I!5Y2P z&b6)cu-cN!bg$G3jInY1&MnQXa(L6n8%xG@Tg%O7b^s8q>C(*YNvA4f1ht`(=7A0X z)}7s}Rj*#xDX>O0nHlI%v;mZ4t0qsa9q_95&D+OH(daC!`W@Q0=_|^t8{Y}~4=tI? z2stauI-)unZTos{oo@J+ybmGUvoF;>OUMkU2t?;5Fz_v|ngw=uy>7L=c762q1RIBb z-zdEdxtD$Y*M)~$k@u!?r=r`+P{my9FiHKs01q3W%GRs38A z++ivTdzP^sTu|`t+x7xam&B-mG!SN=bBPi*NR+w&C>q>bqWV znE6e^#ADx5A8ycV$o_hLFDUyU>FExesg&^EvbwfcJ{FcsA|;VWNEGfTs+HAk zk#5sFG+LA7(Aqx^K;4vPWxQUtE>Za=jZaag2Gw2;C|bp$LH9l)F9FiU+d{V3T4Eulc9j;HbIP(Q<@cnA6|Np8m0TX!+!?-fq`F!8(WuZ{@7QPuJ`YD zxFv+$^swkn*($z`>Ai;iL$Wp-vQ}g1U|`nyGc5b8n27OOrV`qUK>@HLHKA+t`xE^%%Pt;tOBS7_$itNVU^ z!c8jo4XcZf#BZ$!J+wJBxRKk}9e(dUFsl}FlsX@&V(vuwu(`{h58?Fz_ZUHl0%PV? zV?ZAbtuH$-W^Bt`uu{ZfLn2&^h)9gs|3qUxaOJ;0IefF9o0b1F5yn1eTXj$Tx_nZW z2mdLFkv6y1Yue_QQR_pAD5px=mBkW^bFyIoWc|mO8in=1eX(_q5Vz{NZH$cI6<5|I z#XYKM+`7yuH|}|Ul?YPEx18rDdyEzS1Nw7~>Y7E;p#SgxqY*2_XMrL*#7)kSB<_|* zPN8AwI*YHTvs+Ew#bS-=^i>%aA>;o50oVTv*01b3y8LEMgd8s{Hf~8obs?3{Jr1A~ z174?npC}7e&kX^4%nuwET?y~XW2S4i2?qG0DL71scf5?hdvLrL>fUzjXV)^mSBm20 zesF9-R3SL~_{Ong_qY3g3&-%m1l!3!)q@(RZTCJ@d)c_(TWf{Q`T%eB#IUhhutU*< z6T&aO4h1CiBD^1cD!wD5o-ct_fx26*{;vLFgv;~FXGyF8c+C-7>@YlAsmjYstW0IU zehXy={rDOLUr2-b7+<^y#pOTFHNG}++rSNy=p{IX%@CdyL0>r$bZoK<>WPjqf~oto znMCE0@*6oBkpdQUa_i9fovBjZyA?%Oo*U-5=!=|bh2XD+7t}gGglwwYLb5!kVB>Qi zE43Ks>KTbDuIUBz+bOe6CLS+>RK1X^ZQQHA47?VU3!##&U6acNQvJTdvd_2`inrVX z=}Uo0)i7?Y&kb%hEOZ=-u?_40KH*WU4B&d7n^=}P=C$?6U8;Y#xMPCSOA4P}tm0aG zUc+kDdSOxc|5hsIQfOx~*2^!SyO3__+3gn(!+Y;@C!0?U)xf;;y;Q!QRKs*xm12}* z7a)2%0Zp!5tNzr>vQ{ejm?d*s%Y02GkPqx-AxEpN`-?l}_N$a`^}S{bs(XfUMTkpj z_n!M&Gn-#8Sss<>Z?>?&(7C#bVX=0VM?l|JeL^&DSI%ptk+{ANleKQK*RnG$4zupU zug7YNd5rg@I+sg1_R2bIqxwj+jPlKO49;6)86%Gdu6vqVJtKUD!?i|s&RW$zmulTS zF&*)Igy*=ReKtEyoU;ET1 z8V_FqHSsz~%4)pPd8DA7u{Dr;j3)Ej*OS^^(YJNlm4nCJtA!iD4Kev2^6T-W>n{D0 z`jT}$WhHM>-*(Hpps!e0QhrYh{D!=9yOpERrR)P{)3Gz~pNM1ZTQAO;+jHMhbuW>- z$oZ~~z8bB|Nzff}ro_BUQ;E~}Y-&C;{@-@1J-pQD321_5jr}IBH*m?!OoWfP!s#aI zCC*@BNg<`{aYn-}4R)bx7=Q2IqkXugIF-P%#C=WPn?B*~E|6^J#kM{%EOLsgGu&fZ z;Ls;3VyS2xzx(gkX1I9rw)+42;u>T9Q1?46(SR1Wlu+U~^*b#QgMM3+jn8+p;ft_a zVr;)vPu&tXAn$H*g%P_lW!jdxu|6Ib!+Uh)!rEy^mVXa-+%0m$@xjyY7+Lt<{1VB6 zt#Zimo%8IeDz?ArEWO}a^7wDJT`Y2(E>hwx;+rgoZTGUFzw49RcvC`An2fXY+Fij6|$&f6(0 zP;9Vh+Y$>~6)P&*jo2C<*H47UO0^Mn)M{^Gq+p;yiHC)q8zOO!Y%GSdYw8EmYpu?N z(k19E+wBm64QIXE&(M&C}}$mYPY z(=MC^V1pY5*SjAr_k%XL2FmbQw%0e!V+rjGeOwX6h|zXzc}|sKH8l*z2dc(+VOVlF z2Sc$ACma}MY{E?wH82f1>T90>xgKMRk$OT;sP{+eV{r!`n$ zfkc3k{wAQ$sNf)9iZG5W2+E%yWSPMMzP2A7zNVO2?acpdI_>&aWi|ua#m1%H-}!%Z z;q>ituvQJCKgc$VhzpgsSxx?~es9V{q%W{l(~Z*H-&5Ej+@~%gPFYcG)yk$f`=-LZ zdOiAE3URh=d5ZTc<#$!@c3pwq(3f;VFv4nDt9)yt(2do2?06xp5m&K>D|^J4PM+^; z9`^>i_XJ|{KRb5z?KNF1?d1Qqp1xjZ=wa7lSFP%wLoigYtk8N#*AHUvKP{+S{pTMj z_pqnmuAFRDbz=C1frw_VL8RUVz_|U~Fz`w6iT4R{n0MY1*AyjuJ?Q>#-t4em-0|yS z;GUy+M?}5E&XP!us=LcoEP_=1!eg{M1IA#xd%YJUEnYQ(|Euwo$V0y&2oPE5@TwapxH{&02?@)b2S|z+uZhq`5K6ozq(R;2^EnK^@pl3VfU&pKs zXDvHFgf_KqKYy(<-Kr2Y9!t(&zom}6gXf#a*>q+xMwZ@^opHaySPd+?$nk52bRu=S zYjh0TZ07xD{JDQqwQAos?iCEq{WiYK^3HvzNI5(c@gb4lv37Vff1cdC4RyDd$>SvF%KV>{da7Z$h|Uy#amp_=~p8_UzSVQ`f$lym2i&q zB_3G%Iv{*&zOZ7fe3=!}yn52K{QgJXxsY9bkIh})iG8f@y?>fr)Z%0bCH_RChj%?r z*06x{6^UDmz5-!x#+z#8lX~~TU9EkI|m z%E{dvdV{pgoj$7Ok2hzL(>YR^3s5Y({X52aE?Gm;l=q2z*7sVEpEk{fOhL2QzSY%E z5o98g5=roADw{5dD8C-)FD}s-t9Cy~f`D#!n87D)2p`J8=EdE|_+&Wzxs=yRg6O#kVU4*B>Zn0%CTpT2!-&Krzm0J~{FmUW`cCu#yB+a+yDtlic0B&8kf8ZmzuK^MX7{p})wYEkS5Z zqY*E48I`-c)>w8&1gJ4NB{`b3vX)j1A7bkjQ*FQ)#&kr8H>lfTb-Td86a9D6G-~Ii zA8@R<`r}cDH#^4$K$jVyPG}XlS*r{3QkPAu=B&?igwJ*d{kR^YOaGxp^V$AOr|~-t z%$xlK`ln8Kqs;@SJBeB`I`2_0nX_tT|#T<5Owg0(GL8kMP zX^l(t*Q3AAD^>6BG!FfLKdssS^JaHvkN?+fO;Z1@=*mk}vZqIcNg+|#Vf@2ngVEEY z&h+PzYCZVC>rJ}6Qm?0@hfY{Eg(K6;HpkvpW*!z%MaB+wpd-qqiajMDJ#`@c|5yO& z|HoO;{6AePv^#sc`v18HTuad!@XiwO&K?bccl%dP5DjL33g-3x1&WBG^enNqYrVuo@u`mpp-94Q>8h^*fV)-mh?H#$!jE#ojpa3T9SBU zcB8XOl%CECQ9PP=&WGxf5>YPc@sXJnV0^@c-l4`M^}8Ic_4ZrhyrQ?n3?Tr{nbcG8 zic_((v!G~aXLlmpz>Lp{E|3beg=b4biGiB~T?ID`bXhx{lcqog%^VYqdQM`Z9yl#r zmNz;z!gJ_`=rnnn=&m~38_m@@{~JYnF1f_`KaT`0@`3DWuFm;P$SSJV2G*;cj|kjL zKn<>Mbc#15AFgT+%uxjwJ|3HD6P%3cNhT0hr3V^_hB9A*b>d497BM^3Nu)l{zt|(G zl;Y|ushYgoB@saA#WM-B7tg4S`ufsl(QIHVvPEH+S*fHE$J13-M0{y;$Fx)sBEGa) ztv}Fc?$JZJd#usso=H+Fd8AVqH1`eyXf}OU5Y&l59z5s95^j>7pO04agI9Myh3r6zCt>U zm`W-6iVny^mPX+U%?nqLZCIg0nd@CGKrh9T8cEbs{XOEq67ygg81M4XsHwcAbh!?* zHax|cFc+fEM9p0`-%ZddY~6lC1-CK|T?5ZGm@!>{@fN=?4O{qU>XQaOO{z{vpxL8` z<-V)m=3kr6DVoPFJ#`kIBv=R+Z6{N?K9jXlCHAIvjP^`plnd z%73l(v9meQtA9NrrFs;-ETslfT-Q;|2@g?P(qa@YN0~_uPGS^_2zpU7t$FNC&4Piy zZshvb$GW*utQ*06#EeC}xRo!*b2*$6CD2XZm!emv4kvPpt~nA$H6Mja^6W~LqIi$7 zkyR)s=CpTJ)j!@mdYSA4XkSgq3zqpyMoK|<o0(Fpd7Sk}C$3L@7$BjzA&wwp}hUQqPdwJGzY)k8U$X zrl*xY@`2Yn@&T$s?JA(kf1^n{=mn1qTESy)dck9FQt-&25On!@?9EODryjDI2mY28 zl9deS(BzRpRTTn922%-3SS|v+DJ_cZHed*I1yoid!k#yK5$n2Aq*jrXIZ+gs+%4j% zC$uJ+YmMk>kl^Ed*7-bep74>crNqcrl5ON`DKR}mI3#B6Y5vbM2%L~ssVSwF^x@H6 zxeSSNW&2B338KCE=Ubf2M|Vwf{jPe)-aVWUnM8>rXA3j+#{TG_S(ZmWs7h*?y%5(L!~~WSJ>F-(3HVn9pZ+Gze+noxh{6YBf$snGuZAn5(sAOY*r_XiEJ?+c0l99$(SC;3Q)4+Rb<;}j1My!bZq>_1a$mH1GmS1 zAzIhKFYNap1VKFtOKJqmYI~zO;w8=2IDXV>y`Y>28eOj;UbgduNdMMo9_}Ffz+@$> z?|*(mG5tEee|{pd_@AFhY;6H=%t=q}8hC*90B{)VW;ROoK*?RB7|n*lny z*<&Mp4t%7~s5-(}Huo-9rnskjlAC_r*m~WG414ETkvS?Nn`wNP*B{?y^~dQl$;o;g z-xD;321=Hs7U1zcxOwzwj>D)C6kjrUjtBlojMBFoQRc<`wf>4PpqRMf| zJ8+fHi2<3`SE;tF_<^gK8VWezYpQFnWSaxFz~8EcJVol? zv*t$N$G-gN5C?5jtCHr_pvxjFy-yZb-t>F)d+@!~Bpj4x?j+4ZU?E@=*h#T~o>VHjz0kg~jo`+lyc#(Uq-4LN5#ewQ^mG+4_V#%wRY_d}1a z*F&;sRu+n*Ywt%Ay!!?xb-mmN!Fre#JOq5-7ClV>=9wB8cLg}|qAG!WxDuZG{ys3? zH#z#W-4PI4?jxVo01v{-F=u=M>`@#01liexh)|j>KIqms>sR!6`9Xs&VL4}#QW#{) z&6rk+b$7kIWisgr#=Z`P=-y@ie0fKDK9Zih(sOru?n%%7^xT%7gXuY(o}=kGmYzq` zb38p?NYCSXx+ou9+Z^0z-O>|LZVAT6BuiutuGf<(wBAyQY0L-Li*AFfO?GLNKA$mM z_|$m5d_p2lj3qykCn(T^lw-#2yV3cG5K8H3AN{5*cJZJ2D&omR{VQwruT+_t_gAKb z^$_+CNILKPVngh252c{JNl_3zPY`T=CTx6?S(42>3(0*xT^R|GNom!w!ifF|i{CtK ztc_cCyY(A{UQG7K;ERUmi)s%s^P(|rxlaOSV=jum{I*JoqV|NsTfLUW)#u2!^q1ce zLSpa1|0OJpz-EYfNS!+)5`Vs-=IHUjPb?+^r;q?^uwH)O$@lX6W<*7FV4g(>=BX5D zy>C6c#Cpl^>;}K<8o}c;il}t}>ZD6H?UuiR}`Lt@Qa|0N5 zqTJKCSq*1aHBYOGEpML20cv>+rYEVsX!^7ss?S5TOR+9Es7w77#C`IiOi^u~Dx`kX zq&j9zDmoy|O~HXVju@B-lx4qiMc~aG6+j6jLf$sEsSyL*MZ25NOl|a717_YoGlTFmUH03pT1?p<%{%1yE4Mb0jgp9jd?>nnPD(x^-|eAV zLf??#33BUGT;1IF3A#$!)e4Y70Wx@s)aGAOa}pmVUnpLNQewU-rj@O%>uOQaxJb)O zN+?Xy-=*qW*T6sVyYB(TXXx*9`kSV|$@;rYe^c}~)zKEQ#IAn>vD8zc*OU!9EGj&+ z5-WpTGdJdHMon#?-7#AB{XNwPHi&4O+14 zm37XAN+Ue&5(QFN^`B-)l|BvfM^Gg9(I}+woJn&``P$QGf_RWY-Z$oxM9=1+ENK#& zga2$ya4)}m@66?%)%ISfvY0>~Ozib%v_T_KaQ_x%8&S3>0Y2NJ)XJjm$g~8`+aaRRAnx-5Kr%LlLcB zXhxxEM!|%iLf(Z}Sn>|2~67s_}SbhmGCdr5dTEVPvqONTw+T>6U^?RZSBp z;bS1d6B2;umKf7LZnZumx13SRIctwvGrrrC6OSaL>z*L3HxeSFdWs?TFVj;~%btqweca=FA5V<_%&7PfQhGlWGt#A) zHbW&4Qp83HxPtc#9q0M3p6bF6P@{rx>54yN1f%~HBqJ4k=G|!cUG?$@OM~%#@WniI zYaUt=%t%kz`w><4Z)^^|0k!e=?z)xhYtCDTpe<&z&Wz5mJ0R4!q4Lf>kMdSuL zV4SyqBO~*J(H#H%iCFtP68?EYpCi1#Ph9r68z1}o)E@TM{$0wH4%t8E*xOTr*q&OV z?cZg+(UV^CNaaUGXtuZakFxGYE(3*u;qpJjeNL-zjQ1XX+IVM5!6E3Y@fE@~6!-WFbq+mm zsiC;WR|t|Y9RCPnjDMt<`fX3ax2JN)KJoU(%m}vFR_#QcR;uWG>@;KXmMkD+ zGB_>ai0BXd9`V&u9vy_FqEu8klE%&U@C0`l2_`^)mPU8@Q-?GBDa05Y$~W|c)z~KqjYv55rp+3p^LGKgtjx~p0(D4-* ztm^~#`~>}@qR-^Q9U)Dwui{5pHy6M$4@4>C%eYq zRFY^re$rs31krK)BviH<)MV_b$y2=W{B_6yN&2qmu29o#zYN_8;h!X6L`!4{F?cME zwmre=j6<|1aUhFqWAN3pzf@qZ5`@UmIsN(Hwm8 zvlW$FKoM_yf1j^g=O9DW`{A?N@{ir=zni3pk|fRKJ@7EB5rr_ki;Psive!C{$zh&{ z;j=bj&yt<$bqWwYA3p0kZul(t6^QKnhHzxA+n%8s-S-X66t^jyD^%}h;gClFoJUv- zcmytbA1+e)?WCS1LaGIz+}~D`KQ0%xNM6QbJ`_X!#*Ld5TQ9C+g286*rChT?hu(88 z8hS5H%c1w6ym~eC9t2U;2|x5+^CjE$wLqTceOFp+#SU#tdE3NQ#Xu?29NOjs2Ts_l$(@wuI)k#O9wQyv9BOudz=OUSlu-usmR-5?ocDr(<{@=qY|1|Hy%ie`Iq- z52MT;LC5IUrB(~wq6$U)JPc9pf&Cc`itXWve+s&KCGI@Lrg|AHuw!6Un^`CLzPjE(j@ ztbU*adi?nu&*dqu=X0DTT3@akqZqJMUgCA^GCf6)p%=up#>-{adtaNgnh8yb37Zj; zyx|3v zP`c#3sTJ~1D31QZlo3}x$MOb={)vK05;33aNvy0uYTLBraEHWF(U~v?qttUf)}kI( zONJDxNASjXSdYecX#5!tom4sY9w$E(KJlh;8<-sXMEPP#4?A|pu#rq1jjFsT`x#lDXOl!$XG1@F%Jw>tXnlIhd?vSmp2}9n-8q6H=T2 z8UD~aIQ*g0aQq{Y8QOWkHR{;g91T)e72qj!m(ymBk;Meap3ZzO!A%m4C!!&`##QQO z>N35CKS>rv^Tf}<=dT;h6Q}c}w*}tyUTQ2EO%^kUzJ+FT#-fCjrbV)R6F#A;t#oHP zf}8SGSW{FJKe~iW8XDCJcu%eoZ5BmqX)5dO)zZtLZNvf(iF@x^XBQx5y`U2y>-^%~MWvPhU$**3nf*N&`m!$s80Sv+XHuwiVg89V;23UvUK}@Hglm699I!I7G zL?C6PQqkIh(=M{%YZc;9z45rhi0g8)OQ!emdp~!1^Cd}8a6_4Ypr?puPjLJ|Bc$IL zT`kny^7oOuA(77AiME7*^1i}2tw0);lMLBn5uBvj+^ zV|AwzhfK;>PEy^~Y~!UHtt|6PaLb&$o!> zulB0JQ}#p$VlAvsPn90KED?T>^K8GdI%l|4PQw=E^LH>_+N`u-vm*5Tbt*6TIjcQ? zUE(=|AOZB!W*%BDtj8!6+PcSiO(kU@ zh?~bqs6cQnuA0wTP5bS7wa@6Y*?za!?}I^0=6~iL8I}{rX=;g9?h*Ul=GYETw%G1c z-J!EycSsAByzUTdm#$a)OG&BfpkyjIt%M-xk#1at8=UP?-3U$C;r z?e~Om5St8th$};HHz=xc9R5&5Wp!%-38|Mhn+VWD1tfHyyIi#kbxu=G3o#3{K(Gzl z3yfB8I7`dkQlLW<1#^&f9447RH8Uw=hXhlaNe~@ZL6CHaE0VtL(BZ&J3BgN;c{=i= zJCsi%^KtOE9y`;n1 z&bOLWw!Z3m6_R$n`jJ425r$sSb30Eb{m=`h0E;NY&YJZF)Qgf+@#8)%hmKn_dmjab zq2n?)1YExf*m2*xQVz*O$0Ip_9yg%J)36U6H%8PW;db17*5>FlX(Xo3*PG^&W*MWh z8uw#A)>9-p_G543*pE}w$9_!H$9}9GZSniDA2a@vg&J4&{`tAS#4O^f?HOVgXW~-f zE1+{7g4C01=X-1c=f#!keD-_~qpJQ};M%rWfs71A^+b-YPEC+%g3v8; zMKyA(CFZs1L$!pHqgD+tVytxR$N{hKdDz#^IU`vpijKPaeOx-?2$c8pd))yOg6lF*F3V@=Bgg4 z^bUWTlUbn0-wD&xLbjhe{*Fz>Gb;JoX5S;_0G5n?|5dHsQgPzENx6s@DBF9B=X&DG z^%sdO)w<_G>@97~@eaL7G$LwHdv?-G`wy7jRQ2(9y(jjRS{*+FR%Xc~U%-x162(UD zByjX*NU{=+Y_nCEMb-Y1ZAKpVrK!{=hkj&}mm*53O;&1?%vS8%MDp=7E|rGfQip!x zXlqG4e#Wg^t7&l;M%6M1qn3&)i1KRz_HwDs;G?vB95+83=UQ^CI!#FCQFQ^zKsV8A z49%hVm{uN;HCIJo`VsTX0$zB0lZ%h>P05#N0S5WrhPy@e!pupP6{8(JX(Y2K68$ab z+wqSq*P2x+HLE7@MI;SoPnkjX>X1fGV6>_IH|&_&{`&a)RXCxtg6;mKMeX}FSTV7)XvSyql}dKhe@P^z6WY#ym~p&oC8W3HuKzz3a6>;cDs6Mf!7=J}e-z$oD@dR+r5IkgnIv-h6G$!e&RjHf~{5 zzEtJG%bWoA6lLwH0Y31l(lx*b%rbfHI1`z*_kmA2=JKS449*GreZzj=v|ko-p!d?P z3B8wYwe-`GzB>zzHXPe&FMZ^gHk|h#=?=d`!E_PHHwKcAA=v zh#HV2wHCG4?g-JuXV%1Lsfo|hM%cj)Y;#pO@L6;`kUj8mWPyOuB**Zx(LVGC=i zg!g3NSH^M!zY-J#Rd`QzOaBXK+G5y{mp|bjG+!jbZJ_w99%)4A(aIZ8NDnt6W8Q*R)i>8VYIa< zgBjZ8AnBULD^f27aYcZRc30TK~Spvg;u0-;`z8M0FAKeU=5o+Pu}3q zIXYCzMN&LD&uE8CZN?<&pT9|Fr>nFW)SSL_8o#NLrHSor47v_HlDKGT^mD28bE(;5 z2R5scdTU!U^Yy~AN)8ykuqqBgJVj7^^#yZct*k0!kLhzXIN;*a#Q*G zryiMLK6u8Zm?bLRMwP};24{?}srb+`xy}!sF=WrM_`*U+Fg#K6D=l4BPXA~_^7G(W z;4ubC80%O#9b^@Qs5p4Wj6Cb+atbJ2>%k&jG-MrN1?cYCoL`lue@Q41bb^db)~cI6 ztMtg^^e}~F2}+}@Mql84tPTkgQEoY{RASJa)Q`)1lxkn^vAV?au-CD$!&M!FkNn0< z>KzqyDfT&&Iyh>Z5X`&pJT2T0ew=a*oixPSG)(fr5GuN5iY=wNXTj&SF!XUc{H|1q z(J058;j?7*&YluQpkra*A@cF{BdF5zWiE0sPU?eiq$d`OR#m*1IJ==ebA)_QR6fQv zc|_%z(q}{z(3o8cTYv3h4@-5*Q&4it$3_r&qa*Pug99vZ;Q2G>Od6Y`GSdDjluOzU z5!Xxs0_ukgM3vP)2V$cJu(JZ@*#;~Z;nY-LwFQr|YurA8*pYduLLZ_St)>g@c} z5<43sT36L*#iguzBa^D@hsPUJ`icx5dWqtFX|$5h8nqfit{Hlq@%Z0@5PjI0W2 z_!V-OLY^XIoZErw#v38`(9;_mM9UQZ<#?_~$<&{VC#In6H5+Fg8_MO5VT8O%17z`# z6js`s6eFo}LYK+w`m#v7JUMjpLTQMK5(la*OFP6$N7CH`D$@ZOIX_eG zCyY}`=m3REN-`mUz+W0aSz`8r!9k3~le|O9^CVAuA;bpu~4;J zkU;He9%freJvOnj%P%2;5J(xdIcm8lQgP*yPwlEYl$ zrUtltQX{(eQ32Xc5n$ zMS|EmadUr~K?R$NnJYXz1EHN~`^m6YE}M(6&r2y-XTxiJvQNlIiw!`%mH)~x+lRcAEo37W5`8qF1c&BOBEg|VEMc)*#RM%<#ny&o+NpCb0+PD|e6VZ|J`x;! z#2jqI#NVc0rRn=q!~L%K`<0H7I5L{F<)~3-XpKqr(dN(^@EKa;tHaLb(Aoqj1-Lu_ zd9Q~dKCWym4$}rw53UUlpSD`GV#+~}?&hISWQ>8u%{uUpZv!%khUr}Ua^)zQy7ny0RCR8L*O6_O}8*(E(n?ty|H^z#J~ zGh9wx;W(YTB40J?7>bZQW!vg{*s>zh9A9f|NPf4X(bUURR|pv|WKYpl$Z42HKT~00 zBKH7DF&IKkw!5s{06r@%KMd;vJB`Pvd4Eq`k(NMFf%XlMH!7^X;OJ*Emb4V<8a>u)bib96@6Zl^%3^bJUgggr?pqgGIqM^@e>k;9Hw%yp{?XG4IJbF}cJn zNAtyPG5zAxwD;UcSEPQfppKTff~GBK1?Z+a@e|F><$&kz+**pTDdp>ChS)KQD#A+Ohkle?>q9>R^C50(6D@{*B$D#7XhKyn zIV4Ikv+undNjx0>)J$v`I0eI6@eH+WOAck9m3*UF3wVPgn+%M8Qf0MB8!4uBagoJ^ z;E`dzzf>p)2whwNZn74sNt2o%29j{{J`l9S*jB($NQIjtZwfOg`lc<)t$-pT8&}$m zZiZRln%xALVY{O-IBm1D*5(9bi$4#=Qh4-@D17)cp`cuQ28N_13<(C^2v2f!2?n}^ zo1>3-6Qhqfx+yWxeZ)Bb5m9x}?->$`e&9;>rh=5)D7+CwHkAp`zy1L`^5u6+lBTZq z4-oh_DMjuJylqnR@M8T_nqWy|dEnG=+7CQ6?3t`(i&C7OPFh9d^{$he?RDuE64aXo zBL2HbcEQZ&3Po(zoZ_+$oMow6XlFdVtJ#W+wyV5F>e0$;vm0+~dWAKJ%y**Vd zF4R1f6z`!clJGoqh3_^u51lcq;gD%NrMhG|bcHhLu>~0BL&&P3pZZ3EnAMI5!1)$d zEe;!SYnyL!Sx{EYgI*zj_9!lToqAJS5a|Ms1VRc8W6!|ZRfh_7sLey1P)M^L_}+>b z*#ox0amw$`Xr6k*n>%${&xD8pOqF5 z`bl^txtd}wS5w5;J%x?kQ&>Pudh9kyppj4)B$5(GDI#y5%9Of!sE0DnpQG}_Yz8?C z6g1%ogwezU37|&;pvSCP4~p7GQa7gq<7)GRmAqgk$jO%kItmT4&@GW%Gw z6gs@PRKS)(Alm6_4mJf(wB$>0c|-WyVjiHRi2h>X0^92pajST9W08mJ5%WM3``zV;Ibm70hVm4 zrP#^HmefgpjJjS}ZI#--Ja5}WT6tL89uv$fXxn3a=A;r&8nco%sa;}cU|5DGL|jxH z;+6>5KlNWzy-XOpm`<5hD)iVOIO$q4XJL@lD*P9#Pz9o9qUqGdXxgKiw}w|4?f+_U z+R}%U>{SX0fW7Qn2EZZqYN<}0Ppu^hGcFq32rK=YSP5h5P`ZTk5SMa_U4D3@6rqQ1 zuuv*I#2s(c8e*40rDMsq_fhxHId!js%KC?JkMP_y{1I1H52!_)rA{p+hPGO6Qb4LE ztM=Sdvk?})lA@XfE|c&IK6{;)!?T@-L-$0Us;GyWVs@7|bMA+W(F{Mt?J@Mv>No`8 zx-UH?3y1!N+@XKbpsT&1e<4wM8uX!mvBP;b+R@o?1m(RCPOSFn^EP-ERs=;vnJ+u_#8xtpaK>RI`0u7bS8xOhTsN^CqMbprEEGeJ7 zWDY~{0!+dz#Fs~1X?a)$O%i#`mSoXaH?2HFAT{^}Gb1MU*Uh6`88JoSJEr4HSsJ z3Da5|!cp@hst@#98$TA?l-*jBitkS7Lp-9)Z6fM+mC zht@D6s;MZHf%!!#?LqilKD^jgCVs3%%XEx!^NMlI#pCHMGDtV@Vx{2737LAW>1pX) zK;X-S*px+zJP=k&ldeeA;G=zu1TC}c1z2Vm1>&`|gsIG30JOmf1zl8AKX?JKO+gVz zuJi=X4waN@+3^NV(UG&wL#Oo}ATyJfirkx%|J5|t$Qxp!XUoYthsM<+l{8!NydiVK zXW>Yn^1lcu@hTED!XYm6xMiby4GSM+CY5=W0G3IS4 zdQ~ZSC3_c@6z_x!PKrx!=QAzufrPPltJJGB&GRzJFo{mOVG=RHbfz6S>+@j5ujw2) zE7YMxMP#NTabN7AGfY|8&CSD03G@>{7+Vy_!C}@O;ekB7GBLcq?zG6A2xRfnCNrcRA);Wc!TOzlCq(al{yd|$=#uxl+FdWp}`&B?OQ z^KFPts3Q&EpCo9wJ4+C3=<@OcfdKz$YRV)a4RWB>8qby5z>9e>_7^0cNBXRP##c${ zMM33C(;?dkh(P8~Ls-9Ibu3g)c2FeuB~ZQ-Va>o1!}C=9HhLo`i6&y2;445?^2+=2 zY(zyW&3{h9nG%&>E)To>%cClgZ#)L@L`d6sj5KRdg*D8jMr=J2aV(umftW~9AWfu* z`b3JDs>&kJ;NUUIhuu@{l~cLRKuom6;ccZ;C^;ljOc<_q@kuHDQ;%yYu~4d(vnBgP zKw`aEts=;GCK?OnJS_4XWg^elPZL&EarAm#T{Ktqe26a7vOSN)s}$BVbuPUUVNuZs z`Xv@gd$kc&S&c_J1DlE{?=~MZn@~kmJZ%$+1H7Z3l_s8Lh!=#fqm+2X&x9s1D`ZMN zNF{s*9a`#JCXyeA;;jSH%;9MP2?#k157`?LRvPYC6pn6=*k^xHZE2iTll&3ibJx>a z)A2{~;z*B?lXV=-tYwpP~8Qm<&`D{HIo`_N+t~y}zqi>SE$Mr1Fbzp!ZIvo34EfeWlPH*^bch6TAvchRLQqZUv_gp` zX%PJjo{xAg4I<&cIlehT+R!VVscNC4ovAuk#c(Co2VhdSxxDJhSD8zso+{E9FKtmT zk;Lg~yF;={&1r0#VdmAA7(R3+wTlYaQX!PFEBs@o@Zfq4RuWysFtcdO`9TF(|5k?G zJaLtj4s+pEl62eDkUoyNX&8C{Br zwVvG^6H<#S@j)e?7`&1eTwh6~1>bAzOVp}lUma)Tr6TKXCgN4fyBo8NSx0M9^2BI$ zb;!>I$#CV{;U&9wi_LPkdn264^K2eBBXAztJElQol?}{D3%wZeq!%OVdNE>xDSlip z*c@Pi)6#Qbp3FKeIWJAh($+>v9az|vz6F`Ld@sNTgE{@|gA#iNPDe_r(Zo>Y5+Yo^ zQo0zUTq=Rn`9@n%lv=Q**|U%E=7CvA5md{jCsWQGv*9PI=Y~r&>IzcOF;&-x${n6K zQ@M6>D1u!EIu*1`m1VgW1yHhiM1z<_s}&7rag-Ikw)qPIks_MalO%hsZ?%$U3PiQG zwZN(l>gz`W zO|DJZKTk#F4BS7@D$YZH=~SG&&vaA}j3xIz9+NV#U{cnx4rYonX;MbiCuPJ`+l^Iq z!mhWo?$8aPWoCq4*-76`(plgLy`P6Z$U`6Ip^x&=FZ0mHdFYco^l2VCn}(2UlW<{{kl_U$OG0VA{` z53SBa=8!ii>+-aF@=%|JL|a)o=KhzQ4`^HR(1UsCkvz055ABH1U*^~G|D|8YFWmp~ zZAm8k=*eHcV*Lk6q+cyUx=Gz1fw|E!EPA~5Ll$` z1jpczC&Hm^lF&1x14hEo5jFw24b$Xl;b@HT;IU*05FjZe5jIIA5QHCr1j|g5U9wAd z$qc>A%#t2@mU))$p?jHKdWYSmC;Ptdsj7SLCkb%kkxId!V))TvXaPW{|B z8_9Vu;~tuCo9CePfanm^tUZvcWdkb~!jePxki-NB>_{PbHQ7OrC z*-WN*1yRRb6nE3b@z=v$b8-Inq6=SfzgO`~Z5rQq;ahIX$a6Ayrw9jGv=UJOk0$zG zsIi!@5YmH+pb*T*J}vI8pw8Y>4i8@{-4Ied1RjH&H!Ko0;Hf)%9EmJ)4Z{yja5a(N z0Z4?Qrrg-kLC)54-&Xg@HasM*b33+(5-OnVbq7=5yn$4PxlmvO{|&CI3W&90V&S;4 z_rP@_i01?(?nQG21YZfkl9)VPaqkr3!kZuRIGh&HYQ&d{!%gT}nlC*aQLngnvZ`sn z&;&yqgrMeSQfN}aBz_?h!pb3ooK7l>)WS(zGcY~wEpwcd_;S7DJx^(lb42HnUEcxX zE5@4*h&~`pDylE^8KHZtjivlA$Ruu>O?1X)w3E94`b=dq0b_$(K5y!TGhgsN<6>Ia zQLr=Fll}I3Vhg|1paUwXBN)xbQ)^k=`#5sFd4+Mj(D|~p0wE5t-nkxS8Bjv$Z{Z^A z32-XZCj|YAs;#ByMhFX3#+RZ`9)vPeA3$i~0uOCZa7Zs3p4C-GSyh>Za;XqPZnW^= zAR2_XHJ#+p8=P^kS#(Bx&DSt)I_{qL6}nov+`)&I*;1?!ZyjI;Z2^<81W_>VZG^CC z34*@2$uUN*w>kK>249POo_cd!hwz*t6lFbp%nmw?;&ABLgSj(Mcy>wFy+>wYAQ{aZQ^(R-0&jhzN1TS&oR0GLe*k_kiTFrXQG*kaKNhS)Fq#rP6y zx*qrl(wba>?dki@EqL6VwR1(dVM!Ec;9r12BtOz=M6!X{!m`O^#K_-vlnzkf0R;ZJ zXjX>Fyd-YPq7-Q3LZXcr^qf0$*m90>WD9!0y|n9w-gAAT8d;y5QJ*LRU}+O5<3h)= zYMw%gK}{fl%FyF*njY*r$ShRFv~-Wgc!q;09Iuui4c2eoZE?k+*<6jtZ`Z}mOFe?( zk9;CpoY93G3>b6(baYWP&Db9SEWbA4NuzLzUBUZ`3hc@*!SsUCt%qDXD3S>Sg>fgq z9f-eU9U>qd^U)AFl%gyu4NhY~fAc!v05=32{-!xvYsGWyYrFuhvx9}8zn_hil-VpK zr;H>a&KpTWL@<(s7>P(%eT>#K{th>*gg@6gL-6pVxV}uI?jo1>Ii;dcubt>Jx46Hr z9K)FDoW(r~9^vk*@Pf6kGWf#x(faKBXgvnTmi?rQ@9}~975D@7( zKAyg8Jn@nx)A3%Aw)7Ppo=WaeuP>yXy44(VGIsIxfumIU8Y`Opw7 z6I)N?ceVRP*uOfcUo>>b01NM{HAMk0q2h6K2pDH$84lyi?-0SuxH`t=zU zP=*WEw8m}Bh-IQYBz>PY+rc9aGbKUXch!n+Jq>VhXzOWlXcDwBdzwaEPC52QHyE6v zEeToX9r~PN+;;;wS78+Jxp-_)5@Xz_#c3aJ4aWN;xkYmMnr&d7)4o0IDv@n}sh}o% zC^U4(fQM2*=@n4$RC#``tC_g|3zqTI`JIR&sXt+za?TZ+Fb_Xgbq>X-u*>$9!a z%F*RHr%RPOFkY||xLhZ>$^H01PAlK~3I5X5&`_fCOmW_QU(i;AL6CG?ht{RZS=1PVq)yLp2UY|a>vm{v$dF&kcGM-&q6<4CCHfo2DD zU@Q2|v25-@i|d>NJg7ym7C*0jv(EFS8s%AE0ntVbbf8CmvCaW^v9f!yhJrmd-MtvE zP78)wz78Ld84k1{PQ{D66pRl*k+-fzO}cRd(1el#LO~%0QXgnG3h4JE3@h~#RN5!V z65^sA1L!Yf3m3LbdeeO-M;F+Uim^TvLh6YnFe~U7?*kNl$bnyds{)9zcmr`n-j0gk zYqt3YWus*##{9OLbcnB>opqsC6xM0*^qwmdH)3Y%j8lE zA=!9#mZM{Z&F$4ZcHD z->T2OwZ;uA`IR_VZjJ93m8PhLXb@jMw8}~n^)K~B11yo83MfylYf{G&*QAamieG6I z^dIwTEp^K(d^X0k9(e!+wrDN&L;o=RM%8vV{@P70w!6`yUvBD^U>QW~6UQ0{Bvdya z098^Uj7h8|(lI*OH9|hOa7pFm`5|tirlMrFPlc)Kb7o6LmE^UFy&kISReUq z71XXG*-8n13@lN`r=>PA0*o_!gs^R5grIM{M}ch&YkVrg^BEYqy+M_~N%x?J%|Q*A zgT;y&2R1;R`RN^ecpHL6-yi3Ip+^Ho@$o?ou!9;}2g@*q+k)cW78LikU{`7j8whP- z1EDQ!Ahd-IgtoAO&=xij4widW|KQ}{n-YAsh(c#jBItd0nDAQMy8O?ay;2mqX%yXf<)Ww-pi6JiikVTWPT@ zXJsg-Aze;GD2MuOD5oJQry)~LL#~a&okF-FDaXaK9CyH&^w0Y$W()TnAq0?+>Lda! zz<>`4z`mOG#V_dJ8u(TIVHoAzaS>zOrhQ+>if^`^b$4*Djg@z(R5>%jGklecy; zLOi9|mFn5`AO;N2ZItV@$H=1;cb2_vJ>xhi=>arHDSyeC>(!IYt1OLJ>rBL}lDN%N zf=?yEU(DAuA1Yvsym%1Y19J%tvO+Usuj2=Vk}A!C(3lQ1sXe((RI}eG;Aty#U;93m z-M)_^^+)E>JaN@lPkR%uFx8120ZPzWV?~{xAS4fZ@v{`^WZ8sc;-k_ew?xP23pp3rQ zO_Vn*ER@W%(dA`P2#5IV#N1wL7~4ySiJ^9?K2F-S>jt@i+`JkRfI{PM5#?y=Npv>OVeyJQt?FG7&l2sO_@5k)t;h)dpc-Ff^h*F2G3 z-#if$5;fN*s0E;FJ47xNBkB$9rCbEeMjCL)gwl4&&0e%zL+|-g>m}& ziX{@?cz3$hoqof@38N7I$V%St`^<+^3(WxIrveY-y3GeYH-)fUXombK64j+*a1gYU zVsP*Vnh_to1=NTKy^d3>Me9+w;P-~c5Wzf$qLdQV9xPax_}gU_ImA_e1}74zxwsM# z^0xg{lrXd7K)22Jst@o3+6oiug(CrOqd|(#A(@2^$%J{R(#A^T&vN#aokR24M~z45 zZu$$MUMja%E*A>%Au5^ip^a8#;}PnAXhY)>M9}WL=`Sp0(_g4E_yN2WD-lu_HByux zinL17?t|C&Kuq}vj+dPoM)iSx0k{b^GwGFWjowg|@{tW)bD@}c0Bvrvq_%TJDK|bO ztH&X3Za{IC6%Uoi8Bb~vJnTu(S65r9uda5oguLe=zHV9lmFpdQsC06hu$@XI4>nAs zTHgputM*7&7vv5}tAtie4?AkdVJ@=dnHZK^hfWjINnkoeOPCb^oo#WF&NZsCJ^eM^xup08-FQR`~ zjTA5-O;$gw4mKJ^8=B@m2xZijKsxs2X+mLBBWcGmK5cRRM*d)2lJbhlFs5DCe} z%|0QCe@GLJa2`hbYD90uFFI%YMwJ2KZqSE``m2%&wUBr|KF zjRm}PpsY|yXlVwLUcje^?X;;zf#Ue}1E$#|(st2fqQ}Ku_i^-~jp)uDls4#!9g>SX z<^Z3Jr#pffbw{{@syNOxxgAT8>+8q`_R%!FgBw4Xk3nC2rM?WQ{+DG`Ap8J+2k;AX zlYSW{yWqF`hD#@VFnkFFLC<4R&Ot{LR`Av7R?stJ7`9YOD=|x%yAySPu^2j#?$b)9GTm3l_q?g z7A`9*D|WC~9PAY`+m0(Jl+3l`igm`0E4hxyAUp5`A!cbt5R^ePB$jEW5E07AQBES2 zmHBV2m6hm`U37^AdkG28ieFQa*AQZJ_{&POSvxOTszaJWN0N9B$(@8QvMo_C42Of0~N)Q>&p z;Uj@&%g0cD2cPBK@_>Be1o+~36@>HzzIc463Q~=gkNsyx9M=Su1;)^e+MB5US3chVRBV80f)&Wgt z*w}}7T`=DAh;JSMJDR1ukrz8Ek(b=rQHe}-E>>Sv>jw( z^s0S)xQXJMRk*-zQeSq=Q53q8pQ7LoGGOfTv+C#zyxz=hO)_!Qh2*qs(-}Y=KJ3rW zDh+s4p~dEjI~Jh~Dx>iEG)IDt9+3b)Dm>S*(ZU-^64sVDNCCf#)1h4)1T_K{1L`3_ zHQ*N<+_A=mH@aU<+Z{bFmC)e&4*r6#70+Mh<}Y*ccQN|(33eh*dSeG2ht^ns3nWQA zsDQj!LxX%F9l$njJ2t|r9?@o3M?=cLG3Dp0NJdmtKV)q>Ds}Tf>o3A7>JNgrU$5N4RX)0zt8c#OiFG#wh-_T;VQWulTYg}I}W#=$rDHw&H zICnKzYM|`4^$t{@#U|)_fv)$U>yrjJSdbaw1$~Y6NivktzB7J9gMM^`4rzx?Csj(^ z)_@}A+33n{G*)W#wQckOjVNaUtB+su1AiH2(-~ebve-c44u7s=31T=%m~Na`2EmXD z?jcy)8Ub@e@FKiFTzy?b2&_bpBNb%$6^RylfX68)!mrcvhFh-0l~P>8>^=XEbEp(W z5_+xWI{e&Oklz^)5s{r8?bxEz8w08&~$^f`5oG<#(jKd!2kOkzLNN*Xf>R!;NqIwR@)v+xY@-DcF^sg~wCC^jiYxasN#w%a7{7$Q3-(e6u19s6uFsE{FRdWYU*V}IH=$0eTkP?x-i{ex1}Px#f@F5$U`5!`XzUnuOj5q#k~X2&fbw-fiXa0L=LxhU9G z;ne?KQbCW@qKKWvcJygML9R<{rz70lJ<{Mcj3euVFMOW(NTb&}xJ+qd!`z~oT&Fm< zLtK*ENwtYHms+#4%vz;i#+FI*P9?4?$nBiW$pVQugk>bgcDd~%Igd@I#lVH$+quNy zbM+QJ2 zRS@i3nCm)6@|aIg7&@4oh z5=ZxBhpc-tU@?$(UQ`0DU-6L(R9L9i$Y%hJKT84dwPypM&j7YeO95O70Q9MvGz78e zBN#PWs2(}1`0H}HYEzn_oKE3fW7C{dveu`WTp`(>R$C--aH9lI63U2wQz`n^i7^_z& zhv7jKbW3;)c8+q?$i<5i#%xJf#z(2QsT<%CE$p=KQNHDkcw^JFwb`XF70P zJ#wj0#z!>)9PKqamE?~0D|R5&wyujd6uT~_hN8)+qo-ZXyDqxgy7)7~BAz)aJIT?D z;Aqvnxq}i8`OM?5T3SfiF3G)&nGZ~a=~;}jqd$9f2)qM@4%P^^F9Ay5B=R7rK2{gI zYK-G{)%ae3NBz0WIzJrO6tHir0;9|WP26vBQJEs3CXNCDVxOh|UhR1$W(G3f~E zws?p^^_jxz!SdX_(0gjYeqJnz@4K#~8HH0NgefC!7%3oJ)p8#Pw40%FyLw!|^k5;P zNcJ{Pjql<*BKXsZc%LOC%1?BZlI$d*k;wFDnAJxJ90e{QB!0mgyL!+Mkfx1Cq?+Le zVPwEW>OAfBiE!GbfqaZgbbL%h`&g~~8rsJ+a*u8FTO%d{jx_=zXXodSH3^#Mzf~#D zZQ)FC_jx6@qugT}9y+&dLn4)->!Qc60mQm$yg7~GSg){f?D`_Q*d=Idms2=*^}tJR zl*c%<>k72GCZU!p+_FkpPvr#r z_*6TiW@*UhI85uTc%Y0OH&8BjvfRpq^L92Xrb@2H(ZJ*K&yhP7xJjj9U`SGnq2hrKMRv;F zBbS3;vr8z;?+}Lw!S%}>l6yw(S-HHe4UvQHakFb~0JB{xfa;*C0~ZB-S?(3NSLI%l z3sqCZ9`LiFgTI-QhZc3bm>G_b8&<5TxW~sUd4k+Bx#e=bD%4%nM z{yB2z%AGG)EOLB_{I#yWwC#(&M(*P)m0B-X3~+qC{2S#q%59dr6>e_V1^IdR4@0u+ zie`TgPlA1RT{bq-FJ;!{^bKzTSnkW{A!*`16H#7HZh9*kF(}=Tf`4~i2KmsI zU6-ACWY=ZK!190^#!((6e!H=%lnqt(em-aEs_8`OJ)%`@|7XtDz{zS3d zQn};hQhUL6vzz{?h39r$4cPb@`I{9S@y z)|;TxI!@H8l$D+%f2LqPtJe^!N1qDCEgE;S;)Iy<1TAB{x!SeVc{+7&+Qn&FWpbxj z{6v$GH=`mL_LB;5`c%74UIuhkAO)PzRlzF0QIOjmbVSBZcBx#IDa$4;2DNH7pRWT4 zZ*Ge&XD-0<M6UXA|1fH6A$dTUQWwjZ^#Zi|S$< zyK9`Z07YZ3R=@a4qb_VA+lN?muJanJG=Pwnobov;bX7Ue?n|K`x~hVA_a)89Dp(aC zLh%?gU2{^>v#UZ$r;UJd*IarQpfYnY@rQZYcNSF$&f(T|n6VsPGtoHmqmiG2biB9w zx(YilSCan(#~7#j-M75w25vN=;M|GJ306lwD$A~T7vrZCH4!of=UtK~lK2yS<72j{q; zX?=rplgeTzM;YeJt&zJ>?jjF_fvHDE;z6v>t&xaoYZTN{xwUfZ)5%SWS|fM8T#1vx z#u1rjuzVQPxVCxi$pyCxRZHOUPVEQQ6~>+Q{v|K-f?iMFV_qaccGBG1y#)V+p;PW2 zxjk|w<%~5?yfc7IO|2$sH%RG*5+Ne(OJ{ z!nr(Uf~Fz_)jBtLOF|02Sh+H&c>L-Frc%MSE^8$cX}XnhaW)#&WYGBKx)(o&tn0iK zd9=^QP}g~w*;66aKs-1k9vl)64lPprV!2D?irXe_?MCdb z$%w=Jg4(gGhC1AU80rvgkKBF94BK_yAmg2l1__T~4nr6Z4fy#%KE@1%82T{MX*HmD z=sKVKForX&4A}O~!Qx`EAV(&wJ(yGmO1TVx>Tgu^IDnj+6}}wRnVR5vx2e^2o_&Cz z^$F4~%syGosv({ig^lTkxPLZe&-BrE&RfTa&IjKG`JgAPM)hW6N^;)SoSl3u@9WGi z;3qc;g$?v#@S*c6tmo}?&I67zK6wvp`Fbq6o1eHs{f77|9`ccn_5nv%94Se!9-c~4 z^!-J~XY#OKs0Z`Vo65tW^3cP#UHL&F`o*V7nZ;<96)YtS-K~ZB|AYkMDLSszSLW-I&0p3*1{5!)MNTf+~UBGJn} zfj}n#(5MA$6n-I$)mugywwpu!p%yse* z+vF@&-PheR*F3HYH&BId*@hr}MSg3L9}E-2f2&wg=uO_0$6!G>(d7Y`Z;ex$32m?p zw-xQ7&7Y95KQJcO_-9y3mBo#JS%x8+ji9-KV2%IbUI(VbR7nWJKlSY=e6c*&`2V0N zobnPpgJ>ZuN~!&=jVKx`zIfB>_*+{M0iJlX1DyQY_f%s9NWwQew2)3B<8O8NT%F$2 zYh-TSlWV?d-u4C(PsnnuWD*cImDb9vIAET`qeGZiSSG;YZw)Y7XgulxkK|UWYVCFj zSh;S+-#VSCVtWH(9D4By`IAOYwUjCHoI|BBUbp;+t=#fsxjey|+ro>Nxh;QYY_a7> zPP90{A6Z+r@OK%`@BYkd%UlySDnzrjgFCs%< zYmNd@_AHL~EDfRN2q6^wb-8vXb1jc9eklD-qYw2G@}-fvsEMxf5B0bnNsq8e3r-V_0TW|A>z zI%5Ft@xZWb%?5DmuzuXrDZ=3p*Z_nle=a^MxW+1=|dwPBS3*qDwoNK}ZS(sfY&zl<3_m`U*^i3bF`kT_% z>;WcpGzY+sRzaS|-^Rknsr(-*{Lnq<6Nm(OFu8ntX_15jLM*$-rp|AlcE4xw3wiSG z3lbq=bB$TRd&d=vbm#N)s>QoqwtU!cpI0uH=AH}w_H|bp-*RRAP4|1te!mmpmt*Cd ze}>fW(29UEzY`vyKG`gcuCd>F9Lm}Q1!Vx8$WPIM58yH*^#W9pf=Nu%bb+zWu8^(n zw*|jop#>17ETzM0%PzS3p#mPMZ@OS9ok}6h9$)@9i+N~)SV=fkND)`T1DNNjcYQZJ zSMlBOT*Y_8a~0pU=PEFZK49c6DM`3Y<2C;7R>(c*7q7LOa{1j(t2V^mYgbV)eqVhE zzhLuMA7X)reJ#H`5PU4P8`JW*dXqnn@;aZgh%(>xW|k5 z9`+#g5^V@S;C?Y4yLl3ts)XO#wIVBkTRKn$m{CH;r`rOwg8kl|NRRG{xMv){&5#C0 z6lmSt!qQx&d&c=;RT%e_f)G*~TH(`)-dt;`2ZX^%<0y%F94$bNH*OEYCHQ%&lVJp5aV_^_2mxNFOG;<>0FpWZ@ZEj5GB`jksr%VFo zWeeU=HfrD=MclK|*I}b0uYnsUY+i4ipkHuCYgsn4m$E&L_N^Mw0Y@yPnqejr6Lp{0 zbHgN2Dyd|o$l?jEQ_#y<@QI6dFkESprr(tvq3mr=QS99tO7o0(oh^&FgVQlySs^j~W6m0U^p4P)zEf7arJgK0gat ze2U|X;~t(w01s+>ZC>vO)n=Mna+`5l*AK1D>j9f;&=nwc`B(`jv~XJ9h9%?1;8hCz z=$l)5N1V>(TQ>%vw0^x$fKGmSvlRxV?dn-TY%P zsVr|NWsJhowguC$%3()Crnb0mZ*5*LegJ1t;H^h|LhQXTUVt}%5KZ8&nQJsY!Q~@? zjLpLv`mPy;@!n)Q0ypk0O5rPVwccCgxNpAOi)+4&!|xW?;ohA=1jdJne4JCoq##b- zTw|P!Zj>wm`vniaH6Ws)pU|yza~+3M!@HR_3qfERuz9|(%#8e&S&@m-9}jv?hzi;YkBR;CKA@~DER z+Q&Q&LZEf2mdy!rVe|ZW?;L!gL9NGPJH_@s`&^R%Ei_lGaywdWi}_*tk#fa*36FZW z3;`aDL7_@nz_y1TLDKAyT^UZi~V;Xq#P4(){Y}rNe-XCBtMh7y4{-^^xa z^O6;~LZ}%Pzs*C#TDZq2olbFJ&oD4qxKCoHR0~^6)6{D%g%E|BOuY-+3mC`#H?*L& zG~VmZsnbg17w&uQ{so8vnQ|s6)F$S=XV5G0N!E~L)n_6?|F+z)Litq(7TR*d|As58)hVp!tl4sL~%xYxKlmfBa25hV+mO9d(NV#x9^M(ix-oNOqh)24Ib zfFfx~OPd2hj1A!(oJ3G7{l2}#)-hb16&r`QK&cl|UTY1Fd#yEo8~ZVeesyAqesyB} zcEb1s+Ui-S7=G8}w_7~IH$u+W-iY7khROn5f-dq3$Cevbv)7AA+t-T{y1~Uep94qxq_j z=r`iL&J_<-tk=gC`ji1NLS_y3w$#G(x@~U!_8Dt#v*-A?!H_^zVppVR!X$CYn!mkP z#t_|{Mjf`-vViTiDau}P6xe<^P+Z`a|~thksV?$kAoLo9E&8d!?3+D^#ld_a@-iW86y9 z$M;ySG3VAfcB30z#rNP41f=sqAamcqg^-3MebI3%ZzMTbE!wxv`Cxcn87PnhG0PNF z$)RxJn=o7smAJFZsTe*SNcAxl^+7K2Qel+)y?&7q#G;P>MlBj39o+fVhkzn!07HC& z(O!w|Whty35BbnILTN5lHfZT1Ak)5A0wgl#wyrnAUP5V5WMIQm|KS_PtT=j7Ulc|E zyg?f%KBg^e)c3>NsNbLBdE)zKVnSa~OgHi+8MrFtLj}2i%G>hapD$o6#;{QfT|Ia5 zEQ-bN_uK9F0e+_vQoRw+V{E;tUsNEsmD}`)3w}QAr+DPW*Kq>@jXcA?3JdnbDx*IA zVMJ~AaS6AnOEthZDD{h$ZXH5Z@i8`<66rIm6@6zy+*$chI^f^*lKIWX%mj`-h98^@ z0}F{iI0u@lK{(+Zv@Tkjkot$uToz3us!aBDS z(52r}`psM?J)Go!z`Y^P=AAc`blp5k;<->2h_3Id$g~H+AKbDgcU6EK__muP5x(u_ zeZ|2SHidY*jvweUki^GNbukWXrwe#k_Q_(3j>ce92&MIM$n%%o43n_`78neY0J?$5 zLL7HWV*SUrBfh%@!2vNGR*@i>pnY%$n?j`D53GOGL~z<7}st+?pLc`8Ff|+ zM>LJDVar*4BnzMG;#QZ_cRd<@4%6tJ%1Y1>?-s-S*cSn@b`8DUf)9i(OhLMG7A62U z;@(9`2;fUHq1sGnCAflSlySJDVYNbGac=_$a?3ce?gjLfBLpjwR9&HD?ZLFrR-`4B zB-;^dW2THovQwB{@XapIPFsVoMLu;ImSFbJn6c$B+2bO*3s^MQ@B>irfs6DxBC}4( z3rKw5Mhu1T+t_cp`(=j%13qWtO2_AHye}Bi@i`kGwo}6yjSh$yh@w7Ce3H-Ed7jjec+f0xKZjOW5P;YszLX%8$V!sIMuNy`wpk0l~gR zd=NMZu`XMOLAQXEtNFmaM+$-Q*4YHig8K9ey|Qk0t>~0icI%bqgO2ZyfWJ7 z*ChHZ3}9M2ZKa@9xc~4;YbVObs@#Rml+xOXup#d2b=H@@US8y3VXAjuuVy!W>BP&s z*3L}z5$rom<_9{@>6n9DNvV;WM8-FFYOjZ1BV82PWv_ zCnSS_RD?f|(bsE|zyo?o;KTT^j81MTlYJV7QV!WXce7p>Qq;VX@?(yZP;5IFz}wE@ zyDz>O$rx$975Dl1FL?Gx846(Ib-%ZB>@OOYRP3Q8bkfkEGRcgJBIJ2BqvUc%1^g7e$3*MU7&3N(baHz_YxR0=kqcW()_ zp2pcsL$Y~31Z1M~(q_d!<9=#A2>4vLXpIo2)?03@;2Ry?mkblV)a5zU&w|=CqO?u$ z?1Wf+fk%CS4pqAxjQ>bd+XQk_S}-V2ynk|rY$Vd;43i0&S3+A)2QE^4ii;FRW^s`v z0$G?o5yLL2FuS*#lq^p&L%FJZGj#@Pe`N+4D_ZHeuJ!Ru63a=}!(@$A=y8M`X?6@? zmZ6dZ+Id3GhEZj)L)$&Udlvg-R5(3-xJJ!QidX)4Ka>P1cHN?S_k=Jix+fqp2`xql zmC(b3_7M#n?A?-;N+@vu+;jyFC&jr6%+JK^IE-%>WnxH?noJB~YAAoi=AlBMqO}lkeFl(aq+%Q))(6zxOOTF%CoL3mw0nt2#6780$d`n!H3C+WZM!QI zG?n;T5>_Ez3Kk|(Xn$(%b%aSxmxy6ovN5c5d!`wL(SDc76*RLmlg=F37?QHn$qan- z=(MOxPf}-e`ko|&PT%7>uWdpGJI3e!eE|~s#D1O=33A5^KE9|i)2!xxy50}b&nkAErX30NF8TO~FWapy`VViexe*z0UhK z7sBtGk(yBVr~Ma)vBl*~4B0RoDgweQsRcoIVpyi|yQ(=4XB*kK6Y;gL6lm)$$a6oG zki@~(dW+1-QwOcL5>qBIX_eZ_c}gfRXHpN)iYErdNb6o4NDPU0pg2=^d{s+d6njXr z7Tls~j{KuZE>%6d9)0o(W>Cm!lL%eC5FLQgQWbL;gp%jlH?UN8YEFPncv`9Pf$`yZ z1!2wZ$i+|?9o=PK_Ha`(*Ikx0r7S*>h;sDl0gCtdA6aR?AJTG@~SvbhMCnYC)%7D{j279XHNGIST}?{zcQrNZ&l0m@FEA~=9E zF6OCD=#P;>`nMwqDhxg|X1dIQ7FEE1Bke$omn#Q)P&M?_ffkNyz_ZE+T7U!?*gPF$ zM&0$HvIYEga_0aZlXKz(4LQ(anuB!=5M(;$KudgJpRa!+09n&sCp>__x0cXc5X2{O z_05V{t!4$B7ChiyA;LW={tKyM(D*SM-xc~KJgdrbsi)9^&V4og2x zcOZFNl!+}*Wt@`o^H5cM@|K;dV0Y`-w;!`8rOGd_HXWym3o^B*HJ|py$r>jG5%;Iq zOL-9E`vqZM5AucJ9!;hjAjbMr+LNG%#WW$tH=_`KPoQi@qM}Aa>sfzI3ce;)d`H&i zhhYCg$F3bI`P{!am4B(9y%O!GV@cc}Yz83Q%0PdZ?fci`B;v;kxH+wM)0E%u*UzrHeS|*Hj0KJ<`JX;J6Hk)=sBF{t3QB9^%0_wVX8u-_-KHDZyu!9JKu4T-bJ; zR601{duot^<3KIEh6V_KdiP*`sxI}$>N}eK_K+AjKDag15n9N>u>XhJx1@3>8XY?0 z;4MeygEvCvgE#O*d=~YvFgGaCb?taK(fHX9f|5jY3Q_T)QXBr;d)yh4?LAsN;&t9o zDyn>FilgJ9DS5?D$$)w3eyAc3tRe$e>9JMjfrVooCGjEZ)bXiQPwVOV_|&+BVuzOI z<*yyFjrDm@^{5QqJ08|hY!4ptX0$^*p7?d_pEmirZsgPe<4p^Wy5Y;f6tM1tK?voQ z(36oLZH=*>lgG)^Ly5kP?bt(J!#b22)=6Ya)jD)F)#_{R+|s>C+z*!q0s}8zh&B=P z!=>RA(!HtJm0r!--jqVokU`O4M@6@9w7GCQ%z==#J|GH)B(I8XZ%UM{?M?P7^vC$l zIPyX?y!sVv?dTz7*j=3uPa<<9g4j4Pq(_Iry!yph+fgCz#5(}&4bCAOwdR z*tnOPfCDx^;+!>T$2rgrFY`KSgqL}}uRZuWJcaPItFv13oVOhp$NOyRldzI0iI3FU znBG<6j5fP!pbXooSliRwRU>U_R}BhBqf2snS_=9TAdO=3YjWLH1Gye{LjhoqK?x^D zD1$Z~>{=N3XV=2`2p^Vlq#=IlWvCGO!xxFLsieTAv(bp4hsEL7U)Zv0rSiGD%YGdWDlljwNOcpmI)lI+;GEge04eA(B=inp zSkdIIVT$7;`wV<%t$`~WhL+w7gp!WisA!)?1d!PcSIzF2f{qM0F#?bEF&+an!E%IO zEKCj^fD6eBTc!mT*fI@I1%?oj@pMvd=T+0>9p$xWc1qjye9J*T)bq$W+hjqF^voVK zt}T3#{q=iY#F6s?;ERFTa|?>FBHR;#Rs?r%XdaAD**;Eugj?7wiaSA;!HuLmgoJag zKZ4bCeB_EU@|i$v8oJKGGlMQAeTYJ!#%ZoH=&e8#_T@EC&g-axvz4%;6Sa(VyzZ(0 z6%U@M1Y5omAHn%nby@f1fV_KheB`F1*z4~n<-7epabt2~3CNEoidSPRLav0P$@I@? zuTLE<4ka);2@zA!=nLU@Rl=#y#bjY!u-%xUJUl>g)zBn=~E^iAIgSvGwmK8ibvRDrm$gn@RSU36zi zNHjU&Ziv#;G3X`UogW4m7#yM<`HrTH)sE%&gyK0qvI;eG?DOej>0TegV2qC@A+mI@ z7aWNVm(fXxSzv62ipu9h@o(7pb2hQAi%Bu4)EOUA70&vQ#(5GV_LP~{HSdClR^9|J zYnFtV_hO==AEmJd?<_+*%4EOnLn=B6F?%YsFg|*HSko}MP~X~B!>6tJPyl%%)%non zj@}CR5jqxSLditIv}8tr*f};Xfvky-m4*=0k`UPI7>$_mDcOEbRYKT7L)o&*rcSS< z>5b8J&6sbp!MLeAoeGuX*p&R%9;^0sAlZ*SZu%#l>)jRPr4oV>MWLmcOkTUTuO;m2 zfs$>a8_Pi8li}06prn?Hdb=XCNJJn$w#Li+)f@w$zRPL>ml*-SIS#H5!$w)+W9!{u z-L(|G$d20;>I!dsY@@HaLXDYFQ-aL~;jyimbe=rMTb{bz8lS3msyd$Eyom9H_tD_7 zxUP#XPM!>oUav0W;ZL=1B0#+s+;vgclDaM?%5c}k_}D-O!#Ov2k7WkTv1?dQV380~ zulr2uFjxyt~6&xr>T1&N505LCHTXs9C{B4lO2!s;a<9N12WP@z;B zLRFc3Nr*VBJ+T_SCyWo#>+z764Sn2GPset4Cv3L6F_l;^x~K5eBQfA%-GN7R+PywR zvoau`sJ45(Fr*UKBQbyy!`u*J#`z({v?Ro|noL>}V%ow?S`uQ~qL9YW;v@uWFU|@q zd#LnOwv9_OIlP@_AVn6S<*U7%j0S4)FAr%mh!CI3HoGA~irh&EON@aOxsbLoK$0X$ zh>c15jcG{;v&cZB8-@eT$!s)$A;qaa>rDE)^J;t?=_;!8RzTi)D?XkS z%=)Z!K#iLMP~)cfczp;#?>t`b2G>x2ml8t*yA(#h)Zqk1ug^3ctZ;l|CWbJL!(y64 zNxLrCz}pQ#IKsGB4LnRPXpL zGuoY6h+ne<(g#Zr9tzGH{1oB1ag$y_+VoRbolQSgLH^>U59G_Zjorb3n2xSryK&a$KU%J-~ z@f5V{c$@=`PxuY96Q-#w5jq&K_|9)~$4vo`X^;2xc+WoX=|#p(xHHEcBEHd!UkU== zwc*phwC`j207`}+05O+if5}w}uK{^TnD`8GfTVG>9r7N($gkkm9^bFxf@-%nzV5y* z>kIS;+(zDtV>SiRFL#m93kDq>GNr@r_zc?4{<9+Lo_n!r0RnTO39 zB)ic>)Y;=<*l)ScJ%|bQ?(B&Ni#*^t56A^uuJdfJGtnzL&&7kq-XO&%Id#5B1(kYL zuOuFv;QfX0d%V3KZ?Ev~%XRi973hrz%hFxRXM9y}zJ6==uh5lv@YnP3V0k=P;d*c| zZ-aVR*e{ei*ZZ2RS4|pno$CX}&h_!&;~@lPU~o<*G&d8P?;DCn*Mt;|`@x#5eS2`B zuVo@y2(~E0E!cb>ToTZ*@THkhB3hVMKP+!Ddoq1ZCU16LWJyV*3BA$x6GVkEQ=_P} zG1u7`5OpG3V_wVL)QOurZOS1}k^}VI>QQ%k)SW`zk?ZVC06XKs7ET(SEg=s34TGJE zYxm?!GB9yZ1~g%4rtMS4fdBywN>3Q)I=vKeK~2`3oZ7|6hfztq8e%UQ=Q=M54F{G| zhkRSk__mx;TTbUX&xE2n&%}e_6izYx!eK7+VIkJIEv3m0CRt$#8Z_asGNr3`Wpzsg z2XTal10<|g@EEaGaj0n6LS^t4UO!G2bc44>gc#=$j7u7FH6BVrU=wVxy0MokI36k; zX}yMG65*TzA8EY?*vxuulq!~a>a&w1@eYgn{khJ=Nu>|RLz9yb+McIxpO1&az%7i2 zQb)Un7W&%+*yQl`^U&ZVE*@IMtzrk^>9*K=mUz!n@2T}3uMZ5Rjv@`!+Y-lFMvD`n zGPK_Ni{l|mEArsbM(=4%rEU!^hBl|Sogtoa;nd}U__ulBMu6+Q5fANi(K~4$AVKy0 z7q|gxXLCFRt&uM~XO%BSWUsfLBis<+GROL8`*ZQWzM#5$QrT_#<4I-jJ0E-(#Ps=W2wdsAlD-uUE|kOAuH$z=6~W2`5yg)}T6PhR(JDTzZ3M$}fi9?Vsdj?+_&}Kmx6Doi?_f&ZZVOQc*MF=siGK82`8CGJ+eI97&uzaUv z0)wZj!8;Ez1+?Qr4R@!!j>0xS9zvLfPR$u6LkG6Tr{)G27CoPcWDN1WQIlyWmqZ~0 zxZuGZt$#h^M}M>Q zV=p|iW5Qoe+qeJZ2ll-0(FZPmi{>_(moc+(AUHBh%EUEqX6TUU?&u@I~;or%9w(n2te&=uhwD{hyd=J$?1xIl3 zfIDMd;^Xt87o&C2%hB4XE_w#PpT%!Ov@lv0Eswqi_XUJMgZNcZeN-ELE?OO}iCzM9 z19Fu-1@CJ7zYIufqX$%2#WZDp`)ui-4|^JEk55 zJRgm1KX}viX+((%TQ(VBYAJ^BkI8wQFZj-Bd%-MYYabH$rY7u?~vE``IBE&ug6l?Ln zaqpM#yB4Xd0QF+@rRSpGkDdeAJRr;mWkcC_ETkS#Yz$%2u}e@b>$vB@lj{IM4qb6~ z^?e4#E#bkirI`S|}6(I@doIch!%%cKrh~tcJ2J1U61c>d2a?wr38UrZ zxP9j)Sk%Cg^5(U?1~5gl>D&eINDO`#WXci!hv4Vkl2a(j)FK1(sj zO-M&`j--rZLI!>T@~uJi^ynX@<)F>66jnCro6u$bsGO|UtguY;Ii`Qa4UbKixa7S> z{d*|BPJe99dyM=!P|4I2BFFrdOb)+1QEVzAV$5H^IO6;scoNePC!!6%Tqll>mNj1V zUe?%i$1g?S{Q7#N?>T)FhWn)kNq&73wC{P16E1#FYn-5EjsM`iopHfa2f1j& zuaOp$>@15JjYH3Gi-onP#2AxjOEKRXabycOVWXL(IhG!FCm3y3-bM1pdsw4=${~z zdrlaZBe(*89NNVm?Rc|Zumlc!reeLhN)li-ypxszx)$*XQ`SSxuw~l1sFd~mI!a!p z8rMem)2cwr2=Tp$RTme3S?p6rLOBv(9&*@VR->F3@kh;PHG|7wU+}?l8XK02LR_BG zq`*aP0zV=^8bhh!Pc_dL+1C-*pSh#^%=2(to>W=}Unxx`$Ja%xt}%mqoPw@yX> zlMB8M-)H1ASzfXT1W(k&;->(+4kR2$BM2!;`p+5-=R9CEhS?B(9^f0m-)7I_U>jpm z#fzN482cq4H*xWvuFYLJDfA@^)oefJOTLieUkHn-$gAwTuz9%DoI` z1Ck<{{_guYc$17v#Or{Q>jA8DK90aDj5hKo<=Jv1kT8#o2wwbYF!yQ@;#qI8{bckg z{{KVdqQ!`dDVi-`0R|4suK^>+@>JkuRUbvclwscPx=wj%GkcM??}CEDf_n;z3hpgv zENTS%q$0)?ysx0R;Qa+-3x2C$T)_tlN(w$$P+AZdz?UAY`e|;m#j+7m_siF~C+Ebw_F=dv+ ztj2nMO{7~Bm4hGC_{&varWfS5GHHIcy@_xb@4M>}b?|aBPq%NmUieKa1&xA@XC}@DoS9xiooYuwV(LMD9SWl9S@wAr$jGi-N^tfdoozcIq4M~7NaVm; zD~m8&xGtK&)=+P-F-TLfC6r}RK8MCG$N$W;E}Fu99FAcUkPXQKe^&8pz(AAp0sWo` zjqJXI9yYnjN$~dA)Nd z&XmaMy&bbmP3L@L@XDa6eIln*wn*7@Cbrwr3aDm+2*G-|vykNYe0ab|nmR+?#gL)KAiE1imFC$r7E zXdHJpO`C^~$SD+?dGBj1YCJ!tyy#2xeSPWsK3=r2>*qf&dVl%*8;ib7-zOX2_kl!y zN@{AVlP?J>F6-n?3MHSOAVNZi+D)z{#qDqArQ;EN+ z*le7N*r~|HxOV|O{oMf1h#qEPEF`JKG!#A)h0j*eES`VU?jCgd-Gk0>)6{5_6IAv( zt&1=ba{i-i#Uje2sBM4exM+M{B7nw+&jDsanh%l@1Zn+>7{p8Knt;4?g`4b5FYH*6N?Z0y?no|4zB$b7YIQdEBug4Cr>PWIF-;f!C7r*b z^B?J4qx08v{)SEsAd42zd4|px=q#l3dvuLg#he6SCIw$B1 z(ix(2lFlhQ{|%kfbp8dMe@W+0>71eSBRc;ro&S!`Svo(a^WW3?ALyK;^AkG%Bc1<= z&UrdNrSq@o{A)TF==>R-|C!GJLgyl#e?#Zr()nNMT%z+cI)6^*-_f~D=P&3qW5cLu z3!T^L{4+XR>AXSbO*$=fTIsw+r;ScKoo#fs)7e3%gU(JmyXfqu(@6)PmWYbF>3oaM z9y;Hq^Bp?hrPD)aFP*pPyhCRno$t~4KAj)Xxkl%&>HH0y|AWqTI{%5z|4HZnqH}}J z-_rTN>HKFpH|hMpbpAg&x9I#Go&Q4T=X4_MoD~(&DWr1`9qy(T-AiW-osHO6OYJIy zb)Ej12-alv`g6#FaWptINJDCf#-{^QG5mNyqj)|vk_FgJpEMVqMHqS9SpO=fy&8ys zuR`it@_k=~fsra>11D=YCi zFvWADWWbs*spnH-5ONO>ntTat!ZH>C!54tyt6;M)Kz*A>f`|Wc7Kxe*=Lj=eL1ici zT^Br%s-Sb?aGoosTCxrk>KJ;~jB<*p0-$UCiqiOowl=OQX~LrsfOfm=fta)jAW^4n zY4RwhdFmFG(1R+Y$KcY$M=J$Q7*jE-)}a%BB}GnCk=4KMBCV~zwpLD8D`)*WTRB5T zlDxmRR$7rWM=8=oTO#10E!Z~06sksC%rrap>g7bRM@I%WC2@T7pv zkZ)(GgpX=UUQ5!p+jUWe-_o(!lRceob{4oYTZwd{Sy8BbkV$M05u1Ep1VfW8{SYc8 z`t&j9`=0_m%uu}uzqIw|gG@5x0qIL&RPW43lqi%oDOOUGMZ!e+aYB}2(I(Yup%>bLcXqYk zZe$hlUrGhf_FF-8|DDApc3ruFLqnpOjfL~0=~iS99hW~1V3TKQE?=juPFgI)pUJ3) z?TVD9{eddqc$+xC2%>ruFAl#ay;rUYkWDX$s?(b7KY|WRojI#oe-&)PnUlYq>uiP_ z|0Lut*GdaeD=wnuAvD%6n4SY_Y}C=@;&{U7C=s$96dm2tm1bE<-9PYC$*)GO9rIOi z$5*3|@_afMBEz>S#?$oPAdVu@ti$i~@SI*5P0_Z7Rz*YOqbK-?{AnSTK+|snxPypR zXIHcLS=nYkB1^E7VY2dApVNp+63Rw~w>nQ6Etl23gk8E2)Obeiy)PT|4H`2S>5HQ= zwcyb8QHh0oA~x_DyWG)a%ojnHh0&OoMYO`&`wQ#tH%8E?8qFDLJru^>IO+tX6F0oV z`ePl2I!(6AFmrQzdNugY)Z7=;7zzYREDCk*t7S?42=Z`hyH4%Dd8_@Z&)O9(UNS;R0z-?J|6t4iUb_lwA}C>r}2 z)Q3~aawm{RAl+@LCe=l|c*W%)z^5SB3!?`L$3IY5_Q0q;lEug+>#%j~UFHR)3JdV* z%c!s@DlCo)OQS+m;L|*@JaW>UsPIqjwyD`^&NMaFOE(x`)qfFvYg{mVWuW?3bHYr- zDd)C2$N?MlXPcY9Z(niy z+DyZ4bqrv~17&wR)mpe=5r;Bkk*EQKXkY>!N4gZT8z&_YdA% zbR3X&z>$0nwU$Q-{E>-r?_md1ujH_s2M*)B&KrB^=C#Pi@p~uGY90mgI|iokFG38L zZE0Q^t_#k}HaI(^*Xe>G)rkB(>D0B-{3I`MFtpPWz=KiNaX^tEkZ)=J}Ux>7CTbce-1tC2NVd;tPwv`e;QNjzJ6yysPUkcR?kSE2(gM}15zi$B*Tu%c=Fny@CS7_aG<4WIlR?*BXKxDDHrD4%Jq}*r?N(h^uVKj zOvRF{%HzfKbB&)qiSjF6=iJ`Z6BcF@$Y|L^S+*JYC4d~3#^$BbV=C^4>G=422vy2W6 zVt8b+xAZ+YoB)2{m?6*TbpM^eHbfJaxdG=5fL<>u<+6mrjSD7fc@wfUp#`z4F?q{hTNQC;N`wh#EN`Dm3?q}D zgFLcx>J(Zd0~Jk0(M1bCUr<<7{>_2EhVR}nb{Tr%=kI|<0%!5e8RGGu--SUK2No87 ze$4x9e_(uslXkjeeFGIP`YR%n*&HWDoDUv8lanHyGiFL;fxK zk*Z;*V5@n*W~MDW7uZ^LsqUH#53Rz*$k%-C?w&i*`J*D*7lh&A{I@ zom!-iNz;FG&=C|NRc1u@dlK=;C;cdj6`^<<{ocof)p!8(z6 zB^BOXCR{L;AkP!1**Gj>Xw>1H!)b^cf_xZa4M;^BCMJ*6V>Qg0nNx);75lEbo{g)D zif?`IKmDr(&oBIRQOD1J{$Es>n{Set2fk2}%|@EAG(RZewoSCFHfV`^pQW6>IdmH7 zpha+iu!yBtlWh(yXK)T303uCI$8#PG6k$1qQX7&92`b_1Cg&O_seh1shRv z$Z#=?J~c3O@Ni*rr0{Ontf{4OSiat2jr?m^-HyJJPZqP}!=SNG<0*2{XRnYyk)ET9AzUlm7hDIKEM_3~B@KvZTxv zrHoxCGX!m6xMeZiI%Ac)OO`BIs{u$W9J^m*o;E`>m0-Kr7A)-0v5BeV!Nj`&`FY)Z zRmS$q(f-2d2AHyJscEaRdo_T);SW`X=Lm||s5&d7GVV|(b;#Q6bHi;&sn-yP@dZ^zDGO8_eO>O9lPwfQXR8{%T11eEA|JC#>_$A)<$FJpp)&fiIL<&>a79| zK0Yf#tKx>yo-QcyhX!o>VVqqqHIpW+Y9{)f#cU)q+Wpx`uDjWJq@Ulps=PBuR(84P zE`0Y}>7c8f#3|$cqD^4=cVY4P1W!oV_6a)=mOqZu25hm=uFV+>oy?~;c?ORMX1Oz7 z8&&##x6@QLNaA5H&W7AsFcHT09%$KQO8M~dz5!=_6LIoUvs)S;Wt(@0No-V6GM45H zj%O5qC-Fbb2%9(Fo&pUMGj%DsKQUrs>{~?g6v;TFmJv{#^?Vqf<48wJdg93OWJ?2% zQ*kWXKClkJ@@Z4PUrCtpS%t#-_d%}{o6LGjkd$YfSM4MViTR)@F3%!4-+1c{Iqc90 z%w@D%D~4oUti&qxcOHD>XGj$hO&ZSDwCgt zD6%Ptn{aiv0WacS>HTcrYKN!>evZ_7oZ`3TGMCJsc~l2LlCwa#d=I;3Er33urOP+) zTai~6OXJbVPhfG<`0pQuGG|+&JdHU;3l>w~>;NUJ!HI>bi{R&BHp9U~`=5v&L}PBZ zon^3GLJ^juGuf}99zl%hCDjwCc@>}^#IlKxRq**8#?5qbWGsuBrIv6~t;Re=${ODp|AgV)U$t!yEqi09Y z&YSmqNZIb-e_mzs%qFjmegTE@#vd;Ueiab&uq@|oKS7U2kb{{8c$@i z`p-qrj1I)>h`$FEJc-T9-c6aKPR%1t*KT6ZcIlma_}%S-=tZqjA0<|4iBrJ3U9S`BORbFfRO}7n9605Hz0!NCU=~2jz)- zYJpUX8-A%vGasew;<+87qhi5zE)Uvmh(49p-yhNBMo~v?E_<5Uz~ z*InHX33`tSy7cW5XC(0jrC5&GlVJWjj)|joa5k%ujFdkC8wpdmZtGzyFGpB!x&O)s3ov{7a z(dtd(?o`j)w>sUAPogh)eu$e~;)g_PwJ^der=V zq{XOGIyGU8AO=U5ytfA)DK#%HFPJ?HGUiE)LVS_tNeoRId8j^69OJnW+R5_oHsF;w zTUk*D=P7eOeoIkXl1&)p4%!{Ad5wB^EF|O~!q2>~a+`~C!!Ey#wfa5_MQF0WdzLWq z)0#HY2TD^l;ChJ5V7u^pw-lL_B~U^+yWuv*ZmZ>AT1zgv@d9A#-W!87_i?hjk2bP; zr&dw>dI8~-jIbC#W^QKBbFc9W zn4E`28#HWXS!nC94ah-~k+)b4oDjJ?KqJ zYQcnI`4*#p`Dv*cu%lIup8N&0rY>@~36rt!b+JS{8etUvX6*fD>~To`X6)q&jbCK! zja>u@z$e%JHgh>f90nwgnP?DVndBFAjecpT1f6GzLL73X)^<3d>K_>%gO`N)hVX|9 z#uwaI@Y@9w3MQqd^kh~|5F~>?NzI-M$TtuqIVei(D@f(^&7sps=Wb5f5Y9E)`w}yQ zFtZxP1mS;zMTnHZ!x=n;v#9XCFRxv;=9!mXoVR{?-I|6~FTJ>E<=U5CS%+tpdBg|* zEhxYNlV|2FdV1}$)pf7DwDxNiU#eTX4uDVmQPtGPrcMWvlJOyN?y7Zb>X&VJ2C(Cq zt30HX3wl$1L9}VbGcSL(u3_p^FRfm^?8VxeRWH^}efs4WmoKVY*HBTu@a5<0S1td1 z-G(PBE2mdg^1rHT{miydW#z2O>56daGpEm-Q8jJGG(sw#KK?ycJ8i|XnU5{6T2@y( zdqw548Pn^ktClURT(PWfMQvqm-R!#R6}7XL)h(}HQT^D8=PPGCHfz@M>KRqbXV0EJ zeOle~(`P(i_t@-ZwXA*i@dS%`0>2=F0XVxvRtE#J>K5cpBvYB-&Q1Y}{ zRWp}YEw5ZYd&cr<%brJ()iY~nO`pDe*|J$Pr=z$P%W7xMsC}#ou`8z6*3Dco?XkL< zb<0*%qO93SnLcaU^6F*NrdQ3FHFH|^^vYSa%PVKkm{C<#I}`PsJp&gX3Zf4$ShalZ zOY2@*(NO->OKaD>w00Rg8P6XSMDJT$S6{bmU0oE-D2S&1%kPzs2s*X=$@=>8wX0rO z*|4sBZQZ)MwSQDsixbro3!(|?N8nXIma`+v7rpdSJyJhd5RIKVb=uU*sk17lp(ZRr z;oqsOu9{Z0qHfu;<)G1HGeD&kvz9GK{g*vowS30x%DUyXGiFvzUjZXoL3FQ5h@#&v zz()_sG?~H-r0&H>zW7`e-AjIUZkFXe-;15yh~bzR0qQ{$9)%bfANc>-dm9+Jsw-Wz zsxihS&_h=+F`=Cl37L@C?)0CHO$P#E<}xr&!XFme<} z-bwt4l~)W?Ds_odE0Qo2%F$IA$?dI*{_YsqoEaWP|Jj*FXksyIFxT zgLFXl^yW+yabGqoU;DlCa4E;q=E}s-qN_IN3Vo%$ zWg-u~We(`K*7hA`RAm*|?v;zx(y&Jo=1T@-Fxyb;68|Kl_+p@h%u+UeIr*1$X|Qm(NFCC)p8r^sxFXc(NYtoA6HCL)e2R9i5L0ykKtEDi?Cw_eqkuMh_(|HROX**}_Xzi2_TS-TF2ZweN(FsZ@vOM=CG zl)5RIXx*OqVX<_#SMVTVilwW%SlD;gkSS)gnAGII*C%DJkgMDK@;cuRsse8fD6skn~(yed~HS2FqhjRWFuZAxI=S;MdnTYbq7vH{NevZG`? z<^tupIkFjCr;j9)|5BGE3-Y;g<%Ugogoy!(T4NkpWg2MhO~GREnz|%d%-2pH2r=f* z1>>3Fhi=@42PVUCdlxj&-F_eq5a7#>k}+vOB_sRV2m`D%>WqV%%tSoMT7ZQBtwWSp zy`o$iK6pc2a0|w(<z`m9IG;>y#=@y&jyRdkU7C3 zit$#~B}?0e1LIhiS*RrvafB2W2bc-G8=*SU-!M&W(lsWe{2&|QT1QE%VT$IGILNG( z%bOe}6%AwX*41UN(l?&NI`$^$+agt)A`Pp*=^)?+II7)Qwmw@eb(T|f5%Mg?oLnF))$wcwOPwA3C)_fu^joj7@%-xAaWN&AJBE2x_ei;zuabS`k?~UT z2i|by&Kd&dOTKy-u4+tg-5$7ZD=(iPge`7Qv7D<+U|I5i*DbYh9}E~H6H8DEf5I&k zibV8kJhCTm^g*;~REq%NLW%%u>NaJuBmhj81BD6}A;Xp2KA5s%OmDKJnAtbxVB8T_ znLFxMdZR45u?P#qhQi)_t~|Q6S{bia>aBZL*M%Ke+j|v~DT%~)>K6Q>Z;{_u-j=D1 zin7f8+v)=NvTt21WQZlu>=8wZ6+``QU8rdv)GftrrQ+B)H%eL#Y`86jaR%E}7wol? zYsHLell|_xWxpoNZYd7JikYJ>%#Igx1sf|H;@Y|pmq>^qqDe6Qbs;E&=0+^<_CT6M zL4uvVj&j_pTelvzKE>2AMv^SopDRIP^%wUS^2JQH;Re9+pV^c5wq;6aiI+!r4}0Y@ z?2Ho9?R6V)gG59wGzK&Kyv@a|Y6$a3Uoz1%I8m;6V;xFYD|e8|x!f^WDHSmyCx#}* zy{!6iT{V}_dZk5Ot6+)7|LoqKb$~It$g--yyezS~tuAH0V3pgPDLs@$Cxx2A?Sajl zw{4(I@_XPh`OCU>EzB2(Gx_q8P5ejkhOyY%b3|B|P~u){lByqgKgb8MzN)#{mtV~c z+@Q(-w{A23s8XtePqHh@&@!=Ml^zoBq`Bd>k#>o7UF6(%vlDX6Iq<8|g?%kH{rJsf z;+9oCt2@`N-?(u_Z&!cMir#hI8&<5Q$GVNH`&O;)>Ri9NcQx;C(3&E|2?U|Hdv$Nm zZCz_t^snn(zoK{5`tB90H{7;<#j4(oU8}oRZP?hqa$OKYjH%VJa`a=~y{fNo-EFH; zW#6jK6}@ZvSFPyV*t2d$&-$K~>(*oUYSqgAV7(jbcJ^2O1|=~iL!Mc)m`w#Dz&h+g ztwOCURy7)orUb-nOw9p>=7C$Rc5XT}0A;M4;f%PH7D^ z;`PIW$;9`i*xR~?^vdPwvGQ=Sl+W#1o|cu|(JMsU^7ML`EvhB&j)GUMKp0zFszb$x zyuuxOZo4hBa(Lyct~EWqUgzpHk~HhqGZkMY+0@2rLtRSUY+;3-#FEFO zFA<#57(7hGk~zvgc)_VOh9+fU8DfV!>o)RBR`G!Xb1igeG}BYsSd1~{x6;;-go4du zkjf8n9UKPK3PURLvMc-RHZbi66&{j40TP4r{qP2>BO|#3zCL75Z5S~rGG+nN;KbM- zD5qgag8EQr>nQb0{Y@lQHwqZHbzNPE0Xsfx$YGEStl=KmQj}J6NkO<6h=%@2U9@iU zH*`yJd$rJ4$mA!=IpwGzKn>Id^>sfeiOUA8J|~n=%z1vGe_9vlH~c{5_}mUpo)Tt2 zFN^gKP0R{xjK!!QAj*$>r3w)9bp}>VE6sLvvCy%3kb6v4CzOI^YvCEQv6|1*z%1XH zeel6vwbBtZYnUtGk+D9X!z?7nsOrZJb?J1ozgc1c$Zs&4jAhisCNPU3RHdpe8y-Mt zRb7Nq{_;d3)tXIp3*YK*jhwIL$DyJSuL2~G7}2P$b>S}a!xeDI6h#ZA^8LBWXt8Q& zZ-I`XmiR%3tXu*`1_|PI#bd4kthfbZDQ%GfH1x14wvtxc+{xfHY?XB zaIQ~;S^aTuU2Oi?>JMpAT8C*J_UgjkFw`4i^j|<8zae)$fqa|^%pec5VAOPtiqcrq zL{=0-+)FW3+xA$mK{4vF2srpqZoI!b4tH8?Yxt-q$P;5V;NJudu22M+7&6HZW?T0* zViZWdL;@{-;o2IMeV{JOd?f%}Zpo;re!?Ebe(p+7G?2T2oo)p6o9>wmZmtf=iv)#9>HdsIh$z6jqk8nc- zP!8Gz`5S2i4!ZS{_kvmCaJ5DKT?Z7dS z8Wa;<+A!4U(P81bqb^r>1*k#NPk|o5y4TB;hP(s*!Hs!xm02pOQ%lAjaoO4p2QV{M zl-FBdd7>~(D{<_@%mlgnh?n}Rl~Jt4X#xOK(FUjANMKbEXPEeUO03DFhDnU2iRA^< zS1x;Fd-4^rW4G2xu`>VP8u_ z7JhDIqd1Y#hio)hq*eOh!$fEE7|@!!ax7}Wz{UA7P3xEe5JT|;lNw(#OR21>CUFfh zgo_V2&F>l7*>p%h1{g!E_yA((CxRCY1sY0S4SNKc%4|u}5P%q&p}DLL&qQ+V$d=&s zB@er@m0X6LaQ5NAU#YC8quW+W-1PZguG0S&1B)10W)XaTYlv zcdfF!N2nqUzmcSAzf~#-CHUSz)?m@#y8sn_y7Tc@_fHm1VQ5ib+@RcFZ5VLUy-S+W z%xPM=MP%nQ_;PBl73_I|#M}{K?rNSFgr65kz{Yx8Sm0Ub3M0i`Zz3^w9oVe-a+(9? z1*b9+^9Pf;!X{Rwnb&sIB=&*qWox&2C72t>?(3F&GkX2dqm*YS+E(SEBn%1Psb8_*!$MaZ@Mp2w6RL-Q zGY{)`^D&UZ!JpM()GTkj$jP-^`Pl>@23{>B+)2(aesX8H3||8{&-QEG7Bp0$1+AfS zk(sfqo{l@~qOO$@Crcu0n#m7tGqiLD+lVu8u0L84TBwnIJKr z%1*n>8<_x%*5tNe`ZgG%djP}O-G71d;uU6Y=@TkkIAIZB!pA~EfKcVlD2u1~#OUB@ z)4k?UqlpquKosW=Tc}kpohxJCd>kiS;R=`SNN?lFhv{nBOJft4Z1(9Kf)aW9;A;%b zXD5r$WM=CL8~K$ZG=D4S00H=hWTI_E1@Khcv?>e5Y6NZMSuXXJSd}2D_iGV2bPS#c z@NCGaZ*GC@Kh-1^jAd(g6o|qZ&5VYZaJ6GHadk=g(ktjpUxELPBGzK08=J^w6|j=i zBdzIN3$_KdM$-M007L!LzhT{uyHz8nhh3afK{WV1__5)}i2{>P;i_95F0=!?XxN)u zvA(aXv#0y^0g9hEh8?KE3bf2jDT@;rBDO3YZNs@-Qd1G_aj5m$dvQirc(!5V^UnL> z+fpoiqmnLoBFOw-Cn`9fX?``J+e&1@cG%miyqQNKru}kf`}NNSd0N z!#~&_LaPc=Wb`U)H_5t3x9m!$Ob`WPk^4WV31p$G3DKvvP z-xeM>8;Slj!DR~Rf$vtm(gX!!z*{!GXM%>uKH3e2H(Hq?rXlFl`{5GEBaAfsG#3&C z8l7e25FrEMAlR`1DAM4@xn*k2Z1snLWA%!ato56fEts_4<6~(RikA%LIxx$WzqOCt z4@c8wL`{}GUno|%_j3h=9Z}s6)gkVtE50lB(PI_ zl?)N_IqVqA{{*z_kbfZidG(PU#y`I)Q?3YIHA}Zy$^QrpdCy_K;jkFua~ORnp*Nc8 zY;y*_z4(7-4^IPM#n@cgx$5@5eZ?HlWe976L79iI?)oAfub>zwVVKkVV$sE&6kz!` ze#}_5TL8l-*TP{M1r6HG)a);vFU?YlRMsCMj^U4|jqr?FbUrc5d}@p_C(IRv%=u(# zA`r3RN&D{@ke{Rmy@Aw_GG5pv&CN{TPA0zfBi1oxA>k593eJzyKa%i-j#QZeLq<~T z(u|ZbWa4=z&>inyebgXq!J#(~x4KavGyP#O6~9{WE||C#==GLiRw{D8ZsVxayt zc(5v1vkW>$?Uy4P8iNWrys~L%6pNP% zEn@U}8Dp!1U5*zj>*$0HZE9(IK}QP~0I$Kg&m20xCb$Y_d;rpTCo zBBMDjo8z)ME}P@BQG3oGClfdS2>$R;1Ud$*dvFv0satW{poEK&HhG1;mCe4-SYTflH6cx4o%z2Ka9LQVpRm8V6r-s89~1K>f|vSsey* zK+bO^6JP()gX5VB&Z!ox#omJuceL7jFz;XP4I~r&e9LY6j`WWntk8Y8@EN9SG{b+gJ(`e<$T;QZ>rxtV|Grq(9!%`e_-_D8cn z=8-uh5U~g+q_Ai3EsDB=#e690$~q4*1rZ=4D?_j3Ct47$=<3a0xt!VS4P>v}QXPM2 zZ!mz-w+r-@*5eerh=OOO5tzs;lqvX1afH9hFc|#nr5hr}OSSO2VF)ebqnbYZ#ieV> zEJFVAr6S5Lnkf2+E}g(5MldEGZb%b_`5_W;W=s6Xz2bk1;kaHbKp06SV= zD|_=dwI}qMp~PpVq5h$8>6>i^XfriiiZV_Le>oh}k-y0M(U#I(#Cdf~DPQ#&z5xzr zA?tu?_kp2{0>V)4sSD+np%TJhdRYp(jCg+%MbaF6(6-eDwA=}3&@1iB4Z}w^>mBd` z{C-`4tDFF1T<*8CF6dq-XzQ{-_l3px)dk$`1dQ0n#r-=96+!9kMcT6~50`S|)=i5% z0Pn3^!5_OT;OKH;pfN7vcEU6FbNXOT7rm|fiM4#7jJ$!A6H)PW!u-}u&6ZTUY?Q1u zXnzd&za?Ds}8 z@Z8vpYp*F{FK!MW9^IJF?8U<0?(Cr=Zf?)wnq2P0F_9Y`SHc~wxgX+CORj(uE4fN~ z1UF+~w~~P(qH?8#D_t}rk;ezFkuQ~Sr7Hu%O&FNu83y^CNG#Hb(mq<8Sp=&Ycj<%4 z#P@lR-o^dwyP`vVmAvtMW*9_3uwvrztp!Y1nl$U-53?sT{16=iQW0wr?#Zm;`w7m|gGLA^!6QrnW z-t@6Mg=Oo1Uq&^l7I4jN67a z?4O2L=5obb?r^SzBCo&c!BtT+#;9=s+mngS_m+!=EncN(rHDF-2Ea8bC&GklPtB4y zhH086;B+QEQl%T~(~vM~7l>eVY}qh08$z)0Ry0e@3G*1TLtHf%-E-UrrZ+wXsc_JK zD=@!l-{gP7g5@QxnQqge70_jw_mL-cu6W(FX?+J zGH<3lQ5YUA6$`~`IX`i`yZEhQ8A=imN)lvZr6YjQt^4Svh_J1GBI0JsX4A?t*a`H8_<3D{e+7ax6H)%Y5l2BjH?lO*EutvrI zgGT*&TXDRa#~Gd(4~cOJ2xb--97)WJ>z-InvwX$rp=pY1Ka!$z&_a$+U$c#-wT-6^ zb|e!AZ)NlchevVuSQ@R!;UYrvtI~EPp>$(r%Hevr;{HNfx>|Ll2P!nh!BS8x?1iU4 zZl5ZS;f9JVbo9z-rjnj0RuL%%ho~yXzDBk+t_xEZ6nSJRG0P3> zOYyt{8cgkz^)#0ijWbxY#Aiw&*Q$$7G|m9Js7^EvV@^v17YWw8m1Goy?{kW`sa#WD zCnk?Y#UT3%#~|xcG028tmiMyTO1XWQaMu^JUienUJC&U~J(ii^xnYkd1U*EzhqcQD zCebpsaxkL^0h}8fFX9GFGW=AEO;nmx5jMb26tD2;nn%2AM*Dn2Y98S;k5DoFHRDup z)(O6I1@S-XNrKDy-wdunc)@p*v%%^E1?T>i?qtmshV#`du6Sp3=!ywE;5=%pUE<*kiymEfjclRxp z_i%~)GN$a#&TbKDW5WRohHYIrHZ{c?a`49pNLaCVaJirA*xYWcDB0wuE=r*5k{G;V8a=@T748QVmNe&Kt*Nd z7{X(`R3sDNoZ>MD?I(80!C1%bW@0Q&CYF?hu-PQ~rf+!I8?R7^YB@~BZ2u)JBD2ui z^Tlzx$JPidnQIO?jwWPueZYa8+FGuTjltPUqDWG|#S88M$? z{%rZ&KzU0wpC^yS1R{A}xwnK%)NhJ{G3WuUiSVqwlos@zUAJ%d_T@Zy_>&3N_<#jY z>$1^1cv_cFZDMqgGaK)A!B-c`1KD%S)p4s;?g&ctC_6I3O`De_6JH267o$#&+OMm@_9g#d(|+ST*0gA74my3)?BHmzn$Ol+2E-b4n1Mc5 z@v7&CsR6Nf9H;fx(H5K`f{%8|kOZA-HJ9 zhT5d-49?hd%@>!6^SPM~JxE2O4xG~!iNV;pwl_bM-lv64A~6?ckV~xOsg+R z3KORIub~8^5~sv8aR%t7o2i^Fr%A$3Sh1V1Y{Ll{wnzn~_T0>AWToLb?Q%?9pRnF) zqALgISSK_U#3vTJBX^=5puy-oL02qI@ zH>;mR;(x+ewTbTKnP-}GcGDRW|4NsCNkjDNO(wpL1EESx^2wJ?xx&L88&t&O^0Kw} z(P7T=o!JK;?CRRxZDZq~b@bcHWTLY!RG7r+=qQ8j^@BA{&>(M5CRRE@-bDvvJHjmm{+1WB2?GP)i!Ai z&`28J%U7H*q&`~R0uCeH3mSJN6U*K8%R+r4h>|ks z`WTfDlG9XV!@7JDgaY@}2Dvfk<+G8{d|Zt1iW_18Q^bh}lxYy|_W)^Pyw-2(cWJc} z539apVr}iF4#Jr%9;_I+H=boSDAstbaZ5xE{MzMVntK4fDD3P;>2=7M z!}%!GLUtN#paX7nzz~kU&g!L~c)O$2BKezegpJCP8_7xHg9st)+oTUK4j;hmL-Cuq zq01dvmxQuPcW%?OoO1FhxdBQvzUY8`#a%gzK>`3T$!pqy#Wf?~B(F4%yVfg$sLn1a zS2^N60$2`RtJBI_bGofm77H6L!3+u0(!#OU8h4TvherKCBdSYV0{fv6CnsFPn<#MXTu?D~8vXYd%R0oS$T$pID706QkycnK6fS zK&SQwCm`y_?#>tY?14D#=-Y#+B-oC|)Kc!~$C)(>Ws3FPNb&v>HlulcCrt&kE|SIR zcVVQM*F5Bxkp8zh?K(dy{x^e|QCoDJO57uh5VPB>1vVPtFOB5R8l7-!GVwK`T4@;D zs^!rJAQ*gRJH>`E7FrrNI#?-R6p9-T;No+Nk$~eRg{*Avnq;Ck&@4w$gjXn31jZ&u^Oa->3mAX{Nq$FFmcBn*g$!Rm+2C^xcxI2Je zM0pe@CR26}&9FFdLt?TE97>VF;{m%rlYhvDpiv^Dr=!zr;Z!AcVz7$CjdcEtae)+Y z@LvRw0dktlt3D_jRA4fVkBT3P0TLk$41>5*w!0UlJn7*zdX5g5b-)-MVVNY@k4_`Vyu^r+!u+9kUq znOGfQs3;)oCx+o+svAK&W_KkM--%ngSQvqAsA2)YFk*{-Z7PTf9>g_jgTVoWjL`zw zkFsH=^juE?pN-MR`-;v!Mu)iM6>-*`$;28rX9Wx4+Mlv-boGL-qM5>9d_8$b`%6PE zJMw%Z8Y_T-zdD)dsf*J6IozOA<)u)L z{S44EJ;{XJKncoCwN!%MN0C7wbA+s_*&VX|O4Jr`Ylv_10!3>uP@psgbEO#%1G_$& zfr{XJ;8?DxewP)I7^0;w*|KzhLFIC+A>))J-6&dyDthv@Em(ATo3M@KaiADQ+b(py zLnjE<&FAdxKJjlMyI3~x&9P9Bl?;l23(MBhPI;jM-xs0xbw`Ya0N@$FZb>Ev0*ovc z4)zU#DntV`JzlvjTZ@|(vzu~zN{AJqJF*KsNv^0f1Ba{9%~aow9-byh_QjEiLzmWA zcB9YlOD4WM4J-p7{g^^1+@DNrpB`AL>!?M5S2E~AW;hlTar$UOp^Q62z-PP=q=DTH z>FijP#{q`6_vQ0)H_DF}Qp;F#afwwGs zENzmZMXmii5`L3+=j#lISvrny2y^dTb>XWK zAZV-g-7pk5;9l~g@%NvV)qo&*ZvC(km)`kwk#G;1zMLm1hnaz{@s+B9n!)_WQ$1QB z8)sA=f-qire_!ePA(;#r9b(0I9g_(CS2n#j2xUX&FjI|&4YA^odbJ_;WwZVaN}mBw zHH@AC=D>~=kKMRBFuody2{w7KVn|A<(l9h0$yQvI$!mrFGNlvW%mPrBaKo zzzJ2)Zn^aJF{&_S5HnH;*#PcwUmY{OwpP$Ckz$Yt@rU+0M1&K9_(+m9L)WO82~ra; z+yil2!TJ2F5M0dGw|8#|R;P-mb~K7YrQ6-LZ0%-T%CN;dU@VLg6P3Te02J!QaJ58X ze*b;!OorCTG%=$Th&E} zH*7+{`Y~QA;)qANi0xwNB$U(@`a0Bf?PokyNhXT@xiSTqlxl!jC*SIO!(}`adzphv zWI<#PaUZ;FM|vAa&7_+iaEGx22po2>@=X^IOPOv4f;&$pi&LNnl8N#_*;fOfC-$-p zR~rE)qTM|!>SoLhG(Tj-1=e&&(n?&o(6_Ic%aRg{BMor#%ni9*Ko;0o;S{+AaV0z z-lMxZL~sX+#YJ049kV!&KSQ7IYS1kkB`BEzWh0#%;ZDU} zVtXmf8oUSQA4W0QVe--0B;#sJIvTEt|GACyXnJA@#)9{B>{I-7A3P+}&SH&pgAXul2NGmDugpPtPw+&Caa zYPL9BMc^sAejYaj71ARV!jM*dh@}jBH@;*bQTAshmfLcgZbYJ-&FK^&GaBmS$)i3E zYrB`vNn6XeaOq@cXXowMVxWsH(?yP&P=-Da3xk)=7wJO5bQxE3IgE6qDQA$2?~xKi zui))ZV}roUj7bv(euZ)B+mycOuvKCwD5P*tTaoz&sGcp- zHG9fdQW-53_ouVCZV!EhfS@>J25ZWkR66-&R8v>N28=5PRj_n! z3mS!2-tT<_>{by0aUm{n(1*|02$drP&dZfR0v0MG2(F%?D+n7JDb36cik2q9ngpYZ zXPSHvp9VMrYuP)dHyg8`d+-YB<#dHKUscW5M`i<)`WQ}oxC1O) z@>U3aDkB1{mk|eowxult2QHTf;fe+4;^;=&@aygpT!f41jm8tuZ@NPIP5%HEK3Q=X zg;qn_#9m&`m#ES=IkMy^DX*K*r}ZbcmT)}z6WNK=?E6bxZ1V4;Mmga;7fi7-{Jp$b1X}74{)Zhy#Z5Hmf{N|-A>LhJDX>&=ylchdNmXOMjh~F>H$MJ^67`(OM&_8#0EYL5pBj^YG2+$s%Zw-15nqpK^V4S^!5I91)gqk$4E0@d@Dke zR!v&Xp5<+QGI6Io@uSaRlsn9M40+0!54D=F<_Hi)LSc^FnM@3Z0m;*#rlf>tpGPu3 z!c&~XgjnF`q_1J9Q?U{)q6{JuY02A`uOe7r7-w-CjygF`XWa%(EX@X>;7Et40%z1V zdHcM)K5a#$c2l_?W(2cTJTPIfE+fQlf~LkvB-s@Oso;lh}IC{7FFK$be__091Om`iBE<$2#unv zi1Uv|s;eW!)8xTiGLaQi&BhXrLGCY>5bZwrp{uL6tGA~MXWy*|hH8RgVZGtuY7Pc) zxT6>_>tO_dkXKT1JehbX2spAPIoD}mn-M`BMm(IZO`HM^HM*rb3+@X6a2om|B}e>FGO;ywV3Xn1M_b7?4f{TgLjBgXwDqO=r{|3Q0fPr`_6~tj#wstFg zPw60t6o$1J$60{aI9H~OE(`=o|;V`-I@h)%1WnNIOHtZwytyof{ zTgxAcSXoP|MbtrpN7gV<2m^(*`154qdkrFP0p0G6;0Qe3ncI(`+z=&p9Mo>;9uFoH z-)|7SqLZLVE%c#xUrP8rnC*;4CK0jbgAuN>d;tkV<;1?51*+*tpx1G=sIEr-5h)!4uMRvL%MZ_S9 zhOsHE#Mm=;yTruOlFZ~oN5L8g3PDwNC3?o#j$%R4bFASZVo5aL?O1kGY&+9)7ppSZUVj9tC?CI8>&OJT`i~rl%Rc~TB9Kn8t@K+YM@7J zH*Y4t!hud+G9^*B6i6VaeMUVj|Y_Ef`0SJ)rJI8#^ zRx>CK!4YFrSlgK)f{T@rtb)lfc9oDCssZX^r$ImuFS%1|TA`_hF999lAY)8AcS#hSw`3<#}Om#fS6 zkrHYyD~W{-T}*gj>j-wEYmL)dnB9wW1%jt^6A3fV+|I|4eJh0!CppGOfNZ#l8@D$3 z<|ee(T1!m>rUYnu0Ssvw&ZYrTNIt^S097K}vJ=e+GF>U5#}K4*jQLGxaL57()Wp|#?J%;SJ|3kNszbPAx8jls9)ZJL8Ep2E^vXhNfGKR3AHwODS`Y# zod&sa4iAsd`j8_gG7{95nP(a(i)*#?sI{DXu_%}s=*S6^REt^zxIQ`1kJPz(G|+Od zmV~blk(_<}AR%RD#tmY>M*C_SWPQ`0i^)IP-^@B0d)pM%WHbDo@*PB7`2ibbV;Oj8 zk=*d@OyewsxQ(oVRishsebvbqb_7(uw(^_WhT!=LoHL8$wiIyQeuEcG2b715x+Si! zG(3v9`O3UTs}<70w^YX;+Do&?P*Jl9jS?4TYm^NHwGspeHDR$gf<6V5ThX}w1nF&Zfh`bFoC$3?DBr=(l=X~ZimGp zHv%g^b@Q^dss+^FT@n3#d2yaeZRTdTaC<5FwA}o}NLAvLy)a@$!g0#k(Zh?Myb5cFwgNfU;-V9D`EM1;xMC0c~oCBddH+ z{q}>QlbJOHqzjI2Rsa$-2-i$08&9`sS54R36Jl9qeoif7w}%EdwaWGX()DZq7Fg5?PFEVNt_KiP>w$Fu?jXSl7Y> z`^~wL3E7$9DH%h+TGZ5_D$WiEfiP%!%a&#nI#PUFGSMAW_GDpUfK@6CDOhBEAnvUt zs9Xx51Smk!t&=P-2%J#>SJqZIL|G7vZUjT%9(!NbKQO`kEHP^gb0@HILC~*`a9_8|4^}*cfbm>K{R>;g?(gd+C~?dj~4R)N?KMJKLyhza}|05 zQVB=j_;zcv7Z453#p(i_xF{anL26(afHK_}8?j(9;$#URHw+!p+fm8`5x#gEfbJI| zGyUuF`fbBGM84`Bx3CM7BSQa{b6F3uG{UF>oNFiutm;DA>s20PBb89gSr1pXHP`YY z0xa`K6na6QFGI8tdVV;ykTd46>JA89G7CGp?1oXt#TtQ3^=@IbaCw_F*wT| zGJ)gqCAtZ?SQsikgqxmzNK=DKvTfd;+qrAz-NV}F);&y~)^cBjlGY+yceUCd2)qAM zUswS2oKH)16%t2xh*@FNUhZWhjxpZI-4wA$CtoNy6U8eKch)RhO974On?V-18s)nY z5aj{ib&PD!S0;hW*7{~emyy+)#BLIM_CjJ_5^l1zQRza%9t&#qgJ~YIWW>eF!m_Dn zfr(9YR*Iw;6d^0TLIfBs_6-2DRKxrrr)SavgdapxG+vSpZvt&lCeiwlud0zDV*7Df zBzgCea-(DDxiY8|BKLLEAOSmnGnu$W`CAlAY=~I4b~8+)Tf772fEoGX)$XLvB@-=V zCxU3Ho$*uftK}AhxGBZqVjl5C_d@arnWQz$O~p)hd#12g3XgzOuqr6da4A27_4yq7 zk@st~P%>`Q{l-9IYK{b8Bc&n-C<95L8?22j1$ZOxnogKar2BdpEz+g$>zS?9gtc?3 zD!!*u8DF2laY9W<5fhTX(PCAb%OV;RXj|a5H<|dhT+rGzZubyN^&Z56#6_IT)((0F zSSfHXxf~_|7&CtDOeU5&kPxv6n9RENuHM}+3*MegtgKN0GZme;6JEcj=qf5|?Yr9z zR&~jukPLLihaqm)YAU=kndq!hFDA)*xAJ|crc$h3>tKsgI^>Y5mk3QK7A;$r3tJIJ zhgpP2imtXB$kr(<*vM5@F_N`=9j?5uier}Wpd9A#K5kY)+kK9&Vvtjng4_`>`i2o- zwd4W8EY;vUkpPEK&!S|#X zcqM75g^PomT`^`%2Rh`$V5J;~(qU_rfofUURI328sAB#Yg=8b{JV7KXoLs;;;`05u z%BY-Ue9HmB{k0%So1Y&Hh~$`QYJq`_0&X~E&ze8XnGKzlpBH9KXbIcve2*hJ2 z$Y_BPg+^JsCIMVTUO$9rOe`011xE-BGB_z9%huAHrYVf_4)rBmEV@?1FecTVOsohf zf5kDWD^n_-4(9igG+5LYO$lwZA4ce~|kuwzIh$IHYd#>~aIFo>wNgFj(DT z3Gi3?-Ktlb5T7(%Rze_ie>zs8{?cSXuwxdi1X&s{ghq08j-{NaPOP&b;CbO`%USfG z;XF=RJDqAE@P#DSp{lYP4~i?WA%&yvb=cgYsz(CeQTSmFmpIcgbC;F{Dlx8(!5<0o zzU~NMAVEWLdZOX{nTf-%Xvc@qTxuwHg(ToWwoFWGA6zqzi_D6?uzd}|R=r)j0s6x3)01Dw`;4NYvH zM3OkxB(AySz8Z5d=&fAZlUYC>|7-wTe5YB`GWW zasCr~YCB3f)sk9p9FwGk(+D;WE3{K`*TN(khKPfpTT?^ti4CI$e>tpf+cW$96G*Lg zI9SWx*3!+Wfr4Y6CiSUQ>BH^TI%ZxiBpmEv6fo^{t#FBE@=Vwy6$=hlST*wLg@w|4 zS(G^lCpU{Z){sIrve}ro9af`yAb_bm3#kHb3zs{;AtF`}Wo&NS*#dKU;FzjXc?KuO z_7qj5hXB{tU>Aod!88t=p}o{M=mqV&OnfWugu+JLI4Q@mJdLCz94*_mszN)QR@@2N z6LZ)FX-kQ6ittQyN)SPr^gV}uEeM^0z*0)n*X#nLmSaWIAYlJwp<%=JF+2Ju$nU9J~A;rbxCbcU$+7d==)INU#*9yL{nQGG#dgfXTO)P2{8SVmqf zQ)h^^#Xf!`P#5G%*4)G}9fHd-yfL4tkaub(hua-l3>nhEgk#-Y(|9G(BhqD$*F#^fQ*gE8(8ZAUcZEz9et_y=>IuN5r8^yhGY<$MSF?x)VP;;!A z>H>`%S+x5fDRgU7Qex?gzd!-3rqgJPqs$m0hZzzq6mG^BGEF9Z1B5p!Vn;oD>bhun?P!?WGd+5IV?VMN?|1 zS`&!1I?IXRPsn2L)Pu|}8U>B8Jlm3;u>wz&O%#k)&--A*@0cKHY6c8eD_sxOXSscY zvJQ4rnN({6W!2|EOF$Wi;vZhMN0@^#;(L+_dEGTQT8MU6akCi>;x$~xz31er2b`cZ ze(HJ9sy}S%k>Vzzy!xy=3$@Q22rzLBY>7$GFyJyyJ2u@MCIwZJ_7#Z$=%L=Q=Vy(A zR0v(fI-kQ5Vh=6FQ)rwfEi`bHyk3bDmKd0_bCbkDQdxOW3>y%xszKiukiU4W1W>wPAxdC{ECxG!0Bt~|L=#OA4C7dB#syBR zQsJ~35S3k>WG-j)%+k zm4mMSK7+Gk)7b@M(DNS3?1ABuW0vcIqGnnL4dqOtM+&Ry6 z-dmFO)krgWAyE!0_W4dA*oQ8(Q?xWaRLi zP}HaaWPJvgi<_kg4S&Z@PYggCGxnm0M1f=LJ_a1cVy1PF48CKmVqFmf6pasFY9Rap znOyDDe8fMFy`7j)w|M(Q09Wt!iGsQ3swM{WaHu!bDK$nA!UpX@JU#gNJd7w%yrTg2 z-rWU>OeQ2A)g+U%8DN~1jrCzn_nT0|pjFqNoDT+9Zt(ORZ&A6!33SCLp~=%l+kmh*mOH7L8eX2*_}&kmI} z0){q?ir+ORv{B*M9gct^FLm2m@JMeFne^>}e5%m^hDPnMJVpSf#S2f`Ixqt+L(h#_ zt;L+^Sd~S9(>O^SHCCkOI+nBnFqn*U!+bha=#1>y++W;^*&Y-SnKvPteFa3&pg~B7 zY19u{s{CrLSZmv!?CW*u=YVIHf%jy}=5P?1*6VVM1ZRau3MkpM)6pNTZ;8f+p^#)U zT}j&!GY10lG}<1r%LGutFt4GpMYTtH@teK}TgB~si@KY01#w(o8d?oJ(Ai;`b~2L% zPf~tpV%#D(X{C))3QQy{V+JG8+hJd+hmnBdpP4}OX|&C@)M>2fcG4P(XjK2nBYN<7 zQSrMBclJPT)tyHiEUq9?TrCS4#8#tZ8z@WjDa{Rwoh!8DCtpaCF)trV!6_)@~P2mX)NP{w1uQp}< zyDq%6sYlw{^B*=_V|Ee5+@FOHC#g1XE+S@b5qE78<-}eFObc*HjfEpvoB6GV&Hzu% zF@#^q4O4r|qnU?r<$+H6C|G0gMgc(_-VjHky=DwJ+|Jnas$Fg&Dt)(xHQ)%!wK!HC z_vOmCcXY5)&E|?7;Yd4!yvnjOv6b7#+TcH@DTivp!8Tcm=@1+Q`t!Bq;2qa-Jq$QU z7lfc#yGypW9#hKY-U_~REEO68t0zki%%B+&qt3DBiVr!`aR@WJL-fQ49OWG9UNyys zYmwf#wXv{tPxbL3yIewQUMS|+C$VZ%9t(=r-kui2jllyDsQtqH+6Y+%xveqo4z z#{#%!%@DV~gNHQqD-BvAgWEEP+%?tpfsKdXhJ_h1;UgjTY zQH}*%eGt?L;or~QO3UME!;y4id@uqXi<@}(NH~<4V3C2xv8Zea7JT11jt|3woOCD< z4ZC82u;&0F);$>bOJY(*Cvm01sN}vNg14A(Mrc+ zXr||}1Myg!mz*FJ!8Mh;R??2%tU%)Mccy_2r5s9|)ze!?M)EksK<(H2(#HJ*!gXBk)&rD#KvRv-!eUMxSu87bZn#ur-BG&9kRREX*7tR-?(V*QbEfpr zW^Zf{Oo@GnvQGZM$;43Wwf9!xj6EVlf`50Q@{MvjlO97P#?eeZy=Nj_@y@`}|^S@NU}XkW84ocAUZdqfko z#y6oB1OhmwhrsHVxrngvt17dusDDie+FYH64pGW3C&@F+Da`7lA{7cj;~PaX*h@{X=m z<~7n_o~-08Pa6>=${HO?{@sncb;-Rr*tqUhOjB_Lu2_1=D-C7J50xD<8v5yUsx2jBi=-b zUF96WV@TmW$;1Pakn$SAOaZ6TczlcFto*AqHif&aOV}9v=B4SO5(C@$l%+Ojux(Bo z>FzBT3qd+!27pkcUo^Su9_g{Bo=|VeE~;6l-!#}Q`ff0@&-0;SxN|aO@cKE*0e*!u|qfl=h%^k zA`J>c=dx}-Rr>vG0>tIDv%trWgSsZ>jYE&?r6?0ZCvnJn$B~?x&{j!kP)dR~cPf%y zVrk`5tVLh}XMj9;Z(DFFvQllGiPfBI4RVPaFsI-h$8`{A6#ih_d$3PMmjaGHD>4x(?T{4#Y|N`~^&;?+7F>>&td`++(DoooV&5JpxeGch1*ZBL zr^?BMSj8E-HWJ)A*p@C~8FL>$xKvV-9m#^cF;=X2HNpr75%2D09o~sJd68b)UYKz= zUN!)gWTF@the4Wggb z8VHP6)kaY^Qo+(5EB^dZbzQcu{0vp(2z?lgGZqvGLM;wOF7vTufP%Y|i9ZW)s5+22 zP;kV*acHcf)^*7#A*_3v;r~zaRp#o>)5ZVBhaQ9zgl|(L7WBd!uaek zHk0KL>X$Q+%B8|~#?5U4u=Pgc^ry8p>qv%FOhp7?br6u_Ct%iCpDz|jHRnq?{8`uw zT1N1Y`x|u5ZvLFhX)=M$1fOPVOkV6rPELvAc{sd)6KuK4L)?78i>%*x@FtGd?o^m?7E*W8pGYPrF8d}1Ses#UyF@`{CDNZ|j;mW8cXEKDtK zS-7ZW;pbWwwk%kfI{rEy$6opT!o{gOT2hk-@ed!Knrd0tj=!hh#N*(Kh4^(%vh^zb z8B3=6ug2elo0F+y@6)f><8kb@Yxy_v{ruaDM9vGluA!`6OX}DgsSCTPd?uNC1UOtz zAT!BUl%0GJ@8bmIy#))~CO@VW0X}&`)pKwO!JB-prL~ocjZv|-GyRlandDD5CJEbv z$LO(!04$-@d1N~ZP;EyR;PtuX1pXDIjz5CbQ=>?o>7{%>rq>f!ENnv}b%wqlT0sOs zjj6>*6W(Yq)q3zcdi*P8dEknLO9}sjSJUq|qFs17at$6w??I-YEux&eB_)1F-w*vG zW#2%LJiY!NkDn3ZhZx|m>4y_|9KMIM4p8+u&C=o$<(1KDsxf)0EF5O zWdY&}{pnSmO6$~X5;hU?(3zHnSK#l@?xL4bK~*H^4NYr1{4+{{mw^C1UZKZp^mvsX zFHz$*5yEfN<6V0E2YS4WUl0BprQXr?-l|jA>r}f=Ezzl4bm}^tdK0PC!QUrShu@@n z>6OWH_-870=yjcXMWqh@n@SzJMyFn)Mq;=e>c^+lLD2E=I|TD69)~ORI6&QhXn7KZ zNgciwk3-BeZ-cTlv=5C_!NdQMO#SR0yiV<=2MWs9a}xekW#Pi5OWS_74Iue(i2LU) zK)I3t+|lxd#21n>mHa{i01p2%)x45l+77)>SbeAm3_e`BaB1q$2Y*DhPqnoDY-n+6 zX-hk^(C+{h!e=1?`Ss^0!#^>2!sYNH0HmT*o9Sn8U}~z19)0xjXRlD51c&lY{u>hX zVX8k#eFH38Q-_z!DpQL=m)}5K5S+CPdp9GHI(V~e_0K@d)Zv{A0C_Rtfw8Wdxm2fs zeft*@G)nMu+u;{eQ;grCekywr&7S%-rCy`9Q%nD1ZHk87;lG7Mo4_vzll1d12~YGN z4VZ&{cuWxu2}s-F4+$N})WaW1yiDSEqP^URsaqC{Dq4ZcuQ6i#u1HP(w!Qs}{;!k& zynsJbO;gM1!9+|Wb@*d?{K4ua@KNgUd-QmVYI^kQBvTg1Or;4KA|L@eG>OMluaLY9 zxzi%^(w3#|?c9-5BrMVV)ZtEU@ZYj@KXjVD>>xNVpr3d!OdUlAN?j`(J+)hP;S>u@ zaQ)P8s8)VVQHsP0b#mJj@&D9mS@JK&>Fua4PV7B3DSIq6^@w2jsYfWoqg?>ITXuFU zek2&w+()QTo&dc-j~CTduP0kgh1?qAyQ$Z}(QQ*8K0i`Z9|-C$k~KeyZfkq=diA!1pf8b31|t6j z;I}>c@8-kdPUJYe5|76gt3oI6cBo39meP+;wk*7oO1(i}?m+7Bf1}g}nf>RB=*JK7 z`1t~Sr`Ob@Zz)(Gs026m-Ij%v=h0tMZZJgK&w*gu&#$4!t@OBs9?R)*Jw4L&pceg{ z(0q&r_hUN<;+kaJxp!!6KemJVhgAyDB=y)BG9UIR3#Qe_hUin4-oO`+ou;>o$T0ao zMTWM=D)i|cmEnv?;4>xGpvbR~`!ViS2tyWNj~%5V=jibpJPy({JNdVG{QOPoi5r=9 z&w(7NBZTvj1RiJ5z|_y*q2Ik^tEfBm><9RG5#(r_JSGu1_J;Va?MQDc@{-y@5ZjLM z@B$B>ev|kIyv#sWCR0!Uj!JmR)N`O*>fA0ojxsCY(~(VpePkOxoqe5hfnQI*sS@0o zJMkOS=*UijJC?Q)bpzGM2wx!bK6xFLS2Fj=yY#6YkE6tX&)q^m2rKIGpS({bARYF?E_~5ot)wUb z<~-L)C5ZrU5Sw%{8yx}a;B!3AP&YoCr^f-x2eR;B7d|^60_9*ccMkkbtwuAqJ9{)@zK7!OS!t?0ccs$#Q*Z=quRg+1! z{l|BSYQ%0w&q;xKv;6yY`F9QeQYK=gqc7lvigJS5g6hD7C-E7u4}Bf5kN$xkAK`KM zd-!(jI;0-^Js!vU>9LC*S$d4p;{dW8xt4mK;DhH|AyKHVBWnQFZ*4re*SV8B-)8v!X5)4!>e(YUhgW2};HT8%cLC|gms6@&PcV;f zqxb8nZ5c`Ar7f+b3O-Ie_4wQLc!3_EV(RfTG=VHd6>sAoHHOoq$%|Agal_IUQ190m ztDKAI3DtwscoLNN=rJXSMe6a>EhJ|c^S4?0KK}Nig-aK=ZGgBVGCltBHR4qOp-;yb z5Qta4AZtVOk1ycCaeRRkpb$rEK$N%ruOCWrMLH1+SuBKP4tzp^;|nAe`IV(DWn83b zOAVKO_4wQL6|H4Vd8xa!Wl@WT)Z?l>l5EEpq#plJ{m$qy(vQEr!~l|L$ku$x{{yh1 z@q6kmkcL>ENX;+$jR@cB>ju%1&kGVR0y&u`U~!<$uTm<|E6^jtvPk?ok&hVTu^)@M z3*099kMpns#ofP1wFuonE#^NrXvEDoq=?vpVt84!h$aHbE{g~s{z%+l^57GYU=o9W zkJCzv)w1Kn-6vSbqlM1o|B$_M{1KHHR|%fgF}RMi2&Ken$+5>PEj00=8OJMv;;i=3 z`y-00kDu0gPK!JzRCiv4!9X=0gvVRz_(l9<9Ir+_7Zvo2T=LW#s^sa^@t0KkB?pw3 z@VkcclIUuDA}Fn@+=0}IcUAh4)TuWFCaFU?@WgLg@HchnVKpB0hnBBNlIt(X{SL8_ zDuPlyDo^kSWP6wL2EU`sdQ~PW1B$&zWutxvj6nsXUPgdpr|ikV8#9?v;uNu zK=6HQ>Ij*1j=#t(2Ib=vk0WCH6Ex2p|0O+MlK@V2(nur8ed;>;{w5wLNufCYD*+CP z7R89a!V8H-i2M_OSd@C1>>)J1UcSCvj0K(>+UcFvr*iPqJI{YFUvESq6bg)0`T-I} z06GGN#saBoeffG+CNVg%3sOO)-#{9Oo=8A+oLC^+^D<42Cs<7-RR**3kt$(Hi-{9U zs9ij@kyJTBrW-b-9Qitxei-T(6pPe}Ybe_v@Hj$?y%UR&jn}D1XdQB*m%go~2kUAe z@WJzv4hPRGxpMG4k@{se)qp@J?xI=u#3p)dqq-n&k85+!;qTEW(Bb$+nKacZ?Fg^B6ZA5aW=A7f(?b-?67F7FU-TW7X*=;T9w)(lsgu{p zemki~#L4RwHYcxF#ZSK7LY2@Qf9PeQ$WCph){$CF<^$?l8Z(q$oI07sAFBDte@BIC zfpFr0LhFF8A+7rOWE!Jb{(kB@IkQ3GPoRfS9MC;1-|p{HyHPLpj-5f;LOUoRhm*VXi&^?RLUly&H`DpIdZE z|92EkOW3;RR%^C<;ui7i!>Z@?2X1BR$iETR|ELD#l$Vwp>Yq z_T*~GEur?Fv zfkZV=lbFPg4{^&6{sT=^{41q~RO-aLDs_CEQdHuil2NDLP%`SsUtteHf5hFcRiZ;~LM=*67AOKJuzX^+1`3&g%Fz{vJ*n8xvsmSCiY?LrSA!)hvw zq~AF54}?fdEVuE{>Xx@5VpXoyT`C@-4PedD}1EQ7e6F z#u}O=l^CPtKj#oLh@53mLNUehAz+|7bI2$vV3y-UAk$y6y1+0=y@0J$PQ$vrcQFCb z%_nwQOH7_BT4XQL5|lsSX%tZfNuow_0sW(!V7=c+O(HVU7TSqFP@k$v>SQmb5J|z4 zy_!nxddda&$can$_coHRn-tn$H#5_MeosKxSfnNgl$KwRX^~iFGm1~_OxnwAWWoo^ zqv*o*5{F=E`DJDq=46bLAvFAnU4SdInEA>6s8R4`WyUxv`wQduBy$uE_>WryjJcZ| zXfdYx01Vhnb~A||XmVF7SUW0BV)LGlZb9)|>^@2ZXZ1HMU|==U+%ABP?W zx@~{?8a-ae;|Vq^LxpZ_(Td3vljzN(o5?cwaFWd}uzxZ~JaG=cJf5X;uOM}vl#J*9 zKLuQvypF0<7GD@w zQJvZ!IDM1=5?M}zXsOdQtDL?_k8MhaJH1QW_D;XaBmVT8)TO6?sZ4sO--IQfKfOgI zr;MHI^qbTqZKpc@CJk$7#%!lLjlN2qK0^;)refTS{Gg05{RLlNGX$c{ZxKyN=r95V zXNYJ4qZe%~wEfks>WxVEBu#uzUN49RgL2EltMK9U$MgZfo?M{06R@-@f+VsWVOUt*KKV<$J=IT;@y2HICoA}L{x<}O=C-FkK=})xecO|}=$n{R ze~+SBdL5;rH{$JM%KIz&^t$@=0`WUd>rYPV)Qj>*Ht?UR=%_d&_Aqlw)!?dE>-`v9lvCHI5@ttrSc!26c zUp;+5CeFS}2~hFOOVlKY-GhR1JnxV?`1IS{(?}isC(43vln*-PKcN{@#PvtW72z35 zP0|j|(;pypmU!&xk16#lRjDHH)4a4GG~SUvow-is`A8R=ej|Xr@1WcX9_N+ zPw(g-86J`FO)5>f7z`2uFcJ*!;Qs`AXYK*yBUcjmx9~VRrfPgq(zi8r<^+B?gE7>0 z=3L9dtKnC|epqPw=O{Cg>s9bM8FD@^{|Y4;R3Z|j&JYP6qph6BPU7)wKORRP#p4W7 z>CnGRD9_M{ex|);;SyB!%q@8Q=WFP}ueZ{-yXb+^ZO;Jfwr4ic1Ki*C4Dr}AYIr<5 z1vroWZ-NSTeC7;6eT5!J@i_Y5)OQ}il!0h_lgy`;0{#9GL^KNUGpl|fHu%qr@Obt*dMs~QcrE=+ z;PK4w@HqOF7UFnHTMPq2Er^8<^;sDI@P&dDF1% z*%Oq3$iS_67SdXQEtnuszNUHtBJ5cBe`10*JH7 zo;tf5j|*>63b>@s9-!A>;c+NOQ`A{hC^+sY(+fDCeT&cqkDh&-5`9;+x3kL7hvmW9 z57^5E9yaapK$6Wm%brf>5~g+rMN*-ipnqUQkS#^d5cZPr@kv=q$SQ)o zTfsS?etLsYYL{=PKN1ob(h;gE{lY9nCEusF-_YYtO$D*D4xVNiaqcd`^}vaE;@qpM zt~XTTC6!QJdX9Sj9Cr`Z!5xgm>GvsPSCTiDQy1<*rk^m1kP_!jY8_>_aL>V673^0cs+l-ROGNkcAyQD2PrYvLgT{+WUxP>bHfFAE_ z(mr>Z-ibWt-;`J!V+V|fNl;$3@GHm0Qh!G>=(#tP9sqCN=iVkn-^J(2XXv*N@i<0< z?I#HWf+5q&(=wV4jH!cvGDNr$Da5q@cWg3bqW#^g`29T3LFnS2Y@*ydZGrcbi^O_N zdkhMwF{!{85alNqE%bhpCm6dGan8`|h1I5OsG5uB6+K0o9KOk^;&UV-&J!n|CGuQ& z*WfOAf$`zCD2`x!)faZb5p5M*V&V4OQ8g@6uV-n=Eb{RR1S6OR*6X}x0g~)c@cfIk z^dV`$lw*nd`a9uo7Zw54=SkQ-f0P~{;qgL(Qp8Zt6Ahj}Knk@ zk@u*Tx}Ma3VJXBt^t4v77sJ(Isb-!FJdmJ;Ev7Dh{yjWipkea@q4L5KdUSFp{2t>; zbO%x|jN*sqKcL62z(&u%g~tm(ukD4kc$^2yZ7*z6oPX%w2}(a6kM5=)c2O~AU&ieD z4>dKL6Gwa{~LGsV}TYv*o_5$^uNNof73!CV%jqsx33l=*6HbJJX z5gH!nLD;2VNRWJ`MFDvRsq`4q)CBRJH-{(*6?_p5L7C zPGq;kMhYBuN5b9)-H(D%$r5*qPol*BzYV!9%dbF|;jf*Oj0=P?RA8Z3#{*(mZPqQ@K zu@pS^2AeyrdfTtK!m3u$vpq!;^rscWjHx9MahoBz(Ei5Bk3A^V5 z^ph{9j($vX1?e*tG2u^65gM=JasGECvVdpmNmd>qmX3Z*KfXbb-`79>!EVl(3gy5Y zmYQU91M339gUO6jf#Kx2cG_DMz5Mc3%y_ATSGTs)o&y&!43IozT2J9YGA7&$Rs?7>Ecap5o=!uu@pL!7i-5Cmk@O!v{U*k zeiPh_d6d5CY_Af-2aFA9-||)X{_< zicnaN3bn4P3h>ej7KvkIvLmr%!O;HV|372z17hWM?*ESC7`Hm^?EOX?)6Rs+`I$?4 zfA(OwhMPbN7VISl4CHVvSg>FqffN$3-~j^$X3uPA&tEgfN!&3ym}JtJQ*BLRTGLK0 z{=h&A7Cd0Vf&mK_JYc~C9`Jw%?&te`)_V6I)5H16_`d7!v!3<*ThIFQuH7Ebne9z# z^Q7E=Y=9i2;d5$tHW|GwJ_-Uy%I)B}y|R(@#+S>AEh^dO@rGwmAbm*dHKo-%be_DK z-8_b|74^n{dY17|I#6Al$Za~JB`ck7$kNgtcJld5nmr;-Lw@xM)BQ@vkEm8z*+|%K z%XDpEGB>2kjSax6PyB1@qc?!Mv`L;s0&S<2zBW8FZ){bYd-T{3fG>s#z5AG2l3ohG zrhKM%?Xgp|8CnMiy@aUG6sKBUJF_4uA1-`B&Zz1}^Myh>sC4@L7wdoWk_Pgh)BBxbKKKkUcHSX(+#`m)E{Z?cq*UpweKAxan%T#|vwHlA=VYH7E zdap0%{vgZMtDY`{#wa!uD!o@jg5)75;Hu@t1d6=ct2_dH=Y}8qYVN?0f=5R<#RaAA-?lUqhWK13M#fcM_n^Y^y@EdbAUl#%7DLIT4&8Zfvd?yT(UNlFk&3&2>kZ|A+xBER9X8gti+U z5#Sqoc!CS%N51QBo$GXKu@gLB_E7u1w!ZUEBV6xa5?Z3_`=0kAh~u}j?i{|_D;vfR zue$41*7xrcnt!^4v5jc!F+Da3O{hP=Nep`hH2GYFX{VCGE^N1aviVJ_En7Rk2@-^5 zjj|#>pN+(F-QnASZHtucVIB*QDHr_O<*3`%sGBI-KFQjlig}HKVzamMpw-@^kjIVI zGZblU6QN}A*gMQYc2rXbTHBMvr5#l@TkfqcY@$NF@hURx4{8>kRGZrpYO{+X)WS0f zrz2O=776|LXI%@=sSS2>{I&VQj#O^^4h~8JF@7g|594>FSy{&^<8NgavPiZr=d9Lc z9N+}1^)gLnY;I*nwy)=7lda+0%QEnCD&D#*8DG#2n0PYCD6X;yTWg;(#?$c!dG8Zw zJ)a-YuC%FE{xmZnx{&&;q-zR<+|3>iRXmX=hGCn4X+k$3Zr#BcE_g=TN} zKmj$wUkY2Pg1s~q&Ic}9Qjlsac(u|}=OICB`}4-KSz7Qpu;40vH3paxqD5*frj zK6)hJ0-|Ygc#Z;oEL=;{I=k4_%3@kMLGVmjUI}(=skspX<^Y>;n@MuUby21FpxeFV%Q?? zn=*qR_#lV`GLA;K6TjF85tmxx;Rk~9!H^ysGYOx`Zshp2qMdC>yF6mv-q|rh?UVV# znsyBC?URXL?US8pPWxmQXHpO4MX(~|BFp@EB^%+^_QYN&Yo$Y~k_*w^Z|1e-lmuO6 z`CHdS9a*1evIdI-F-N2Ox~4>=ab43nVr30mjwLljea0Px*WX!oqIV#*KD!LjGK3^K zt!IRk#JBZKSpZ2J_mbsOA(U(yp{2=^fM;!JIA5@np0zgzQ`lij>_OT36N&w0syO#t zqE1qV*@3Jx15kTup1*f(%Li%gFOl>-N-?9PSf9XVRf*VMVWMLk<94$!sXoRL(=ii} ziY@t59|*FCb)EBwV=-u7%d%?v+*#6eUsgQ_+<1|Np9JvC154~z&1O9vzE867Og9Q9q$*O`bgJ$Z;6kP$Y`(P zgY#J$E_H_X!_Bba$Is^av$stJ=ED(E{aKOpxF(sF+6ckPu)H5!&KdI^c}JXM@5cdA z@`hS~lC5K<`W{P*c}44lHLg5V-8xa0IZ+zy6PeQO@^iiR-fnlG&C-ar3*H{e$MiK2 zcT&|xHJ#tRHz(@l0XJgo&L*7OyK_B{&gddw*~9i8D%fiUNR01fRKJ;&jU^-Xkuw{k z#=U50?C$^(*))iLAo?FU3wBG(Yqm1+-DTBq9=dBX)u&491dpG~AvShNvec)}Pyv71 zdno3|&$p%yAKuq>XzDAe9dwPof7C9{CS2B_cf=Nz%pIk`(FZHZ!=r9DB+(|GK4U_PVn7 zrP8w(%7G_ki=sc@pR&jM@`y9u#~1&(iR)SZ#``3H)+ZkW8uvs)N-=h*%y39)BZHnm z{*&jM<4@Ai_*Ncs!e~wwZ_e7~>N-?3zC96bQg(E*uk;!w#PK)x>an0^RO8L_%0}%5 z`4BuOzS9(#=k(aYjj*Aml z8GYqemY%qqjpC$-mr3c@gw*B3vw9?`EX@B^(F?D|dI zl$L#XIiWT7MAnGOjn0sSe0W_C`Kq!nvQ55@fMn}wtB9SJ&{ov9Vhr?9Zrvv4_yuBf{Vpu}bOSh0-Sf;wuM%>7mg#w!A)`0Q(z{*W1dJf%nHC zzYrr|KceTO8j=Vw{2z;A7K7kKIziuQUw0 zAT&9y^}ym(94K)xj*&PRXGUPnDNop#XRWWF8gOqkgHSv^9TE9N{uEW;UcVjkb853P zQEHN|X_{&=`FHpeeqsHTjmW7j6xS98JHZKxiU>%rw_3|&kVzSY?yXTjC2MPT`r|cy z`_k+0;`n$m!KDuyNtUpsIgkXseo6y^i0Wb>Dk0#T8A3?`4-HUAeM1;=tQ`R1nX-le zn6TmfVzeT9PmyTDQ?JbTSY?vG)cJ-+5_W2*+JjHC{m?D2Yvg4d=5sTT_seR&Bo*U2VRA`A8?U~i#H#j(5O75vy2LU|0ThQBy5dO49Y{k!Um}j?) zMxv^{plT#bxx9U?p2oV+>_yeTnA9Xb#yNEbz6nPU4F+PCdUmIKU#3+1PJREG8*l4(g=xuW%A#+u?AvQf}3f%sX`?U>F)RGb)wAa4G_cu zNwg?)dUMIW(_1>h*fs*$(;giWm_s_GUz~m>aA!B5Pf>qhb$Vx~;;v3`w}X^Ayg`pM zr5VvI?ObA%Ke##lQW6v`aK$x74~wd_FtEht6j=J1s2*`wh`w&m7Wzdxu=-f%?mS^;E<7FD=uc zVIlgZr0kcr!nAROZ9%tYMjSeyUI~LE=dw#TU#Vw2_EzN^9`!8^C4nD9 zX3Bve!57Wt$G;i*%iQ+H!yJK2kP}(ntB^>roQ1ba*9{jxT{H#ip_2rAm z4CE53Dx31lSv|709XYFxnS6=dhc}eHKD+^F9?g7`=1}Rh@*%*XOMp4_f<|gnLkjL> zBMY;liUh*_IWGNm=*T(Tk^0DAyUJ_MX5$(5z{EFVqOm$XDNJ0uZEuov!~f-?u&|?o zc|E)#F*5Q>6%LKlfpvixOd5EHs=v}wx)MzTGq-dej>`i6s^9&A_z|s19q6m6P8#R| z!R(MxVL3a)(;|4M)AIuY=0~2yqiN-i_@PVK9$NA$#G$-FOzM$yqOfJ8q_7~_R%kQe z(D}r-LnWzg#eTI_#U!6HTk|`Y&i;s})rFY-&F>rxVfJLwyQ%X#1wazx&qf0W@x$r% za{$aoq-mA#S8=hMRYFbvs=V@Tey6GHGYN;Zml*^Xfqpfc5Ik~*Gas=f9EoQ3uOzF# zI+s}x(cIxKRTSl|c3J5I$&0FeInihM&BVH_5$LS4=u$eXhR|Bresx3jZt-}-swe%9 zBVG?7WA;IwRUo6~HQ~cOn6@lQx`HLbjn6expx)dPvoI$jp4pl@f7qwZ&OA-pVG;bu zoDfrE8vp%xV@dVHOFV(YO#$m{d7%mIj#mK~uk)VSQCcDjgtntehB0CRrJ3UX)tV3%DReE^nF;pTVdF<*+Dt*PSG`VDF@|s=qvZE)`@NjsT zfhFPb#>>t+NpXK7e8z@>{bAqGeddC|$QGTsDd!U}J>6e$NH@%M-(OHBkX({NoY_n5 z!$V0-I%7anw&jfPAc!?iZo`%cbuDxnAGEb@rXXV6RY_mTqOfc|ubzBiB3hg1H? zrSQE}W(jm!7wHjh&t-PJ%#K11?_^J5N&7pyF8#?9mKHxT2L%?LXF;sKRvB5ZtZFn`L@~6hNE<9JeCzt)b!_Ay~mByNlc=d6b zoPpQ+;;?T=LgpkpnrM{?%S}4-IxE}3dP}DiL_iem6;G!eLC)E>QFT&l)m%foWkeu9VmyJ z!ODQbbv8?`_&l5w$_uy36uPLDwlZGTnG^GKX>t+)EBE;%y>qqf4j*pjP{ThK@v&w> zA!F-u;dRoFxtiql#!{Ph_?dfbTA2WKscxD7Sl3C%aGggVuc67E!afy;W_k=CQ!7*i{Y{EjcR;V|A{X=|E=O?uEo@u`UA4W&crt@9*gH{Idr%@9C3JmJ`8m@ z4nrN@r;$XS$4~esaip_t;9=>=CbhL$gyXQ%5w8H_K05jAMxg+Uiz5pPdHonMVBFAP zFZnq8Wa6mC5)XBOBqQxUvJZ>WXSeFHO^;{7y5Wm`&isn`!$qEdBkdxEk6h8?nidVU z#;1Yr2xmmgn##?S^thuzc&p~w9eU&u{K)m9Ez+LdrFG`yw+#5=^&By&Usjp@F}g4J z5%06H=qlkqJ6F0(XWM!8?d*wePlFFsx@EpSj+>i^)81s_w8!%xD_4|k{t{1{J{WoS zRE}OwMufS2S~-c`2Jz|a*>dfbQ|#UvYMVccsdjrIi$!~X8M0e*_A5Hs-l?7@)hpWj zQV|eJYC>ptSI;;re?c{_>Tykv>$#y}%fJn#-PGf@@Y7fR**Rx%KJIitY}zp5x=P(u zDVXKhXQlIk{LS7np=PBSpKZvkY|cixy_ph9mDl4!ZO=u36;&Qf*1OFT^o0m>7_mH$ zNFdYW$YKUM%YjxOJxS7FT~oh4+oZ>49&Jh0XOiI0@(b z;@9Rvj7zFnPGpW6?rzfFIm_eie&yzQ&C%@{ucO;pgi42@V;X#P`}*R$e2~9AYm6hN z*wO7vM2osdw+rk`P)mlhIO_G}&-RsK{Mr6aFb*)Nd{d9{v~}_Jl~_mcRO7SRB;7vN z*IZ^X<6_C00iE2;@rMm=gCQ^O0wR&%|#U+69|Us1ZI7y2N( z`0R{CKvw$rC5K?Yz7;RtZu6LUT%mIcarZ^>nUumXo$;A`9up+WrPabBbv2goKf8#6 z3ChK!zr$a3AoFR z=w?dS)h}HXZ)-$(`wov^Z&BzjkE4BxK0`SSn+}vKx()Rb0RQ5P>f30t4=Ha!8F7`! z(M`FbPUvkB)J=Mc;%I+aL|-xlEUbLj+6EqnCGFQQ37vI3yq0uS-j{pvYZ(=T{PjLz z=cpu7Kd@K)x}Eff>n-N5Y85m&eu>}e(9w;wt#z*uflC}EsE=}XNKx~_Vgvxv?@3Y% zX9QxiLejRQTe>^q%Uph4iio4S!=yO+v??g{>x-Dez$(5hQLaqEywv#Dc7elkB(Dw+ zJ7!<*bP(sJir!H-ewLJ@@meUc9sRd^?s4?XgmSu2rr=7KJt&EPJJa~+bF!_kh$%VA zbpFi{82m;U8=>DUrNVWkzpN6^=&^&Ua`%QmlNtO?wq%$*7dpB}#k0SAbU`lFZ(dGy zxIZOv^ymWeNLW%}Z&zria7bz?kle{47~_H~LvZwz9@!NJ(D5s!LuVfSCK>ladDl~e z$FCzo>OK;BG=DvCbU~2KQrRo1N!)6p^rIJ)c{|lVe38?xWf^E_bvoBanRB%;e$?dv zg#aY~AreHUcXo>466|-z%g}yhsBiOIbK3k?MK|W?Vp~O|Jn!tuY3~e|Au;8hy=7Dy z^v*^-n)le~cf=1%+&fM3k6z<(ZkHY}sQ6xFKBPxUCn_JC~YkUZxc# zoaZs$r%>LUId=ud(2ykboaYVicyZC91?$gU$>fTV`giV%AY?`}r;Y?WA~4#q&*M;{ z%%!1F<8w9qdGf)F`ASH{JJ00?Vqj7OzGUZfQTcOOj(0rROBmi3`9FUyb?fb2Scl~O z=u)TR^HOS#-N;an-Bn0Xa(UKPCaOZM+6cl285THg3SdJT*dWWw{V z<#PkLgr0@J)5gE9Pp)59$zAkb7|I1S3haT$x> z_G^~i_%pTfEH&RwRQAOCx7!m|!=EL~@Y~(M%}vD*%lvkao2rldcNlPBq5#OU5&$%z z9XHVUU@D2JFIEQ0={pyA9JeA0Q}_R!o1`7<*F%Hju^oCyCXR_)$8HKRtd5gj$Ja$b zX2jnM|MsjpM0>~ky@#d(J=8lsP>un}>hVoVlJNcZE0xDJ@{L2qai6Trd(m9|lzaS+ z_BqncSu?1=Gla8mhx+Yh!P_8s$+q$P)^H_^q_U2mOzA6xDUrDdB!>p$uIVM%#J( zPDDRmD(taSN)z->=YKbptNF^L-)+g!WBU}69(FeW=5<3+V5)73MS=6)2gR%mUbl?` z(7~G{zk4YWBlbmdNw3fb;==+{8t@K6$Bu!4J>6sdNrI2{hcq4EtBz?rIKE%CHK z(s7~FagQKEeclRX@*th{-$9xB?@;;r?_hHMci6@H@6glw@2+X&^F&+Rl2Mp9ovG@M z`4rn?&%HMPcXuhdSP=?$|EA540=g<9sFG}cB#ZR2DRKiG#8!Ka&%lH$sP9@!wr31N3QoNO(4V=JNDr$3n7rv_tK# ze6dCKKr~|hs{4x_B@4dj9K-q|j$sMJ?xd3Q7h(|o;)UF)TY33o{uWs&b>d466o<2i zdKj%MzG#M&hRkwB#?q+nVM3bYr*z-jV0@CAlAP|8_+nas=JI#F{sdQY!D5FRT#y!h za88e}c%0lWPI&#vC%r$y0u&!z*W-q0@XvE_nb+}<64(nR#(QI}cZIFY!&sSzmgzaz z`*6?0j0TS^)ALc%zExYMZ>J+Wo?Xe0yH-4vJXq^h>F*~n`Tc|C4+F4wpvFhpk!y%F zp%M|U)Ry(CTF+{=s6ef^fAg|lw;?tgbCz?!d#nP;uG)HI;jXDyg4fBK1-CI z9}KNeEO7Y^A2zwb#Hsr!C%{636~*fPLtKV;D3zV=>%FfBec(d2o?g8wD-^BX*VDV9 zTB}sx829_U(OdyT-)I`*8%>ZNPy#4}!o596 zP?a>{#9ggJzQ2$&6w2U!0cGGC_aZ}NuNpG*xoeM*uK;U2zLvEN-RXd(r90rwCroE~ zL$0Tm3G?1+ty<|x4fOQ@hDUF8c~8$WP**$4dMiT|$Bb{IK}sqlJM%lWWh+VsT8>GVIMcIW#MxD9QbXG=de2kKyL8F{ z25}K<;;iwa(0;Us8}HVN9N&){=$uC4O2K)awv9Ofllhuk8NMdf+hf(^*;6#u8E)iG zTq9B+zN4bNoO3F$yROL0v?DXqtJQ@39Mecc*26$w@4!6EmBuNfR=+!Y%2xow6$ z2Qex21e=|~Kn|Hyd;8UypNeaR=-bOrh^$)y0eJ$ySm!zq@pJHQjeu}!9Pl%wxiKN; z4Xo-xDp(;wdLIy~r$26nq)dns{p^D0*M|{nK3KYY$ z5+!yw`(ZR+6A$t0+h8yG7YEcmWP4!EP#*}#b#y|kebeY2H{lw`iEx9SX)rF5)?0m! zwE9$RVuOoKY^V;cP(#9uoz{L*+0MEqDIIW$x&B&D(hGmXRBxOPyg5-l+=iwQ(!l{} zO4xo6^Zudb0#`-H*~&UIP-d(jV$O5qoLB~5*%|X?a_h>v-HAOJU{@kY!LllvCjy!C zhp(EQi0a|1fRlP2zNy9&7a_S+gViu}4ZAP2D5OAvDQyHFBhhn zAb#okG%JDD`^S5T)kCxS{2m1V$N|6;mR9AT*eT5#ev^QkI0POdWq~Oq2sGp z+eN-1HCBy(t2h1~ht_iJPjnWora^zu@o3!min;I>^)%Kyd-YHDta3(aj&r16>~ zjn@=;P_vCnyxRrpVtPHrldz~3uL?#}RBrr5#~6B+^;eN{+WU6T>MVzpV5BXEuOa-5xz+o7 zR!K7XM}$nQ8YJZS#$dJR;q&6xz`z#Eh^&GPjn@Y85k(Mwa}a{)mZmyI5x~X|iv}Mx zOYv+c)d8Ed#s-G!Yv1bWqXmToj38RXy??c&Rf;>gucxQ@ks6?|MqFMg2dF67HB#=E zkxHY2AR@?tH`bCuY8JEmieRh&+{pLzWQ|v%*;pn1PF%pQtb%riy&o~?l}hx&+zN~m z;ReHO%!ppDIU^s1>tKz?$eScrX}1`8Q!s=EGGPRFyZFd!F4$0#6NWGG!iCtdrl$|b zq8Rytur+hyETMiG(cwl7CyB!kkk+nJgE|C2NlnO76fN0puF~jNWc(RN#-EWQ@Qc`u z9X0E5;~B}v$d7?vjQoMdejl2=8G>eh19UA$%XtaTg z^yx*bZM2Sy7b_LsBtu2s^qf$%zgFlEt}q76mOBFAl^P%UCzHfWogZB$E46&N$Tac? zsr<+vijlu)batTHTg^eE-HXKrf$3tjUW~3PM%S*`ybO0-)ko_qsouCy6X0Ux&yJl) zG3(>#DmU;K8VEG?66KYQ3^bN8dPaaEhVpwuen`*8Um2LHxQ}b_{Bhx{H5#Ggy*2Qu z^#;U0xrNt~6f}knB#6Q~FI^d}({lwz{%lGhY$JbBB{~FJFok{@R%4bID{6m%$a<*- zX#E$MY8I&?f3#3<9D|AhmurX{$3$VGl97m;B#p}+5#+ebl1xihRN9KSuts`pGK~Bs z1OU>gZ8s{6>W%Zt;8kxdy4l4@UQ{UoOz&P%#q3!X^&2nPF4DjY292aNUQk{4mAdOC z-Kyfm##!@WI{2KCNvXl9vr%vwp~u`u1lUsbzl5$-fmZ1up>Y{=u5pn7%R@% zMqzt0qnC~EDC|J{gN7p}=*U;fsgHcMTDB3>TZ}Z<^a#NS%fK*p^!F^+t3I->J~H66 z(H~=ss$8r`8v4w9iN|tf6%CCu)rubX>9JCeReG$Bsy|V9jUN5XVygZ1k&PTc>RC}; zTXj%5Yn8LsQH(b^N?f5wUjTU?vsLZU;^k(?lwuWpaDC|r~u}W!-=Oax96Xz1dV7L7B{<|dqla=Ghg=8OL3Mb!j^4c*p^A}XG&$Tq)W>TNx=Cb()RR@4o}#(iTE2d8 z02GRm9mUAzFy$i)A-oF~>+=ywuNT0jw?6Vh0sc~x`p8S3n(>w#bYPr`YG({%C?Z3S zSmWy>XBByOD|2;?8Ipf%j1;RrvLPZH9H~X5rdhM5ffc(F4mp30ryLII$4kgK2SdV& zh^lDf$S{^5tj+0bvETs|Qx`>haSkJ?KKeXU;^^M!^PS|zU&`dhUpmR7KQ5C;e;h;C z-oSiLgszXwG3Uj<=gnk^_X`A9%PYWKGy22cVq`Cjs$v#Wh@7>!a!Q6d#9U~9S;(PW zh)5Jl6Z!u@nl6YoqgPCu(JPL86p@b{xf+qHj(iZ24~W!9-&KT{A|vM<89C=j%$i0# zYZ|e8R3DLhR3AAR#ZD3_Mz-rYdMRLDQobid3_Jhs!ZN~8;W0eN4~_-Qpz655rV#_GStiKOC}HYs>&4f;O~c4swBhxs#=oX zfgIS71GNZLYSqW0IHhyp^yX_*WUD@sO2();vewf$>)!fR*21-zwZQHeSkm-1{5x@b zn?@c-)k(OOS1?Al#Y%v4W1WUEWb{hntizFPCQFEmkxk2%BgEJ$h14EKc^vz{Y;bvn zKRJ<=UdF;oFWP}uDL(|uJkARVTP|lVO7&`ex7Usk*z7 zR_n1wkA5Q|%JeHLT?-{ztLS}ttkh$L9)0RKiiTOj&5X#897WWxq!DL~OST=g8htt(&YFiA)f;xfe^lcI{e5k6v6|oSo)gc~g4Z z_i66sMru|a2M6o#K4*Ens1|{W!O#?a539}A<~bC)?CPpAw7LwfDMPX~L6ZB*(8@9t zOcecAlxcltyrs=XDOn|3qHLjl>|VLdYhA@(#JYp$vHHk$EvXeFCnTEk#b6&e+o6*M zvei+h?2Lzm6E-<4GeN9J*%u5fY6bXf}^)kV3`!enwVms2Hntu^$i-wN)kwfB-2e?HY6-M{*rVzOGf}v^d+# z+Iw8hYS^P6s4YrpF&?KNCR3GS^iLHKLd*VC41YXW!GRedJg7txl)S!Txf=bG`9kj= z=#kv|2>~P+au%(P&yviTQ~V=RUSUvU1!sWZfM;63s~~b-`-Cro%4;R-2_x5w^$^JK zT5mQ?1HlwPFPRrS+<%aI;>YXnhMG5T`jMc z@anZa%X{mi`>0n_6ikTKaFG+3kvsLC6{!TGyGo_*Ck69_Hx}_KB%xMBM}Lf~iRbFP z(I3|$ueKVPFY+H$-C)IW%%|7k9Y^?e6YmeG=vq(waa$p%X^m9+4=&^}Qxcnig zwUWj4X?M5c)qD}YXCP<4G!(R^s2Hn18lD?C_Z89I_5 z8SF}bbP%^j`d>7^n+treyTId}iccs#7kWyO+|u_42Q+LN%n!;oe^?u+$;vjLmGK#^ z&|w){|HQ)Mnp*0&7Jk4pCw?~v23ZGj?o;{|ha7pNJMx4g8OTFjKz^8ue@`XuA6m|n zAA3-QznuA~;;Y$513LxRvbC0i#E{LR`Fzp*$#>WUX#NA=KKw+v{1(MabRxYTT#`qz zl)jG5yKmGnr~VOrE}9L`QDi{HvF0{ySUpzei@i)P6( z(a|E`{DD@5OxJjtf6o@pS5SVeAfEH`YSxJQ(l@rI<**N_2Q$%Nr>~bqwhMyFz*s<2x76rwkh;@U0kBdT^g#M*fi- zaT2Dz$lq!SHWYeR>_?By^8plj*?_iMP~`7HJ6Q0&RXt#ZKtK@^Qm~>#)5~cJX@F}y z0v7e=A3Vo)9pYx7#cRFOBS<%Dy#P^+(FZA*&oPro&yJ7iYQAd~wh3mQ;pe8ZPk;lFYBR}J@C?+2O&>H(^&~h&b&PYF2M3R)@z8s`Llw`>s@{e-E zB9e(bKHznv@Mfh&?@}t7hc)?5@#4oG$Hj zDMVdN58Zu0=PuY4>|M!IEXGoT_zBws&!b@uM1XJ_6Gj&h$)bs!!R}>7LL@lij12}L z4!|V}ZisXV6P~@f!>{!aZEo~nIK(ga6r+DMFvbjsuf}Kqg@|^(H*}xm%x#U{`fuL1 zGgfrO`1Hkl6i3%_8t{8vdP5P%E9w0tB#^;qJTeGcL>iA!1UkuXb~?bip$Rd1t#}pT zP$ym`>3-qb9MZBJu^P)_(1_3e(1%q+sDp|{qHM3sXnLZ`V%7TkdrxDJX{ze->!rz% zy*~Pxc`*7JBUb&uL&P=W@1D$*YbEL;-t27R0`;-}%Lrn1W5?2&Xq-X*gOnMRL>#ptavbX(Xn zB#17JM?5M^wqjIPNP04QOL+?1&V|_Dgzg%=6cSZN<5be9Wxu&GIkuw|F?v1(AN6DCY2q=mG>Hnp0jSd%VI3QZhV1P8@jI>Ejf!?C+fL>Xob=}$EMZw571|!ov~8h!$Yf;#ztd8Rf)A; z5EEeLG)N%}zi@#uYGjB%q)#=_I&k~|K!88dZat`&Xk&r4X z0k)_O6{cgcBVehoRuzNt5EpcY%FK$YJrKRqxX~P<1Wq4&%&NXk(AEzyS@B5l-g8mL zsbI!RlN#$S64?wu5NqrNuRy_+EugHRr@}*cSM(Y}Py%64u42+sFig<`Zl&77kRO-p zU6%;~h8&@Y)I?OyYfG?t2H*<>I0hj8l2molI^v=ZNjm`3Nrapdom6<`X0+U;)XHF`>|a;0T)Fw5Au)k$nfh+g z6k}2sARL$pot%^lBCgH8G8LgR(bV5?eMXK|LNtz1$YWviF^Qw&UE|R$Jbv1O zntucW5^|%xQyYtfg^XS#AQomOS+q%l4XmoAKy!BvB*2U5 zAgVB;LyCErXpR#DK1<;~8p9iW>djdJPOsv&^+;`@V<`wd#it;P$E`POc-Kt&};=V++y z1(_jV!cf`;fx;{vtA;|oj2)AB&n4fGkX$;}&}q4O5@45Oo?{Qo2~4v!!&_tvxVs1? z2RemZizF?^2Y{$`c_5ZY;*h`KTxK;M-AmbPYMjNVY30{AO7BpL58O{?cfv{+_7+26 zo3|LCb4E?wtT)4fYMz2TQe7+1sjNT>6wRAfpi`khr=Vp@u<2-?V#FENq!S|Qc!24A zS>u(cC;bsvMUetU^-8(;_;l}Cm!JTPY?n1A5kF%@&SXRsP`k=I@M^r`$kBmDR5%-n z2jVpmuNhwS>a1P7<~g;qio;YvM7M1^BTY&om;EPEK_N+lP*sw$_(LI5?tRJ}urY1982;CqfHAns&An{uJt4wab(!I)Ey7-CSGZhgHxgY7?TWj{Z%SQ7ATP5Y z_`-Dj2WR~+XMLHozNBjdkjVPY8tJnXsyAoC~?T3Ya72VRDcqg)lOv z3r(E~E}EApsC9&nLV|#1|;Z? zjsAE^?D;N0YXkOu@md)#R%=U8tj{Y9#ID5EUE%&zfIaL^t^2ydE4#w0y25Lsv4lZ? z#N><6oK70^S_7eZX~9)*=$IROaY>I}jB@qY>;(kX>N-74C0T@gTx1v{fM=+b0GQmZ zn62K73TVg8u!x&O{{W)1SJ~1s>+%rhrMH(7z*f7wGL*{y|h!;N@qu9!LaYA0PRq|GaM?GQ|Ll*l?f zFNJ6tj1R0UTiVcRN$?9OFFS17q#$*`H|BQzJQ$@6ArB>==%lNY>9bkFwuU_wB`q=m@Fy-eeu!k^piv!cp zX}MlLaU21SunxJzctpp2Qm{BMt|mG0?AL+mh&|!gn2{R&K}m*b>Cu5{&N_++2c|X3 z5k0`sUmS~na;5Yd8&ulZqXFnqM`H1C>=8%47dhW^2f+71UsPbJ25RJqQ>8RL&{p2b^ zNAdL)TD?azYj{~^&=3XobU-8o0;zFIdU@Wp&7$82P8mjUGR5S*9~cJ$^O2F?`)GYM zHu8HAVUne{T2giP>py9l)Pls2yfde<;^x%qG{lFrS0^AC6*Ybb05IJzu;!hKZ#l>*w z4qUX{;4~jVTf%*gyaAWTAZiH_n9;@}R)a=}8_HE9ao}nnW>)SHKe+g;U*lIyobfC5 zv41Sy-&VZ8-EEwr+{ec~^RZ>sB@*8BI z6k#Dm5X;MjgqzkXZ|p~&exXdt@5E5uDp9tR(=?J2()CoD@7CG9Vv~V=E^zM|q%4+! zrU3z;$9|;0S*Bsn>o;kNvA%w7ivzMx_dP=hed4RFYa1_sRV(mwaxx|+;>5Y&2g}79SO9dX(#&*N+N~`@bo5;SI3L7SwyIhFMrsC zyiZ$-yo}Pg8tk0) z#GCvqUVjFwN`34m+|d)BzJEchg`x&uR$nK|U!3{m99Zno=yPZj7LidKNVcoCHao(L zuD1I0(OWV6rl9bNHvcqjXTL_m&Wz_2LUn<7#YQia5y-N-~yekmzUlQt43JQAO zTD)~5^Piwqv<1pn_^#CU(GeiF^~|`IPnc>K>_x%dyX%gAnb0_L+fGw0ihBoDYQ8df$2e5<_;&pX+0DdLT}f7iyuG zRYXvHzbOk!v$q^Oaz_2+Q{W9fYnSk$TH$c2RT|`BQ0p-eQo14br-ve_)mC^Rgzkwq*9yTU%@=pTbUBRtVfLCTidk#`PAP6Cy%ag2AW* z7XplfmN}NIlFTV2bfJPs+3Im6QSx?&__rA&0j*ZH#Ki!Ts>8QsUfO5%s}JO>SM8fX z>E%ai-cDt;|qL%%mpY#Ydcx@%R1pCA`c6j zh-*fd;RDEe*eHiTT@0^j&0IkyC?)^=BH59=J1p%(lQMcbBP zyy7l{Yiqlf8C-`Ykk)oFu%+Y3x>71Rw6n29BK#Hya6>3Su71hLY~XD>H@m1KWUcM` zwIAhiN|jiOM2QO?@RW8>raQz))u~_s<%S3eD8NkP*7k47K?ac{R|W*Vmz8Id^us(((e}pc&58IxsMq|hC z6s^mH)eUJTpjO4qm#OD677gP;iaFfUKOFD4oy@~~yiTWU65=N-9erHU z_zlNg=Ze?vU%vt>LmA7~fWWXB|@nX_MNsFu*lRrvb~odkFT=zYI_rEatmN|Ra3Sjm!rgbq51t@?2|pmAkKwv z+1P#~**ZN(Hc>PyOnH zWvr~^y5Cj08Yz?it4QJq-D^$j6GhIGv8RDLV-@PNclbLTQ94_*@d|d#!qVO)ifR-ZkQlr@L?EQ-)RRN2 z=p%-u7;B`J8KnR49-B3!iedHbjiqXBC}uaxTPY^?|4$u8kPVbwphV+E*_Z^Ykqu7K z=5CCMhlav>NwH*yE)=tZM9yHoZ7JvA-|Z=L2T~ibH>tSIaa8@UGT# zA7_8P#k5*Hxyc;0bAyL7$@79kj(8RuedNCU_9o^qh!3IhZ(-&Hti4&Ht+duf3IJIR zP{7Vg`+{XV}zBXyD7n_fX9} zo8RHr+q4-H**afJWb1sHxY$WtM22x3Ca#m@TZITF`|7)yu6t~o&<;3p4QLczlv85K z*4ZVisb&yxN1kCTh11EtLp8JEmK_;6Vk051P5oqo6Z{E+e4ZTYhWpcO8*A2 zZtnta7LP2+(At~2644~wB*_#fSA>ca8~*Q}xQomb!)KPu18XgwVle|@R?YUV1V(=L zBT3&fLy-e_)+)CtY@E9dC-@AUA?=r@AJZhU^19GRm`Y&UE`>Yc}mkRe7M zBvYHHV(W&=zV$L?1+BfwGAg}5Mqz$?bBLpZPSmsq!6chsuveXyCiIBO4I(IkBD@rt zd(M%$=Tz*im;K!5!f)*}ESsVnYCWSmqc5shd&tkTf6sI;bJcxoJrl2EU7ClWoDJ=x zSJJ*VUh{r>B>O6wTOqx`Fs^B2(YkH<9cL18TZid8)`q-`REm?CGUZU5tDRaGzayu) zG)i<{s8e61H*|w&;Z>J;9{n;Hm1c;l@WQ_gO#$|JA0Wzv)}4D2)cPGtd@I2M9f@16 zUM8#EoD7g|l(G#Cj*egZws_yVy+mRH={payx)L+s$hBF=xxsX3MjEB~@J-QiZW9Qp zX~mgYXf?m<0r|kK@WHakg$O~FR{9n=oYVhjXf^$Cvc_#j3&pZ~+vPTA7dN@kMD5%< z+PeI0W*ss9Ie!1zhIFfju+=b(r6QA<{Z{SM=z0%gn@LIq2HrY0%9R zg>U7dJ92VzSz-3JtmNOSaClB$M6TdL3`($YbNoEztt9h98xnX^i3OLkd`NY*IDS@( zDM7?z^aDQG7`2YE=7tjXaDe%>^(^D3B6>Ne+Uv!AGCL_uQ}L2~U5VcMA)Ko0s0!gV zwN3z$GWM#F0A)x5@WWDwL~3DFZdD)cL3C6JMb&`s_Df$tC1hQz5>sL1saEwN$++a< zgkJWeG#3XCPOqgLGMR5ukvduw#sh0}q)b*KqS*wuktSKKJ#q8UQyNb+eORwXp3+sg zlAkPE@)JgW$_(c3tmt_}xMzqE1bjg&R}_(W@l!`o#WI+O5_T^&pu|KizXQvdtr3Pm z5$0nfcT*E6fj_kF;hvTEqzG}51_5b}dp0g*hrmbD9P5AdtfsRa5V45Cg2`nsTA-Up zZhfl<#ixvL9=2_xHU*UIG)bx}aGpg;pwfZkojos&0FevGp#4DPQU`|`d;-B-<{_p( zYA1S$A_yEn+gHAw*eM=~Wc+N9?G4;|uq(?*1ALI5!s=!#&A?WOzJvmraRR*Tr?6=my7D7upf4k~%M(nla@CP#E`)%ms#URgKT@37FGsQUjaXEH~m;gH2 zh>yLQ;$t)B?ARePy-F}(ZHqb9+AzLyk0fYwHxRV0rk?DA zW7lndSByujOF^u+Hd8qbKtSMNG!7OwMDDG>btWX3RDDLoKr_MVjv*}y>gpvot~ND6 z;R?VZhJf3}nmSx*J#Z3a^9DHG-q9HN<)p}S|=tS9X$};LM zS()4)mSHuFY6HSrh;xpRcX4uWcwx^kL}`4tt(LGjjb@zx2b%WP;bncs#z5_Gmt z+8k`umDlMO4JL9#$Bz;Bz6S(>cQUf<$C`uuN?637&2d%HUEhEZ=8Vy)Ab?Jg%6%B5 z5Y&ET2E&%*J;}Z}qpM&7^Kg$2;->m>5#Pw8nywZjkD6{0vXMt63A`rBAka8(H7PYPU~sTb;2l*@Uyd;}D;fl85LGu7DAo zhjA)<QL?2p&i=PD{(|&i{I2z2{H`OnBEo8w zy3gz`Rz(09&kBj72&kpy6Gfw?bG4GQCTyLb^Q0LdNzw z!t`*@w|$=6vIh2%nq-f&9vu5J*SGog;dZ2kQm6uerPV3SHQx4t^Ko%C=T;z{E|mcn z-CWb1++F3~Javd`1v%y6etx(EKGzUW>_@HI2ZeqDnb@{ZV^;z}Ou`NlX@5bqt|)t|LyrD`B)Jh7st3r?|2QwBYL=i|}i4cW<3H`H?dW z7g=+(1X5^%l!(s|mIS2fTtD}{m3dRRnYrX%VSHv;zbtd$Z?Ok0U2rrp}av{y4G zJYZul>aB+mD2*drx9Sl`H`70u*I&zf>bcXN&aL4v9@)LRn>8sTQb2>snGChpP~er!7tq1M&zWJMg{5P6)C8$MuiAAlm6 z10#=`&6gb4k&Q$TA6B2ZEL$?6)3cx|HkgEoYp_poB^$<7BE{s>RW(^HCbzikev%LHG&b3RRrx)fMvWU zw~7v!9}2IwtK^`Oc%D+XlRS%kELMRCF6QJ$mO~}wZrQAyRMWlW1P19Rw0p-eSv(&ScQg)7G3VvmfoVAyX5 zvERJyp7%>9@Yl%I-9G#ZDiP1yCbv2buU%ld_dP1>b`)IC#^)fbBOGQtGQrPh$hKC$ zNpFb@^cJi2vBl)mG!GHs890i;qZ|bW%=5T5t4v>oYD;+__bDzP>xoN=dn>+DQQl^6 zWesa^>ce&Jgb3n3nt@L~EzOvGPVE8#JV0TKrZ9B#XV|K*lRc+5+`8R3t=l3@1%^&; zG_f-aDV4?xCH9Cv@e%>gR$U-h>QyC^y{e(y)je9`Zcri5mZaX-WmzY7t%9tL*R&d> zfs)Yh*Y(!-#Jkqx;SI2^q^XnFnJCSa=5geM5`^^PIF();vU-$q%0?lh9Q*-?sXqRo zbcL5xxR^YvnFI#v5`Qi-d9#?5nvr%h5|kAQmn^hlM_hws=s0VOh_+=Yf?G@;%a(KU zQmES{nUKjjD~o{&RbUJ8$xHa=Jdr2;!u8}O(ZKL{(u_b>nd!ubC(b}OJ!5h$w+>BG zC5IYMajq@&%GufuO_&6Nf&L}ns#-1O#YoKdhOl`mkV>6XuiS}a9wyVs*wM5*HHmK| zQ=Px8gj0R$5cH2erFEJzPU~fode5KRIhF5kpR%W6w(-#tUPc9^(V@mu@DYJ;JawNF zD*e1tM{15#a=+Fx%2s;2V~jJYsA^TaBe~YiAkNL4x0F^lSC{obNTlK~cX^VNS7~{5 z@>H~QzR|YtN)abUP&$J*3XG?ecIL*FbJK$d{;V)hVqybiqKclshgn&#$_Zb z1SvaZ={=|Hoq|88C2SZ~rmXF*VV{ngA~6td^P-vC7;{+%gjRzVpmY-_O9Toq?XC|cZ`UJ{VQ|V)t2>j)rwrKKYh?}V6(4-#Kq)3BgE(dwo_a*q^!pG z@&GV3R7|tZrPZryed;kXc~WB;uT?!Zjm6Y}1cj1QJ3KBb(Z-aqooiW{yLkV>8lQLd~}M{uhjN}cibEQi^LsH#9Ns*TOVD3rlhehiLc4lAUs+yEPP*zFnL8`L1Ox)Ljv#(5??Ig~YiF-PUd+Zb-GmYa6 z?!a3BB)w*^*qfb%sr_Zz{>Yjwv!;uwS!>>Lsv?FwEmy!J7^N3->4S@;xs{V$)lRzF z$xgMCQSD?xtqKKIY5Hn0wNC+o+3&z6{1n4$$|;f0F{P00kQ)_+_}J*DWn`8JYu7<; zKEL^CZ<)2PyWHOH2w&fHAPf_+z8$=*EjK>+;fe7BW|_jzuMxY_okY2=0-IgnstVj& zDhl44y44k~sltC%D=Pk1xz8EEbpiPApah`*t`uE>wt)NJg=t2K{z1JP-=JPjA-|a7 z%-F%M1Y$S8Q6ZdPk!D4!|G%GabE2U_V){r)(7(elc2MDQk zIgm6#;dG2i+INuwD;jjnUnZ$kcwyWGq`lit)YArkzd%j;4FJK`U@l$zskYiuh*xPL zW^jHeYxFeFjF478lxlJKia>Bz8=x%p@GFN!>(M&DRVd$BYeHHi>Rxw?J0o=Svwu6X zCw@{f-z34-Bwm3@!Rbk<7xbOJYtl^L?M|TQAFTtI0kVbXN9zb^<#2!?_GiGhPmw}k zT)a&3(mT}tXq^UHiaHxG6_qz!K~x{7?-J;~0Au7Ma92Y}*LhD3r&xB1si8tRNz5?` zrRkz`JuDJpRK^7w#3|82yDzbOxCb{u!#3k=Z!vR;wM+W1wM@m*cs{)da*8v4mJFI+ zWNB2+;V3SmM;d;EYkHTlCZO>aPTi3PO!p{NOLtmStG8a!Am93#LK&FyU@JT1@vBQ+JUAFKDzlZi;5M zUM*S&+yjTK7%eXPfxI05;de?vbXwx(q7Jc^`f5&_*r0z5W zd0h4;44H!__Ow=i=*lz7LuID760U~I;NM7Q?B=i*Aa4gduvLth-bRG5aOJ%%fNZ6Z z@!Ezig$A%xgCwaWyZwEB7c(()ffAG_WH+3onP(7A0S=y37kSxvM5WOicY1o47;>=Y$iW)oMq_B-5H#GSkQ<=K#LPN-Pt*J8jETr^ zpB;P-9$~WiSv=LQxjHj^*21TEF)If_-IY?GzQb{SLr-f}B8sXd=};e8Ixc=B&wGf) z!a&o)SnVCs!n^VCYif5|BS?eeOq$vyD=-byP%iMC-sjN4C&AcVeKNZGq|M5~yQbWb zDW|X~H~kWnQ$&k%T!~&uZzd`$6jz3Z-=|1`!@L=1;jaP`X}W|^CQ@Ls#J<0ZEVNwV zWhIvB3X3(ph%*^Nt*!wSm1=#a%c!_OjzDA8z#2lESdVKw+S(o;2H z6(2Rz`UgYW!Od$JT0E5`4BrI>9{`@|{>+Jng}qLD!u$A>PnwX)b?}j!r6> zS_;fyLp5I3XRvy^+)uy2IAT*f^6b(0l##m>C?kSyrJCM_Qd?~9s5K&f&Gaa3rbpck zW!;gzM~2M!ESr^T%JQYQ2i}=};92+?JcOsdt<@&Qj9ec40KKrIDRzl-k%nv=kq;+e z#d;t00L@~Nj7QF}pK2RaZ~-}`L{{HZRJ1;iJFV^HkR~FvzEohd@78PT1AOkvBEydK z13mm=n-CRjos00&-$$n$pLu~73vNr=!@(9rZuWXM`Mlhc;U@TM2B#;5H+O{@*}&6f zx>{44_aokgn@KpRF(-Hd8qubi za;4~6buMf^$$2D|YN;gnQcilRM$3wkAbX5>T=Lj?P^LbnG4xediqXXV7?f&yRP#p! z>Hxh?P5yn;KkIejYuSjI8!Ws zoH8M3_H|a40U@7cW)r52r;WQ#A!0W`)X)0Ze`@&*ir~rih5~KYozd#6eCEdSu*Nf& zz)jl33ngOHDjX6IQZr97YFVhs!5X=fGh2F9NPwVV49^1Rt4(#>H%4n;P~C}orpMGO zQecAiepFVZX}lI^mR_%D`lXrOfEfLHzc`xGrWeaFXE2npG?!EE)cZy+``H{!r)}!o&!U-7mAF$)4RSxUNcElKddC306%w8Cx@q2GEvp>{T zA#8!U*nBqZsSO(rWd_&!Q&;%wHP|f_cNom7b!&NqJyjlv+x#{8ELwjQbC}Zb6B$s< zY3qyDpJf~Wd9y_GpEr}817D{=+} zD#GV*V))YhsDqw)!cOU>*&YpYQyZg{R%WDPmXn+&`r3M9*8fN#ti4G>%<^w~X2Q4r zor4@^LF?c3vm>HVs*g!-J}UysVxoBb2$Q(g`clNh&EozpO8hhjo>x;OVfcSf*_`|$ ztgdj!evi8_{2%7CEfz-0pUCA%@>ge`%v#C_EJ`9(Ry9zL0KTS^P_7~JUw=&L-eJwu zh?LNlO*uE2;)oLwPa-07WGY6~n9gjm5YC)YV>1iv%aUA-yrNMYm55c^k)M@bjZ*{$ zmLOJ7s7#>S)rpax+F_+TGg|irfz7#G@N$NQ;4-abZZ3qHnH)0)cj5}3I0GUh4?`zCwWlTV4zbLb`=?x@y|R!AQ|-MT+AzGUXn#&o|w+ufn+A9 z=!g>64-W8Cyc$cj%1IVzG9j|5#iIzPdtYI7v*OW#&^mHc0+Bp@wNpgP4JmnQPbYb54?^IJ}FUjn%d(!%#;;hyO{N|u_h6xgNnUS$V!MX&*Z+#$fh9tOfi+-_O zfCWJ!TBk8Mpu^-UqCvQwT=@|V14AdU9jpKIERh<^LVcuqx?LrWD$0?lO*O5hYJ51% zk%Nim%;hDAU)#;`Tk>~slY*MoIV2t#iP#w;ZR!OfOW@!}TMmJoCAEy;#+@u9d&#lI zQL+6d^g%7=1WERhE8WllN-KH`xQ)8w2RHdO^+LoJ(QvKV!mid2Nn%%TKox|*NyVqQ z0Z%WQjh(Umn(iqDHN6^YIR1S~aw7bP>A+FU*8KtM%wBVEX0NDsaC=Dgc1NCzNW7+x zAuG{^1(xFoY8$+;zlj0H=|h9)IKt1?FuK!SfKR)?E||PD2S_l#14?xdB7~h3vkaHM zOG^))p)sMzlu%ljUI)*7M>bW?xk!I1UdXRvJK`)y%82EZn5F~w^fx;RS$ zm=4)K+_O&7eXsfT>5kX8#Ec+=+92vki@;z>9``PMGm(@$x5kQ3@ABteuRke}GV6X~ zn?a^b)L1CJEEAAoi_pkVgC1YmYR!BFLq$H;k-kpvKLt-Q?{JJ?SLV>Axg}Y|67BYD zCU%L?&Z!qrjqpa<{n$+;8hGhjfc0oW;?!=CV-`8JTcd{F{85fom)&5N6TA$wyR@fJ zoN79*fW-Xsl`3~s2HC{FG8ue&$>q2MnXz=A-~C-_p|GtFO68AbTQgp|z?E6UU!x1m z#!p*aG$-2uj!LkyunN1w0WR}7?M|kb7Da6GqV+m6Es46u3oiR+A!eOE9E}v9ro;zz z`ZW?~)@g)G=|&JiVfSW;=QTPXV-X|FZ7LuNBw2i>cd^i68I%yXXf0*<{wG6831ld` zOJ_S$l4@Ym8sB&A3{s*a_^)rZfE`?XSQTD>#xZAXqe-uHsnK0b%YB|EfS|3a%^F+8 z^UhMFF)SE~|DUn<0j>JV_P&#-^(56a=ci8Noy3XHwD;+}*K+OqI=s`D4p?ZR7c6w3 z0~RbOw9tZr1qWKNkdqvf6BBD<5_6(Su=n*o*ST2g+}ewMCj|#Ac)@`TIAFno1qU2( zzySvwaNzxXzia<~=S1yuZyV2V@3r?{d+)W^UVH7e|1}YWI#GSqy@?iaPYhTHZ=;iXrqtD|#>EXnVI z*yj0h^%8{IxP{Aj@VY)sl4WRg0-SC;6pC$PPfe{w7bf4~cQ1nL-*Q)WmU){TSz?&A z$6dRy6E=FjNd_R*Imt!UU)QYIiI5)!J zLwaii!oU@fXb$6W@$&`>tpA7UWV(`_B815-ccr?MPa6km1;a<5oq}wECeYWhOE21J4jT;X*W`{xtkf$Mt)%=Ta&LO!n62S>bcd8oGJIW-NhF zumus3UO#LulS_v<51JHP8MvC`dp1<_ z#$QHjM)xC^*ILBpKD8`L1Ls^+@Q*wLwxWRy9=(?xS#*UyI#*P1lc>`a&)jb<$kl!I00%4XQmQU^1=MS83c41Kkl>vOqOS(9t;D(A(NCQ#n5gW;muR9C07F>y@uGET7KYsj_!>e zt~s<3u5wUT(mxO-3LQg1o{Sde^aAG<^6`Ti9o~E}mPL&k0ZA4cTW@+Hh8_Hxlb6|c zOjM!S38DQy2^e~ zC_MhuRkY>sXbu(!!Ia!juDL>7Q(itg){f4EK^zxa6TjYc%E3t1UC5+%iXUozv<~;m zM$rJ2d1nh@Qyw|pMsQblwBK_)Ljd5X-gF_wz;7w5Ykkxgz3gSQnGS*@hB(q*!p;t= zGuhG8aVQTu?ZL&#Mrry-Wv8SHZ(HL&{8px;WZlj)_i(f{lwAV`B8LpB# z72o8|Bb1M_&18iFaFM^RKF&_w&*m|qQmj7RVwKH5g`2`ysXTcn4#rCg_a7pR0Fn1- ze>NvFW^-$#W6rJN&+6&P`$FYIY5S=D(Hq#s8oqnWe7Et1?Sq?gWIj_DXadTq=jVXl z#L4FtAI`5wOu@n>unIGN$AzB2lID4+-~s7L`3O>6R_(l)vXa3bWupAd8rPMmeDpeF z6X0}OQDP6ek0@vfZO(>2QFw#9k zc#gfdt8Il|igLDcyolnFJeyiKcdgL6b`jYBH9Aoq+P0~6Q=3i_6GIEKhD(n~PE{gz zH-W!TjtwwY;|9joA4FJcA`uROK#6-0ujJOsR6r;n2#=hflre-!y3x6rC=g#XG?F|b z;D3NDQL%}}j>^y=xyly|&E-IXgJX3ojaotF|lhcB#h@E+B#aIMMb?&DC8 zpoy+0C3JL^<86C&RGT%+J}CQWm_^Zx)Grmc23EdE_x?t4A8|dWK+Tv7BwLCK7f2||cSQc5 zDaWF`-w_YpcX_=cp6)5_OZh#Bq9_oOwkd6q0lXu!&@{B3)!qE!rXF*;2xv3P_$41l zjw@Qn>%l{)Eoq7ZFU|lqB`~5k zI&W%i(E1Eu5hWVJ*HDZv;-Mz`D1SciUyUw=&sF#OEHiA7>(EBMDezdz?0=6pyHO4_W6-BWE%=r{*$%uDSHWWwE3p}nyOQY|_2kSZRvJ+%02NZKm7Td9 z!HMR~<@tHE#;*PX#Dp=MkW~ssD1gRe^MZ#qF7?nmt#y^R7sD$^g}JX9J|cjZ{-OtYU8HqdmgR0A6a*3Ok5;RH1?vw1mPOa$#dDUDnsWJNJ+&4cx@ zVK>KrWIhoYGI8_VmM+yvX@t5T>BT6a3qzlEj1SY zYvP1#;(!}(;(#PO^_kE*iHWD}6Z2PvoeB1_Tf|iS`Ku0}_YRO0Iem`Zk|ZoI)YQf- z`#e^-vx)1JN$fG25~TPDV*PUJfMz9~`BmxJ#2Muj&43Xk=pR6mZ%X>T zqD7aSQ|$S>k>LAjX7ErjNv%`OTwrfzQNXai_|$sLxGDCA?ATL0Ox1`q)#To9)gZ6Y zvC9OxfE2lK<5a&>PZGJ~cj7`JbLL{RiKx#e3m7D5^$+TN6BkmYC~pct`bUJsTe^{3 zc&8575F_#-Bw|C&)B(&TTuOKVql>|g0yn`*6XQ>&!Y-pb8Btg==3$tE*x5uqsEUc& z^>f~zob|{H_B{9pMKEi{3a&Z?YwOfq-OW(4XpG3usA zZ(t&xsL>dLkQ#Zxg+eF|-%aKqi>_?`EOjzJpSQ3H=;RgizBfJ`lim%Ix!B>RBnjYviHrWw#3ubik)K_THblmE#{S3Uu_4C)9{c~bJig@E zf0<@m9~0(WfJ|JZME{LM-%oNdMTUmS1%15*p@C2V%^7TcbbjhVk0zOiKBP*KrFz|Y z;*%HGk~Il*n9b0CNcUB{aZRyAxb#^NC%7Ybw)ytr=VK3Cv_=sL&4Q8>_5i%}KbMnP zMSmop|HF63dx$X#8vu8f=Oysf|0CV`7~(WJ!zO0#Ei6wtGrK6?9V!u*Li1`4~T(thr*u zTbAL5WR_~nTY8+>a8B61vWfei)5E>KZ(bE{4hqK3gNiq@l*&P=w4pC73m(5jmsl2D zn1d$nYp{AaLT^h5Hx~-~pkox}%5|MW;3wp$kSN=Bn6!Ne87p05ex=K5{)c+Zi3V546=wZm}>AFTZOe!gxYeQ>p(f$>Y8!!87U#x z!cI>LxdRcOrP{(yaoofW-F@gdC^6wxl%y1(R4?qz)$4_wp|}c&)eDshmr5f=&rT2Y zug5_DI@=}ECfrb-B;I%x5U45{(x*8DE#^UJ{tn#2&Y*aFP%drZQX4!F;Ak_t0X!%f zEZ@l_Df}sZ0)O9SVIJNKBIB~iq*E^J^z%~>9; z2qdOMI9OssawM{OSTHN2qBcljsiD#Vk`d2SwNJ;hPsfPMeFF(M11vn{a_M0U-Ciuc z*sSqsEf_L+pjlhnvr>eyX%}KCN|8%!OmxkGy;5k|5eus-0YuU32>Q^Sc5Y!%GrT#; zaskf-i;n?nG~f9zS*Y27<3Bu z<3PTdyE(*FpCq{=R#;u@TB zUC;X0R>fl%><&ATO`XTiY00jC8uC-#WqP4o=|Lc`DtI(%&u7f+aIkIwAvNJwuUwb3 zD|NjbrhKQ~F?pxniE(D?o!nU9F^fUSiV1P_#wij6{R5Coe9FH|KD~MDRN77{Lx#)l zbTLe*p|O85b~IeNj@6dXV{v}t*cqSq>5QlPq)#{jy^)Ex1S@oIi}@#<7|mS`QNy^R z8o4w#tz!EYQQZ(Kem;h&r&?|iH)>haV-{N4+61w|tea#wV%KZLsK>E3JQxw{yB^oT zrd?AY)7Cp_(%vhFpUwt(j#ag_6h4%b&Ya2!Q0%8eA>hcJY`{z%a|-c!thz=W*i1yS z@8$Jf&Z3%eM|u)yJDzj0gD#;gBQ&mCVAGz^It6RNS#{B!8^=}aNL6V7h3lN^K%8RV zFX}^88?pqFIoXfHpvivagnzQ%tufgj-HML1yh84bp&p;g zW~PX5;dE?oc1$nzp&_ah96QBb-mck$5m3|u4q}K?xY_9xgjSB7ZIS!(tO+3pSDHA* zHa}A|jry7MSLb@oSw)q{~|ER#&s_ELAMJSs#&~Yk}Y_&NyJZg6@55Sk5 z8}j}s`!fNUgDKz>ebh>vuekWdY~rjUH#o5yooo-SvM61SDyxO0_R>B( zm3T6w>3~iqv8L}62fWnM?{l;`2H03AW}e*dHr$_c%OqCi{bmQb_7<*p3YW$&P!h@s zDI7a};RWg4^<0h|;_$u8eD5st9msttZSDnI;|l4?1C2UPY7nK4@3@W_VUabyXL=HM zPKe2Ow3KS11Z5^A`$)CdsrE|mpWGXNO&GXyq`Xj()eit~^ncbGG zpH9BdUxex-K(Q#U625P7YFETMD8i&J8`)0wH>a36m#2u0B-v!OF`@}iKm52HA`Z`E5N!e`jk~i=10h1)h>YhmAfv?<8C9D|;&zT~b6lF4ulti_cOOkRqCf4pYx zi%79Gg8ix=;)JhFjmc^(xQNWrl9%18H#;=K@B~pd^H$N|2N})MMh8xOlacsWmnYHa z&x7EcQF8U2nc=oZaaD0#>#^RU$f3vZncTyzjxh|kDn_)1ojjf@RAJSk2xCx&Rz)LW zw8c$wo`TQ;-e^)L1B%Vwa`RR;eUaGtI)k(%`hUU~ACC@}LDrrlONBBB%&ID$K;C7EvW0o6Ts z%*lJ63{BoOOz;h3p1Ir*T#ela zlLs5nYlU;Ft4l3O)ux!H`BQ{VHa29<*vdlD^VpNsphwlksv<{H`Qm)(#e#9=53;pCkr&lFcgzH3DQ7Q@LNb+a4vZ;Cakt5Y5FocJ&I2-#gc z%3|g+4k(4>yys8_-urkA9duVNkaS;9#9kZRi|kNXEIR!Lyy(G_WIEuS zWFZsT$?J^>*P0?gWw^$1k;_H8gJzdK64OCyI}H@FM07iI;8a({1eo{^*98^I4Gb&d0+n^RJkQ@pOEh)Q0xOUV6Lp+;=cV{)n~gsULw8lB&7qFk!2W|L07 zJS=FPhpqQKpp(~jMCfgL(M?a%G2RM5gHLNlMq%vN!(r#?z}eIoR)kN%esd9Y zNh3r$G+g=C15D-HV$jL@Ki4RXApRDol`(nfv6GwSBZ3 zR3NrWnftKXHET;w0HjkRlVa22c-gzvXdnE!;mw?&2n9ZRNn_+vNWOFsQG<2z@o<@z zHhziNhyz7IiQ_GiC_?Th6jnmwXdLkzbLN72YU&0v7m6}WULo!V&AW1TQ}1y%$gL$& zj>(!zXaf=rCRqmW+Jz`xl{?$8UUPyu;4*yOX_v{9!J&%*fx}6-*xw~-9bhyPX|Odk z6=TH3U}!-|)us;Qm7F>dmq$sbNJ>ubwB^zpK$U<$qeSzWI-o|!)-$D9MmYFQZo~78 zW=@IC+?%tRQ&R1w#!G!jmM&N@dgauZB~m??QzP$}r|Q&Hxk*rsyx+$)@f!MzeTHwU zkNJqdKD`J+T1;${44~&T2@nRbf4`VM*_$5c|NSE3j4SE{_mtz`F9N4)oL|k6F?|u8 zs`rY%meu@g?~p_qdYRIm<)Zs`9+YcY(auLZ!m_Cg1R3wp%Y>r;=oO{at9w^rlwpI3 z&KyvxDbB&ldolNBEvtEMZZ-RK^bm$)H1g4frji}ZI?Zh>O3DpVb##<#zhHDFP!Iww z=7wI*hJM;nIKS#BQA3`x)1!8962ViofKAo>4qi5O1!&kooY+;l3Jgsl=s0z*gAN{v z+40KYBQ)n^q`lmA~TYoxjzNK6N%e7t5*p zg*cW_K$QyGf&{M*=vpdEUVoF-J}^}&UG0M|{wdqq2mC#*UZsjc)-_@_7ZFO56X(ZX z)@i}j8ol(^;Htj1Rw{|oBK_#`+|m96ZX6JE1o)xf_O&gmv#INfAir!Qtx_fW|h=_vX#@EtVxhrz&(KNH~f5-&MM%;jQ5-^Ma^ORtSHO1~q@O`r4s}SGB*mtlsxP1q^bu(^9@z6uy-}5>4M-D#-~UTf5vNVK8P#I|9&%@?h>Dtr(cNA*41#^^m@9GJHbZ8iCdpK=X4YwmZ!Hs z4iZz$^7J;mxVz8-rhCfMFSWOKm8W-ic0Z8=%G2B1ySuyT#8j%My;PolP0WglWB_?! zZRJ3pQtv>pb-{*(x4Wx-4YCTn-QDe=$+48DU!k?~fNr8`g|)i`=I-tin7i8z!DuFB+-b^26!Yj@^j=4wOVQ_2^xBt?iS=q}*PQmSmQHj)lje=Thjo9B@7X&I2g$FD|3gqElW-5z$mH zTIm^HEF7DISwuv!MyM z^Ym>;StFH4LvVI{O^nQcUfBXJam{BXO?hegp+C73g5_(J9NfG!`%Q%5DZ-Sd#x=bZD8vv`Jgg=?F*$j-lZ$$_&{vLM5+)Yi!wXTH;Qw73j+jX z614U;e=7`}dO?ArCS6!Xy+fZ&~ zC0^IDMZeQ`|E{kC`wv7@gJD7dM8*)Q_O+XFOKQd~O>A(OHGg`^;WY&}99~14x;2lV zW?7Qq+if>;cMT)l@pD9FOuH(6s(nHy#KCV?^EIcnPiPwj^;du}S`$a85c}>(oj}1x zf;i4FeN8=qRMvY;{7;CYKqbvPSM7_e_A7gMPFR_J;y%sHiKIm5SyeQhXkp4*KK_o% z0HpR6LG*wLKwah9@AOYAqFL=fVFq+v;EZ_`;VU9_^_FYDWtUHzzKX3WAx^0p%^=py zs0~x|6i&I(I;9obG6Z*2M)1U}oNPu`ItGRD>(t^On^uMP!{^@abg@0RT*St0D(Xa2;-;7 zmXfiVjD|X$O`rB0QiuCsdl!g4Grh#*2Q6`l@@f^950-rlfs~XkjaQv}nWEw(R{w71cr*I*2S!h$i)rR2q4cVrI$3pb7&cULezwne2Goxp-j> z0h$+Oy2^15b?j(G(1cPx_(rMG(w)qPKLNHVMO)fEY$ZE`6VGn&R z4=2+@hb-r|pv{X(Z>a}lT58&(c+d;;C}qYJ^_seab~v#V^XC8E#kw+{0c=rmrA)d? zt;sOu5Q@OPErxACu*?qGCpFt!y z<)z60-^5aBVpX{ zH1igRBBOQKJ2LZ{cNxv>R-X9MssJVHWM?UK*Pim3E&h7RfeTqR(6MZxt62$6ZuK_O zleL&@<~8Na$DZBciO<3nqLLLy2z7X;#3$0epUoVURzK4p5zdDtkq9y90vFsNX3jAR*(930WKY@8rz%i0 zH8mD~{vt>pDx>{Tz%TO`@XL93oMBPuM&Y@Jr^EEP(`z`1fZ3BDm3E5ApBA8}(l=*Z zOa|K*8Ruqh^)N_CKYDRRPYY?4nQ3D~Rp7SR)s0qg6PJv}|8?13cENwx$M;A-;= zTTN*N`yq1syYWRj?~X~uU}~iGp<8Jqk_+naR06ibmM95(ImEhp2Yx126K8Hl)o&IZ z5N>Me&FWlc4=qyErp-4q*Ub;NqCIDBl~oaP78+Zyv_UqTxk(Mv*gSI^%QcEf7u-eM zv>{Pcz8i7uG}b%*z3*Qyw$I#y7l?U}$P%(M_x;wAzT#D7#8M~i1+h|D)eGvR>C-);6dQ?*I_{-v$XkrGcOB`kO9SMQU7DDD2hGvM7HAfdW-9X_@JDY@9# zQSXyVNW@~ZUcIkz{<1vM+q%j$W{O&`ktx3nR5S;gl?RUc7ST+81*JXhIxj zCu&Iu@em$LSjy^KUC_AQw7xAH@ZyXdTTP;E*vPc=MRu_IHYCSYDTz3Nd+^LA&(r^S zi_rBis<#y-1nyEzl>=8kF8Wj7?q=97Ax^2N9C-OgEo(%>N(h9O0hruuw~3*zepNw= zL+VRS?o;7r?x0QuyRn&_#JlLF=Br-96R#fbVI7JXAq}cmS(IW+ zkpT@ofcR`QYVB3btGpK37Ou{G_YoVB#2P!Z+KnLUPRdcPzpe&p<15+8N3JC9>Zgm2 zSU+6=wTu_Fs~;$U2OGd*k3@Zc1GvA4xw8S>8Re+a^F+%MeLAZj)Jmsa2ec>_QFf6C z8bZ?;F0;%X~Xw8fpUuRnx*h+PL2D77_0s6hZ2{3V(n2&8@f(c1pUi z4_Sg)Uv+#C%IPv15-u?xiU&bSKqTIWTmP+@Y3wRT27+lfaT8nWuy=;8^^;&KB|zmvb8C$H}=T&J6(op!YS z0&!s$4{poly)`^m5?or}FLqRei1)~n%?M&rN|4$NXs6dlwK1)Tp{s>a2k8PTrB+&{ z$l_;>tN;_HEqUZ=mUo4kqJlNmvWWN{STA{*n#?qbXCIC71>}shsS!HwxA5HW9s$)Y zbQ(O)Mz@^t`y+nA>NDqxu(VB6th&1Z)^`{D|9%7beh$pH1Cc0cH1%j5vsWWQXms_S zJY9b6mf70M*-z8%n6)ly?sG*>sUu|dQ+eY0O@prn*fzHMrSM$xt6q6(&E?%fNLGk+ zQ&N+&X>wl)Hv|Q9O{!_`!ieln9UF!mW>)ldMG-laz41#LJ4xM{&Y78Iki=XVCO~RG6n1wr19D~WX(m8FL8owRIitSE$LduN76@nP-TO&>(o0XwNkA_;reiZhO zr3M%jeXE9_1@2JID>=E#lDr<9IN51kuQ^DrOA*0+0;8=n$pCtpSMfm^StE|Vo;#0z9^|-7q@IQFiTV4kbDF z*O}gQ6Ib!3TlH$T0i`h}&>DMHgW0!;3-Y&es?SP`A#>V$c2@yx3=*@uBY|uS2)53A zho6hf=svs1iUVcfipJN2Q1lvY%c5BPEsMTTdPH=IYyv>Fu+*n}3}jlJeQSA4q@)`` zy~GMD8;KOlqkxK)yxNt37d@EJ-53wTFPoVBn5rUTmny(6M=}sOXHQWjIYwUP&yX`XEWZHFo=K|u$g2|Uj`cH( z%iN<3;|zi@5H@>83kLQmZ1zlHF^3wn7osIQMfeeHhMb5buB-ND>~GEi<&ixm<;Wgi zL%1iV>|(r5_9~iZ-N1$j!R*c8)tldg%GAopgP|7afVVdDdYHZFbF-JM5zb!A0p_n| zG3eOdz`88PQ(IeOXRmNP7$ffjk3ge#g|kWJj2?le5hF)a&`tg9N+jHzuGq*vzns$! zeiCP~^VO(LM0KJO=;@<|VmLP}eQcLFnNAB^&)!2OtG}Vh1tXV@+?h!ZZ`_bz<%6#4IgkyS>n`kPh75O^OtfVGxDxo*e|(n z-tB3p`w|J=GtF9|1q7c&11~zmMOjZ~Z_#n#;#+{qvmUGXsK@cq#^xBB<9+qnI7Q;B zWh2_NrV~UW&G3;B0lcSk2pgB%N?j?hVX-hZ@U%PFp{SZma2Gt;?A?;fHu9ni4gpWJ zXe~sfJVDRI_GQZn0KJIGpz<6gqa!`%TklV_0F*RP_4#OO2ht@XEaqoz&F5@4Gg1Gp zI9$)Luki>engnq3#W6w|30$lby0T+q-cH<|&)E2m8c+Q<&DCNN{2Y30%_? zuNMs-w@?)KU3@Q7H??*hWbPmt{gL$B%U|}>w z6i70C$<#oWASZBEmBvJjr z6ipR(Tb}62<}Y_{ENW7#b3KO;rw09h`o5=;$a+Om^`&I+U$G?T{cl*Ci_8wSn;TJVwR|J4vCUn~A(@ysc|C&DN9&NC|5zGWGFTB>| z-ti>c4&e_faq>JkGdi9*Ils@wRh+;1a0F_Li8u|CFpflogs7IlPn<4#bMod93!))* z9UVh+P?3!!O3n**)Hbe|IdPhig9Q_rG*6r^LTI1mAstHNIGz)6+@kWWU6WVj3L&sN z7Xr>M1d($Ld9xdNlS_`%NDl3%6_=xHyAYv1GvZI}l}w-7YhGRWiN`Au@N|hO4))&@ zr?tRsi0J7oxUb4HXVGytF^T{ilf^^S)SvF6uhU&~3MirJ z#^5}=Sn$AwYv60}KttxiLk>%wn5x_FXKFR`YDTh9`fel+OGvC|%`Rrt|K!2Ka}aEK z-(|F&eT%9&5K7OXJ1)A|cv1y~;9x;Z$dpS2=_Duj(!kPUR3cyco7mpf2@r4XXbBMHoh^jwZR*76`1l>FG0MounS!3yZJlrtC5 z42rG&1 z&Kc8HMRRvaI5@>euaa%8|8oM!UoMY$D&XhH#TB$Rf+WufSR|pT&Q}T=o?Shf+N-x^eQ5{6vG#jU~@ZD~_sg z%19L;F;5SB-IQLhx|C!!K)h{O%~X(gQ(6kOh~8thr~uGH29!lt%`Xkmd3YY83g(R^ z5Fn1g1L90P@p0oU3@9GKgDZh8kxt&o=B@!CM0-h3Ud);xn$w-ULU+|D^;g(tcf&pB zEqf<5@5@fg97%ny2g+O7Tz58C0<29W*~upV+*WBL^ae7eEL+>6E}L4*b9*Uo+)><< z&AlT%1dV;SF|sbRa9xFDnhmYNJE9F_*-0BTPu{VRK`2*RRhl`U-XMed7O-|Q`YShk z^h~+;QqEn==Bi}Z9GKSj{+DTEV3?a*IkK&~#TZbCgWroq{sSGUhy7Yh<8l{w>6U~kG z^dZyKMswbYNFCgcl!j15g$Nw+=hj$S_-!aE&_;k?iL$E;C$$z(6ZdRjt|zx@E=oPn z@od6O%@bNIwtCHD#7h_TzzToDKHgC)w_j7r^4xxd6brZCE9Vn_h5scUt^LJ)onS)O+rMY-QDAI*uwQNAyI5f3wiWAx7Qm&f>=t1 zF~@aYTZ%%3zBhNRGotXA-b%=ld@K{QEy09xv5;rHoMdSsYBv*)fmW^Z0F>5m$fEge z4fo0GCW=$6)HMP+ugp9a1oq0nPgi+cl{f9fOoa59Llhev1UtEuuk+1srwtW%e!JUg ze!IoC^mb~keupbROikx5y0sAXWGAo3cYd2rFzC(fG?VpEeDY4)R&kc?Os2A5B zB{4;GXh8XaHgs=O1YnA8GCZ@QrKKbzv-;j0XiN%m10Id&^RI{b%)Y|hl>NF-64bRF zcsock@=J4Dg^c0-nBDYa+dJD(sVC6EWa`xAc=^C5`=-E+v-j9M10cO^Bk@<6}&t;SS+6oA9JR0Pet@QgoA`nV*w&o>UU>GvzuQ7mR^Qd;U zQo(l5N8ZrMM+*A}Y~(e}?U$!~3*$Ryfg;SqM+w zTnBEYUy?7vIuGGlp_N*yVvcOpy3gY|zGRi{^1b}znFBQT;RB19M#oLX6{(W)9Iz2M` z4=pQmxJvVg7+SrJfJ_2DV&tI55LKnFw)Q+k2Q!Ei$V<812eGy=M@iDITH29?7ono; z2x%IVb(?b=(qy;kZfJJn7%Dp=3DO7XKXUId&(jD;Lt=1gNkZyR!Xr+_Hb%vtcYbn_ zGg(3x<1pN}ELrj{oXHl>_;K%jR7H87(I+{n3AC0;Y%W>%3Px^>$B55liau7F!hsyrVa z*dK&40tCV(<_Wr0E<&vj<(_u0L{yIWgfEv&U#GN4m+LzApz$|c;K8L(+B{s&;(C$A zb%xIk*-&K#*I=uZc>;1y2^ts+JeVVw#6`U%TJ7E7Xi6>YKU%oH>=id1;qYcnteqNR zFR3DYU@mx#&HP#A2aVWkH^hP>X-E9J>>RPdY;*C4~ywayUFWlZwy4Td~jFh zb+^*wb@2hqkdxP4*#0^1dLdDIm{dv4e zZblYU=^8x6;|4hwmIb8iQWl1_r;d#a`f#AH#VAJ>^W+?{Jzh<XROCka80NIrY#YoDwr!^SHlqv*F;+&3kv}-U(bs(o*&6oi9n9+5lnAfURF-~i*V855&Cg>WIgVX*wMYDKg- zO}4IW-3m~bs~g~6#S&BF6IHAK#2*rpBG~;qcJ~GJ_by-cdHa7(AQkO_pIcgcdP^lK z8ees0;6U*xWgnj%>n7o7N? zUvqu>0OKhC^9~&3k!|3(St4e+%9$OQX+u`s9clLSp>pAi50OYz?N!6O3gWpyRq4bG;SK`69BAl6q3=#SEc1m2yF7rn zF+?@uZd6<*L=VOsU*j$QYI+s8exN8vdd0d{(g-@y<;UH#arI4zFG~$`^`}&i3Eq~C zdQC_OWcB@8uwDHE=PT4GyTPwaatsu4^fNn~_J53PgVMI~POUn54CH`W+dy3}| zF;pK>XS^q`Y%1tDH!Yc;sCaKs3)X&rL*5ALYNnf><;_LWcn>j~Jj3rJl-+1;1(>Ha z>Kb?9)7ZK#)qYn@V1{2&?i}E%-M}GEt?$gy>2QMCSu#Ky_hTIdHaVb-luP}regeuP!dqQhS2aS(M=*#LZ;_CWNmqZW8 zI3+o@7~)R}pT>F(<&sXnoxCCW^iqqivw>pmdGbWTLiY=*BFdBM0c^VUwes(L>@ zoGO7d*dLY~zl5Az5pUi0(hPV;m9W~9lc&R`iPGbqcU)q10k(y4A5X>Fxh zh~Z!*et1=%Hf~B1*5llkfUo?&U_?p6+8i2E%ASsybCPZlb*jpr_;i;4^>se{rvAJN zUn%?7ab<*cz&tOgbtvyt0MpN-t-Lm3o<$xrx3Zrj>7!7PZi0#RaQ ziSVrQCuYG(gOV%MQIlchZZ>k)x!$HpjBMz~@*jRG8~QPfg~JSi|FFk^!ZGw?Af)Kb z4!y}p<=XJOArFVd{OAgLk0?x`mSDQ*mxq^9oB{({CK$xW+2|_}C=gfB zl*2Xbvk}8DJ3@cKF2gTpqhr8yDZ7I%d;U_&#_kK@*!@tFyzFNVj`=@v%}VN}e< znvS52ZSPi|9xP?w(%>QU2=+C)KY&MttZj(^FlA#eeLwl6k}X3(m<@>E$ zv!#`UiMz(O=XE}OR&_Xhw(wuH|DqdeDXYx8(|mN73RLC`nvLxOOfN92j$TpZBTLzl z^~Q4KExJa~#7@{mbNYc_<~`<@XK`s9&^#Y9IK=rJl}U2!eY5P?E?f|?jgN*f4>ShC zE9;d zur1kP#0TR4z0!rF$waka^En zO!Q*>$PT?rrbKFZ6z>`1cilf)EDCbY1@T0m(P{Mcway%7>%m%&1r)B{S^l?i*<%K^ zlGR2W{y;987H7?Tvcp-UrYYRNQXJM~k9Y zR4Rw`n947a%v_-s*j2dmTt@q{(H_c>RbS=onN#d&VC*yCHIblz9-$i-K1NstJ{W^4 z3SlwB|D$s(5jaW`;h2HogUaU&v}b!_zTcvzZiaN`QKa_JCyEH+_XSEv?-J7C z5Idhxn$UNhsLMf3fDK(J;>wdfSmZ|}Ip~F|_YDoTY1j=->+BZH@d{E2^i_VGdMfLt zZ3Dw4@UocYv(ErxC@K=eG?AeUcW0j>)YAHS0dL`hKsXC8JXk*ag0yB4&RsVKe5q&! zNU~8A5-e{$g&aW19oM+bwd(8u5kb&G$C+UB%gn$ba{P)Lblk^E z$FX3720d>4ipHYxOY2(HN5;dFRkTQK+cnSNtTqRJQT0nuzBi+NA+c7#L0! zFSx}Pe|w$1kryMJeWXZf*-+}drMIb)bXrw0O$>QT5eXPtbxak3XuKwBq)>PajYVqr zcTSU!OWP>%VOwJPlkJ!?@bfmLG$4MhP)(F9mNnhzcj#m{A54hxIVK=2#XYGcnRA|6 z722n_53oG)bn={Id~#NP(0sy?FzQA%^w>Q5-6v-oyVUIsj2HP(mW!&4RvCjw8c{(o z!Ci5IO3xh|;Is=nl^2>;%xPFZ@!e5asyRsz^AJ@a@7p!5>a04qnr9IUyTEKNV7~uK z6LUdSjTX_<~Rq*@Vra#53!^Y%=or?Bp=sll~=M-Y??o%nPZG3W=R=0K*wfc0NoEVU_#iRf( z1dn`*)j0LlbgQsV+q^4cskU84Jhjs=El)gySc#63e`#a*yD0zw6>b8k%;|m#dh!e- zChLW69K^t6uBETsQQeVmoySJ%$&2DqH zAA=M1)5}9pKdnp*ylZfOUWLhiH&QM4$Kzg8@MlzU%J{VWp(P~NTWUX5>1aQmgTkSw zpkRvtSUAcMKb3gLQM4r~Md9fu(!ilJ=SaLEo3v`>hJKg+42u;ID<6|vnnC7+BEnW9 z*s2H*#)^m!Lbp4?HYND3RFUwzLSHk2p9sN!g(VRESFxxb;N@ zSgF53y&OU~*D8WJ2biVw2v(#-OH^M+Tgp@BkWH?n^v1~?M5iv?sC+aYfaNjZU^v7B z)9^pV?0Rawd)U-^7kWLjZg=m=S035D3vjAP9!vH{rUSl3P){GZtLP5b7%sRul^b0F z>grO5^?&YcE8i+xOO=(}2db5-*6dA$Ly3n`_|uvSYtF>&$aq>qB6JGfp5Uo-{K4x0 z&S~7`#!t=O)%Uo9RLHr#*q@QNoQ{i!iKT0X@#;!bf8rMDk-KaOPS>0=<<}!yUUYU1 zD0#@5&wi4Gcih7)thkK4_KAJ9-)ieAmJKrZKlR$_%OZnyE!{SI&!stIISP*~WWtr8CcxyuJpQE0UCL%KEkJW!gSaH_omoO^plb>s zjgQ|ZfU~@oqtN;RMJS9DGaj+)fLJq35|fo2-T#KEgoU0}tlBqU*2hJvz7$Zea%1_{ zUZPu+3S%z-JhF6!x|M8RfLx848x1k6d+bP2i#Fw&ykHt2x*#utLKRNyA6%dYa)KGG zM4FjJeGsm9VoF`TiWfVw_?wUD#K!`;X&a!8a#53U zs}ZkyD~;@Kg?76~Ar=l&WD!aR1ct3CT|FGE6UfE@C-SEl=|cq^5gQ+P6Wz^S0;3^u z8-?=a7AM;%>X)+(P6MhD-z76J?P>hsGDI8h4}ltMQokd=zCU`yXDk9jX5e{{Y- zj9!FsOg5hA{f~p3oz97*WAv^%s;%jFVhc2)!gR*A>_5(R@=voAM&NvwvQOgM^gOWx zG)a6^J~ZKGTOJ%13W$23)UX1ed})h;cZ^#lT2mBfI7X4&M! zWO^dlJ6>5k!P&1+syH4@4rpmqvW#Ua?vAEt$uW|-Gf zngCg1t|m}pE?=M`A64eU7>n(R>=#nb5ump;bFSl(5d$@kDk(Pl-0Ut@M$?*58;hA% zEa;!Onw>CWwS$H8ukWUoIk8076KZXI?a)s2X!dZ>I| z%Up{`n3MP2^vD3X$T$2vMd`e@m19h2OLXv}Xl>YXVjMTW2!PY^_jgv1$?5}fQdNLDEW zJ<}5`MKYIyQZxHAwhCy2siV5rfrWY`eA@SS#+Xx2JfqfCU%iw&G-k7U^%XQsv&-rM z+-YW6J>ZvaUS^Y0>=S5SlZT5auk^CCK9XvOaka(a&)c*dbHDEQtjCy)za=Au}9dz)7z!9 zqCdhetCq_z`;@VEbL%GV5CZ&9BxDN|`^MN@#asg4ld|hRBGu{^c(8m}ukw^XY=vtvuNV9F5S~r2Y5u%c4^Xn#^n4$Bl2Ap^LBS=*Q zMcmZ#SXXNcH}F2jk_}bz!So*Xz$fcgb!oSzozOG&M*Y3G>E7z@B_t?7C>)`kmcn#(r z)xcA({<6T4c@J2I*~ ziwtVxNDi9{xv->nMfvdGw1+h37d0-VSKgaizHhd`1?3uf+s#FEB=^-E1U^m4)qVT_ zDoIP0$1FUUFsc+XnMw zzZ|&No8kH;iOttqw#KJ@67o^e>TeSd45!O8xxW07yKFg8lzz>O%r{M_6 z>2l>O=ppk18zlbD%<{+-T@FIhYPCrJo-%f=XJAYdsF}aSZOTEH7U%HbKDT*`F209D zMBXA?B89svR){6(Fw&M*uq(E$b%jpFajk}gNKcQrK{19}9~YYP@Ww9GEywtq2|56s zP{z%mFz(zm$Ik1^P7G{w`tzurz@l<$S5ggCq zW0t487rR#ym=(LhyY+t28%0p7FSp9!qABw3~@9bJrg8K*F&Ib0-UN#WF>3sn> zdthC{_Vp&mFVw9iCOg500QyiLUMNwiI(;f};6re$pSKp2kToYkd?xaV~p zwH?}^OW`W55xLT8%}NX51D|+g=3(`LBM-L>?bWjKv25T=Rd(P@&KR!Za9oAI@@RW` zbS*^F=v8^K;Pw~sC~{?F1zK&%kCP01%ak9s8u*iWQ1&tov<-YH(K5J72nPO;4SX%{ zz&G}On+<&H>J4r*xoKG;9h8xbG*8>JIXpkI=UHto1dks*pnV3uGQ~cWYSvmFExVS3 zKQ&(~zoZ2fkinOInq39apKyW<5z8dEas;u=s(@>YL%gEgD2{WjjOhIw*d-(;0zmZ_ zN5C+HBW2`H8dzZy9V=BGS-cs9Wm-$UrV%(`3JJ&Z=mxSa>A1)mP7!vNM>pc^Dgj(2 zAoW_y13q}yJ{V`M%1c;>z6u0od2o+holU3!*$cRQ=x<%{=Q8i$8#<9T_(}XBskz}C z$>kB8s6C9-j_pzk*)Q31APpJ;9a38}SOt zM(dtz_;X|gk?1gkJjEn=)>ZycJWv&rlMR$sLhyY_zJ5AMHv@4{fE%bdSe*__c`X%C z2VBT!?Gr7lEP(RXTJCpq34w5YiHKp73U1|StSQI0P5rlnrn*anKfV6sCi4aqCK9cb zz4|lMCGC#D)gw-*z9E`cU+s{aGX<}5fIv?8DjaV^0yDkSkdMODgBS)g2Dj?FN>te_ zsgH}Zu~~JWK=HSsOV}{nDD-XRr2bL;8Gp|H7ZJ~d{J@1Eh(f3_73)8cF!tL^9Q2d>;oxM=F#drJPI@Ebm-&J zV@dd>D)TDXboT^>U)@3}e#3=1`a<_LqQP%0fUB?C^DcxI4WDQty!z(FK(kp-fU2t@7@)0WfNNfz z=bXu=qHQcQ0yGt^PWaG3Gr{Gg4D?0|CQASaEF8W64qbny2% zbny2;If0xMTC`U)jr3O*&!bH~p;;_Mpg?j}V2glNHsgYQ2KS(1^U9edqn=G`TY)0i z?q;WX2Ao2I1&0b_bgr`F9`bZ87)3>X`-L(fAdgqt|zkh$*At7HXV(-`__xa9LAOWH$wGzL-x zUW60(b0f#0^wq3Qet2Cx{wx`AvSjqR9_dJS^rVi0C7GBdo#!Mx8~mET9NXs!%NO(* z+U<~*oJxEUEr-6;&sU0oeq5HC64XOxln}B|2H_=P3}CgTZX06PUdXL6?=gA#u1?@p zfQh^^LXpA_U6VrF1B_H=oRL!0MPsQB%FUt@fxjpGd!(;u+$l-!65$QV%Du+nd7C^r zX0m)t?l8%}Ax3uS57spJ8umy3hWpQiL)E_+^e0FDi)L#QR`^K!m-zgNry5j$Wy8SC zydY5nkAfJdn}YB~Jn$y{XlRuRkqx!SXKj4SowQ>%_+wA&cr$y0bYC`D-M?J z<>N2vni8S_?W5BCYC33Dp8d9}#K6AX?~QCITW>o4=#ObYU9MTKwzv1Kl2*;QtUkO7 z+fgw{w^N$Rh%|*2eQ1{?@q~zTKnyXwsjlg7PmHkH;BQycIfj0bZTLQV$iveHPboY{ zt$Yi=(VMLDAtqE_|lX+X@3Y5z&g_Z_qL-nCQ8dDJPlil`d6w!dFZbk zcO|*ZeSyT?d1wT`$D|7PSbj~Nd3&=w`eKjH6w*0gR)Y=A5D|xuy*PRTn1yfu3L(hB4!(~muXsgnS4sdz$tqqPa zH4{T{NU9)3u_dt1ZPOX^SZU6hqj3FKbK9f~572d-;84NJ-c+R3_qp!l!oqA-G&ha&t_iXq%xmivc__{p&Y!LouF>xOL@gpX}$s*}uLE^?d z`9)>P|0>$>^IN)BtW<;N<7Ckk!JfIrEvD6vyH~06^!BXmYgyN}vZbY0>bJ_sGfTs) zba6E0ReI~!NInx00Z%X2^~GvN7(vX^e@M9xJZ&wI^*x;34;hS9(9$NRk>cp#x8Xr{ zPB5p53n*f7arEc5fP0GNji6zhX=(s+8?K0thSSh9JiD6iK0NL5U1QtubWrmRQ{-dI zm!ZFvM>SMvtl*EzWW)cG7C?r73C~g_rnWg%6<&F|Ji14;2(_lNSwM#N(G9)Ozlfev zq3r~1)#%i!!cqGEN|;U_*`Cn?4CByVJps|oH9~nYvf;1VO>oeoC1_CWpBAMD4&`QZ zw}IR0%kt>k<VfpFXAh>h*# zSxK#|wpM%0`BVF4wQ@A$FGTm@r=g}Q>sCWRix#zwI?r6&Oi+3B0~VE6u+v=xwHNgU zb~Q~X=&_rhPJ(#3HQRNLx`hNV|BE{SKFB0&l%_7`Fih&6??@pR4r|OG$kr)lbi&Ym zhret|2pz1?5E`T84+wVdG-IbZ76}+Z+vx>XOm!m% z!`>#A5+n%Np7flWz@lmm_tWSEGPbWlsW+CUGO?9%aoZW4sfiCdU|-jajqdb7*n&Ui z((F$0tQDqJnPD91O*H(^sY8r|xrW}iL!t-PGLsyuo~ zHgod(2Iu2iO(63MbDAa_b>&$L&s88Dv2=VhMmj8ewyS-M2^gb(TZiU!Ty=`Avgq0x zQIw{(uh4-oRxr_EEGdfYSEEP|;B&-xYcPHdd{|Owcd3Q4tSV^? zZheBG7;m=<$-)gq>t=ZF=w68-Tsj>Q1TIl@G&2QD<3PuVvV2VO%C&!SA%E%LPyG8U z|9;GPN@pCc564qz$DXcRhl!# zy26(e>VM)-bYhq`#&7*gYoP|!{-DNWBE~EZB|?oV3majKCTr17sl}fmu(@LB&sptP zYGiiQfumgeoiY8wkd`O>)uF)&2{kbHTt^F0^DC!6;h}RPaO7n#i z);>{oP%Pi0H#)14gjq_k3fi)6ri(?KU=)w;l{j5X*vQkKb3S8L*iPvO6Ug zc@Xbs3>~sbimBPAZs6#A%x8u_4qU&GcpVwZMm{J}1dMcCx~h)L+<}fuugfJ(A`Opn z)1gpm3SzOsnd^-e`o55KWQY!L5K+0prO}2hZG|LaVcgOsB*bdXS8x`XFh4#1RD8dJr_iT zl7?M@yI@B@2I!!lnZBPBI~(~T$TjkXSOL<R8z`&+28K!G z6nXlmtw*&`N%h0>$RA~;U}~EUx`PdT%bjMJ&~kH}T$FX0zxq#klSHl1d!p@;$i4Zj zt;LPT__px@K1dNDLnFkCv^dPX;`2e_<<>1`wL7S6-%78{4xy5DP} zQ$DOIE7BBxJ-k7>YNJSML74++OARyX=>vPjgY4xgo`+Je(^FW}9vw@T^yR}(d!6p^ zW+f_*ei9!fwR{XOXxV87EcpuTOAU5V@;BwtuTsc=u6%|~7W!#j@k~o6j%)*t(kdLs zs5@@4PrCYJrx66-oWl5O0Q!g1I`>_4p*y?gr4k;}nUBB~+S5x-o;gy1nbj#f@ z{JKpE$iQ0t9(zl_Q`_`g`OuJC{FaZ6kuj?i0zWSRYpEp?J*(eJ`-)Oar>lxII*6xD z5r^04xAuyn54OrZ@LT;3mhJx5zke{KUB8t-`M2M2WmT@s8FJN`oHk@}ZBVpP|7@Ve z;eT5B3^xlY!QfRz9a!}5WkjDPL^e=0z<35O+I`Nyef~YK-;cM`YAfs)s`7y8G|;8W zS$rBe#V@ti@8C86{?fnK{rfNcmIpu9@4yBBn#=UNd zo$AMQtlZK17{@H}D~{<`DLV+U(G#5Z7hDEaUux}f0d(GcaEs_ZxYyKwwq0Z%+^v{$ zDX|)41z1^qS47vgDi$aYey+kHv-llUQ)JZ^{SLgOY((fbWg`de*l=FX>o|C)aNRYk z%`P7UK@^m^ue(?EsYYVw!);T_^PYe2`}c-_uQVc_YdHHF&hyTJ*-;Q>;3^A?MJU%v zrSh)L3s&8&P#v_VEehaOWyUib5TQEm-&!MDG~59LJPqIdxKOC4XVrtofh?&WG>-V) z`}i{q9n~{{ud4C5LyYE>Cq8cUL+bxBtvjUCh?w)HV%%}EyK1AVxw(4YkU9UJvQXKF z;-I2%*fKB1p)Q3V3SmUC4t3+_9xMIYjTzeRn&J(>)V~A2kbCH9|8Dm0v;KWf zzXR|2_g#KjsuVY^lJn4Qfe&4=d(rOe`W^bYi}-e{L^C@f_yI6Jz` zl4rz&IHDn$4{tLX28k!~1`c@eR$O4D5V*$~47WHbjW&5#rQxY}1+#g3c!x3>eoauW zToshY7!Tngmnk&dUsj0f$OfD^?(DeAs>pPrM_70=U2zfR!*lkR^vjb<0ri<}o$;3U zIS|Hi*&8&YwuM~^A5m?}m2dp}wIH>Fa*sH*}2j42WGCw@mRI&jI53A?Z7RZ8qT_&a#g#CW&0(>+I!4H-UTxSdyMB&tSL z4nJi%rg+)m7lie2kK(dEq&!7TZfNoEwuZzz{92y#;q7+43#M(B!djGc^d%#k^6%W5 z#Gsn(g_wf;hwpVYz+B0|Uyha=UsRN{{yp!WsHxb?tTGZSXLtFt!?ztNm`qp$2`qAW z$&z1Lx7m#)8On@6l*WvKT$>aiH=WoUc0~ z9bxpKDZeibLV|=GlPj1{uJI!N{=zk;8jPN@pNu|QwEw*QWcSfl`!DHtOvEh@?^7j) z-!uGz;obg??xP}Jw*Lj=pp#h7Gz1$zawiDT76Z&!(ZfifU86vTl~9KVMoN; z&p8D1389(ZE4Y?u`xpF0PMc)e&k*Gu@a<6TU?E@G%#x_1=AT@~z223!Cp;V$0BTnb9M?v&&G<+83^UbL$My70LS3jpK>~l3>mxEqP#>l60MAw}>Ob2ZUlgj;EZ#o`f#RIqe<({9aT8_`~vn?xY<$hMy zN)(Zlx-vg2bp>n*7-)W0cz3^EmQP*Bq49h5?Cmr5JE2@k0J#erCVj6;-TnV|4M6w79^fklc{5tDL$ zoxvgt2QJHhaCPP{D{dGtcN{hA4OEow{j#0|X8M6!LTzAkhT4E(9u$j)fs1+Qk;saF zAd}9TGq!keiwb3EdlrAhZF*qJPT>e4!QtN@v_26xb!=-@^b?%=q99-Q#gC>?ZW2P3&IIb4W^q0M%W z`gz@`31Vb0sTfz$!AvKjxogU2aC@D5bDbMAnj%_34aVX^u)!TUshsnmsw9jI={dYz z&mm(yY{85_A+CfDu9$U~tlZ+z8fDTa+nP4wXQ!Tn*D{nwMpZ>a z=F6cfHD2IvuD>GR*i=r@B;B9P9CJy}es4h;azPl%#@)M5&qJ&AEcXcJaI7|Xi(TG| z@o$ETe>jra3^8o1%0F|AV|k3o*@|mImfz9$;rZG3LyPbicbmP2Z)Ksw)!cP4cO6k@ z0h=nmHs#?D1@Y5HJr6bexil-HI~8n+*QXhJ4nNSd9Bs6p<1X&r?5`0`jF&@W3O!UY zc=&5rMJ^t4L5*w}zU&ZjTk%kTW;f8AXiRoyab%UgjjZ%@t)I(zhLLrCc8I7WZdW5E zEWBuxjKnPAijnJPbT0U2KX|15cUd&n;Ly%r&t@e}l**tta>re$>Wo_37`f$K?z#j5 z=Q5AncjzNOW5~k-=U|L*OHF#W39ssG#hBXWXB0zJUbN)76HDy_nANe|72_yuXCAgK zcWnSpvARC@M|~iyCiAe4+!fohzM5I8vGDjS_pM5i?b4Cf-PKrS%J`0ut={l6wkS|e zAXX)b#};bFdpjQpY$XP4tvl%$=5r*9 zci81V8cR%;yMO2`-_}%#zh6>?a*@5Vzh(D5J>Q$*c+e%>NM)$i#aN3!QpX=5Xy2YVh zSK_z|3d!BMJ5^R^O;gnoK7p+ooVTK6);4lvnlSj7b6Vtom>4)VLL=NGjA# zV`~~ZNV_DJh@O+4m-Xb#Lq+v@jPmb`9W{2K&b0VQ=MVam&FozZY(2gb2N04)8Jh@{ zivs{0B?YLnWk37%-grI&D?l?mmW;ZhOqk!EW&-&ECj`Z+omZfU zjd6aXpiO|?z{E^}I~Bb)#!KkF$J`-XndWh`Z3>+A1oQa?CUD}X#=ofC?X4*gdbnA5ffSUiYa%E2+)A537FOcp4?r-?Aqii7nN#Q)naF7WiE|}IYH*? zo`p(3zRMg*7@9Baj#Cw!V02Hlcb5k)_`dCFTGq7cYJ#l`oX=QSgETGvDVMHxFjtx6 z8P#6BQ6vZrG`zlm{J!7T#Da^?YIyxih54StL?xVux-?IHq(O$)Rm}=qNCKh`ai05q z{n*Rj6`OX~%x4R8b&+nTo!+@Pw-t#2Nf{E@mrb0AB)+>*tmi znj?%TgTg@$S}=oy`FkCX2(yna63bIuLs7FDUGz1W(Dbf{ueMX?FGghQLKaJ88)x0i zLjwppjF5g^?ob~uD_3-;7_Pm=%6qYxVAlKs+naU4&pm!GipHy$de6`|i8u`7`)y%# zwU}tACA_Z0tMpiHU~A-D1NSk#&#ysX&EBKy;a;)vXa|l3a&Vj=QX`b3(t-Ttho;WJ zC|4CZQ!clN6;pl)T<$Nz2Pw-BoqkJ9Kf1wSw*eOM7)CkD(@m3zRZd1HASL4zDrLJf zCaW%$pfW#m6hNZ{o^lt5KLHssv^x!UP8hz=GIz$$>D+0|oYu=&x}LvQOVA8BErC;< zXaqXW=`saxXs~dDF?_&Nb5xRe5RSxb3aGaIOQB*YCsz`n<&0cII0`^r z9y+h^x%0|1s&A;3%g2cJ9L0&GmWZ_gC~rqn%#BK#G5ttPIfy$t$`R8d&!{kG)LuGz zHfXc^7**VWz;9!#;2#?cPp}mVWh7bE^~VOOWmebY>3)tM`;oezYm&6-jw@Hffli=y zw>w+v;JvXYMfC|&y+J~P^-jvNYc8sjnSX={%L}9qqJr*vnZu36K^)A?*~p68Z@R*m zob&{M?$`J!lh^cdbV{X|l2f^I=Kx2uRLg%^m>WiQ-AW{n8Ha0|MlZFyo)?Ds=^Ce+ zOK>jo;EI;WXjSRY(M!^jS#0u;0ROJ=lpJ(3`6bqg0o$Fa*pKY4#O&m*?u_3%F}mI0 zuM|`@Fl$q+(u@I}2Zj=i*x+s4s;;X}aup>qzu}4b)D9GXtI-^NBjadFFV=vNe238V zL`0xlDupV&IC>k_u=z)>h*>fYqj!vhe2RDHtAzz10)zSDDxPpCR+XCl>nS`k5}z{d z?Wl=pU04ybRh{8=Ro3V|tu_Jhz4?VXVf4P5Tnf2w>>lXj14X5RyMEb*I_Tp3vV_2z z)_qQo^WyXFpV^w|98jwa$LLGQ?mT8i+4ZU!yY~?xy^mBi$Jl74R;zWz z-{rzkYGxtzib6X!Uq93&%tGZpB~wF{Y;(ubiq<(xIUZ|m#N0smcmJvZ)|f4g{&Qpf z+qS}!QAo@x>oQ9@e3B_+#lAcl;5SnCS>qUzhV zklJ>Y+S@g1tE9p%_R#(7=cDnYjACxFKzQ|IpF5=bg+Ea>=9XRUVb@FZfXLeat}L&O zTJS?&U9UQ8jPYMeB6h#qJ_n6OVJYxuU?LK*-%L@vf3N4UMNTGb2ubr9iwEoFJ zJt34)-_-wTL;*e_bUripHmRwzjfB>q=Tqd_tg3!Nj-{twl^Q zaA7V|YSz%W@Ld&&45;x7(ZwWJ9e<@T7XWk_)c2O95Fr?dzQ;7beZDXkmZIIM5+sw8 z1!XUr$_q&78pAg>{^tT4`RGc}1hPc+DX7SBY$;C*b_;}KOSE&16>Ax}$Xs1U8_Md* zdN72AI=S;IbVWIaU9YQTdQs?N_Z=0Tby#|KiYazM#)`bhmZ>JXJ41JWn^n-U<*<>z ztWZRK*=FxbJ$o+J^14hwv3nEw@N8OGc!C`Y-J4ZDwHTcIYOyD4&P6rnR8|}sqFNdY zW$6h;7s*orH%lcZUn6~yV)sytt3u*|)&*YdUu54GyV1nwmh5vJT9;zMf-Ii}3+jR! z8nWPq2C0YypG~LuA`-iPfDb8?f77o0H|TGt?|3mvYPe1N=p9fd*U*=QS=S%5j_mPw zjVw!Q&X$_fLmIKlTWg`sHRs}*^NO4vSL!aQ zKM^TLg;m0?adAU;{~HMUm}JRg@0R4x-Wka3y*Pc(-dB-Pv6u}Kq)45>2eiBS?Aub{22SZj@X&SXX6T%^1f zCbrK(0?C0$dqQtxin(g;!~Z0LfL*eUvu3fiyuh+RfN=gzVb-i!5|ef|G@x;26RBxg z;Y+g`Ni6XMGquS99`%YnQ z?Iug>@zQoKvg9X^2NmR22gzX!FZ-(mMfWnRfl@UmT*=md)(}eNZ_~Xg?GX2CvOqA? z>w5`I1oqfd8m-8wA>r5>er)HsQHL1PiPmEY%L^4vBaEeMd@3qq#6hwb3Ueak=uDLb zw9z!Q;R28>av_bI#wGg#vm5qY_NA!%+l$B6Wq+<4uZ&m=OcoYiMr6k!9 zQmwPQ@Qlfw>fG+xe$ua`8e)TqNY8OdY-jWAe$vnRex`QP^_hKeU3&i@zTZvW?4ovd z&D}+Av*nr&g)vfx!plV|*}_(uvT{2B_1D++_?c@NH>v(`ho4fkhc@7P39Ha=2ZDe#06d2u`saNsX1{#viJc3zY ziG2)`>SQ)AU?putdv4~Mrv|`3bMnvN-g&JB==@NPu={}))w?;qg$uPl?E@Lonfi=E za3~8AeujrQH4lD+eZ!3fTW(f+tTLiEHdDlmyvhbvZ=kU=Y-kK!cv!Q@_W8ST$<^}(CwmRM z6moFeER3L6BlcFi-hRlzAF#u&Wh_1p$0lu}yu)(jV(AP}lxH|L6^>nOkr}IXx||+S zI?64vM=`=)qVu{C{uH(th}>Q)mj-4v1QY#wa_*fP?GG5};$id0pRuDu>8F%fl7ep% zJ4f3*z>DNaer2W9F>`g184wl$-eQRo27YqN_g@M{VXQM(lu?(y2T%s|J6U}|+SHw1zLm>S75rc+x7-+Pd(_oNnn5r-6N8cbh zEf|ko_hbU~1VzKKTcka9lLr|ox&x6A(*iwe@&lHB3r!RdCOXP#7I%$HztK}*>C&#( z+h6H*L_^5tOY0?DI?>fN~D{FVb_rKPzif^j!$E8ozlSVQJT^jS)ipe zTzNZ;m=SA~jxuENQtH~gBfKbphTPr(NhnWHd{U(x#UfLjAr6Yk883ilzQsi4Bodr3 zC0o;&av*A{M=aODDd)*}y<}kndj~y|@y;_Te}4dLgeJ0+(;4UJW#0!D zP2q$2$P2`th0&Xsle^x~%m?ZlnZkxVsjLrLppyVIl>ez zo}S4CQ8=ySGyz4=Fw)J;XjY=24PT>vca0TyjXEN4*5&hSi`p^zzgc(^X*}l610z)~OHPOFL4RXw9>6XrPx<{&3WEnq*xa$`j zRPYONNvXN%Df*Lp!o5MpBG9tJ zQxr?|GM0#Dt$p>8Q4FdQn>cwNT$bZkHE?*#0AmWnoED1#F_E(C-}I@eO_JR?Kqkt; z9qGp!{e_w&-&lpz7cjOIz6N`u)@j-wtjk#EgZ1s|1;CvjtmD@fub7Wi{a`J==EJa8BADVzjoisrpkj9216z=U%aBe9$R&EJm#9T`3YQfY zV56xOv9S~+P(jMQ%?43&*)OoLOB3t@2S1KZ=5Hy?QypR)h&;wbV`v3;2vUHi)Kv;P zyqMgoO^z=s_F&S?Xe-R0j*YrrN+wj7Fn<$E|0nsr*nO4A&wzwE(^W!?iHJnzheyFj zqN?;;=}fdX!j;xSuN=8!avl-S>;+^OX zyrT%TQ0%xauH=?kcLi_^yQoO})cm6;~s2pyNL=+(?af zAOvI`b99E<{?D_{cSq`c=Wn-&OO_m^j#inIc}M+5ihHAJu4k}N%cYLk5Te9U(QEz| z8OB;WR2S(A*0M(g5aJe7-(Z-mbUW5csa#8(g%k;Nm6ggA3?HLg;REIk88X0UGHi3% zSB9RqqJ!;pe^2xIU^_yLyi@XNZ!{jPMFTH&h{L;=Q=Q7nmLN9&Jg~!0tuDk6ifGB; zh(Nk0YkHDx(4~&Nd6mk7@>q@pff!Guocl)}{U8~?W+d0>IEADN$~hJ^+n$s0XZM-V zqn~rXAsQ*;utrkQS!=j%69mR;8|jquz=t$ zFERrf2F# zdteOpMY-Vb)^@snJ*}aq&7&^>YGVU(=vhPvKa5kU%%%m5sL3&F7K|vCZEBk3z5;Xm zkl2P>56788aN7K&CQEu+QgCZSb5wuCq$c^$plMCW^3fX|_L#hzb*5jW&5O#MDh z@CabfmC$ovz3v&B5!^K%dRD3j^jUb_vf$V9UI-sNY+(TFgGWk_wtGN;!oPwf`)~#xiiEYw;K~ON#X>5D*9_A4 zk{5i17Pbv1$Z8Ok!3j%J4MoFNNKurxJZz|eE?7Nqg=Qd7l}s~XC=kYYr;pO3z#4=b z2PnM*EcIocGVl6Vh+rn*3rBZx(Z66TGM2-zu4+>ixF%kShAbb6Tc@_VfjbO=_U1zMg)5u9tqMYmvFOuN|62$lgSZ zrf!z3;qS7k_mqfJY5QYOpr@ZysND3yk~?#&r_j^SqRHpfY`$9$o=Ml9&d^heg2(x2 zY+Mt`Jv*I0MesZ?ge~BV@^;D+lPOSrio}A6if{hK`Qg1r>v%vurEKSMZ1PY z8;9DYh!khulCHYb140XPlaHYmMXU$X^jhH=w%dmumcl{Wp1b_WgGL&9ZZK}DuxtL^ z3_bC8D>~v3`WvC=wj@gEx$|lyrotIZlMG>LarDW>H+!RTX z=;7UmO)PZ_y;@_}`=-D?Tt+7|!RUQmrxI}V7luTtYc`S>-%Y;sN-K4Uoce@{&RR;R zB`2pWzWdF>Tq730(i{1y2fKlv#o_J1;=A)8J3k{93oYFz!D zMTi(EMlFgK#Pw23JLu-6|CIVewczlfR+W9&nGJiAE-QW$#G#%UQ}wJ65a>bm==dO2 zx?0$eIAj!Y)yt}+xEf1zRco#;4V~vzRlO{S<-sf+i`ZBiW#)>#r4NUc@zCF>?@ZP8 z-gWi#-VD85BCCL#v99^5KM>aqgt|E+z!gZjcPY%-j{jbHCO6iU%P~Kvr=Td$o$I_@UM*8^m=Q zNCOQh>Q15WC2d;hrRA1dg{A3hKT5w$21blxgL4@Bud=uE62fFj zbM%d%*+Sszs?~+&@FAQKR~5Ux-VYz%V=FJ4Hgy54>FkHXSmvuG!r1$|AQ*7j%Ht33 z3RRd-X{Zs$kk!j-=)vnvUqdMLsoPkig6=DeAEQgl+4>f_j|E(3?i?ytJnaN8d%hqp z4Q3&*enX5el0{40?ZC3|b}2M^J>>^Zd+06)kJwdyK$sBoN3uGU>{Qf)VF}Rt*W;2| zXl3?Id1}3W`0xSy;wZn0`M?1WRrnb2P+T(5BBmLe0n^wqKXY(Fh}XkKCSfglY`ew2 z<^0HtoA1PQeaornFxDbMjJ13XLFP5?Ds{21&9%o{&RHaY=fDLYu?O!nq^e>9jPM8l z6{f3BStjaYw77Cq3pdAFL-i6YU@~yO>{y(xlWVbKj;oNmcy?UEv6B%trx!+A`dHPv zzcFPwrp4CPahcs?%R!Xo&wXpCW!F3+f8<7c7`e@rzqd{Pk(*)UCKgeJ)i1Pd+IZ!P z))cmZj5l?_FO^HAO0gZ)^2Q%b&y|qz8CvhyDr30Q@{PiU!;6H_DjtU!X5|Fkb)rN5 zPj+X%x{+K~ClxV**rS9H7IfqhI}6V|q-N6aS>{HHN}MEhh#cvZeOi#MYB$Vng)civ zwZrX*OCXa*zTf04e(e70`iz zNobKDXvMhG@GC>4z78I}J9tzX`-lUqz3bsgWf+&B_H_^|f#_W?ZqEp~G+B&sg*z(q z7dpWPEhO^UE0xsFA3rT^EkAri^w!6?=`MSlVt5`>^O6jV#buuSy;WiN>LWx?3WV4c zGE0U~O9|D-OSzeIHo`g2!v$ju(?OK9I=>ID-{hV_-;D;SF($f|Fg3MqlR_5wMn>#4@rg%Lq3koR(o>Y@6X#rAfxNW$b}HNxR*d2`Ft1a7&IhhH6eI zRg|WpfhBWTe0oyt{p;-lP$fvarKkg1hu86Qr6t)vr>{ri*Xdu#IE;-FMX*P|q3)Gh zIC6;BfW;ByVNc2c!UAvZG}*H7&e6h)td4hMavbg8NpdcuLiP>=&7p$k+|GI~=ABA)?YS<*=cPLl(fp5Sv{+ zcuHK6n(t%K5TdW#D58kH;Qyel=~THrs>vk*WKQzNUoiX2(=SDAsDiBN*FSXotN zpBxWJ&%|1I973ub0Ol|{|F6RHF?ATbOs$=V|1V_$paN3UP=NY|`n_DzWSsFBmJt=~ zHWj%J1lf=SPq287;!><>rHkptNc8#OKN}XKC)2^6rhL!T*kuXHSh!l?6|ul&JuWfW zTu&9MAhQEp9~g8^VeAS&4>xCgK|&i|S=RZORb#@`BhRTEe*KlyNF6oAV@bm%Al9~a zXZ4<|R)==(Vq;H|bL`vWYFPVaJbQM~wG3Vp&B@ zfh*!2S64wjk>f447LK>b_B!4gfh*hAuQRl;8>xWI(7rjfAslgxDs}O^Nsq|8ujFl< zt$JyQJ`U7t_?Y`6DkAjpB_<&U@3ESMjwZVx;xbi6ZNrNt*R6b7YPvRAi zH2(3W24tN_)pqG7^Xe2GWQ|Xtbh(e53~+SHLQ#Tc6xTHxyAZK||EOuyRK4*CG`zpOXngJ|?y`k9JJx7SP z&c%Sv<;(|g?jr2q94qtvI&3(=obvpHQf2->(t$s|R{@8y3j^s$#Bn1!a3(kIRI1NJr+_gu@ivoG6GvQ)agpRNnHr5cOX~xxW;d+KIA*o^8}KJKrmODIC9#>@bP?M)MabXdA~LgyZxa8{0)nHxZrN zgN#;8xEFbfgNpGxYwomfj=4`!z{+tSgW!4!C>^)Gjws7O&?Pg-R6l*AqW;6;6Q9F@ z0)N!XuxbqmF+XZ;B4%BfsPoYxePK5hDQI>GD)|?CgF8B7gs~Y{`rRb9(wlOOu!B=t zEOs}vpdxd}Cd=r|9qJ9Xa!2$3@MZ&y)sL3&i*aj)=ojTNjoFg8{Gzbf&7qH@ZjUQ8 z^=$JGWSCi#A1zV&mZ+vbS_Wn$QNS{!ALN#$%-LxwfD19g(Fix9>rys+JHjSH5-+ch zR9Ph;%Xw5*d2uTAsiMID^ov7Z!bdBW)m>@6h7P z!A_MOz8l*`g` zgJH0SlNb^mu>R{y#hGI^T{svkIY#b7%ZTdzP^r>yQ(hyaqkY);!y`&nzf>r2vlSO< zr4{^=N$5#E|67{5czoo`{1k`iXcv(xe`f6KG1^skw536zs>D33Ic+FQ6ng)@=a7M1 z-PRaB>d-3{aGPk?@!vIKxisTn;`LpO`rS(2O(3<{i+qYmDhh@7lvK2QplP&7vIxNL zyO?sqfo>XT;rC@a+9U%IeSJh429!v|Re()Ri!>r8%Ob*#uWO-t4W)e@ZoUr8>haL&AA*!~c$P`O#(* zCW}|SZg|P!SKGzXN)X6Dd?fWN0TH_dKvoLWd-b}EzjxGYc65RzWpiJ1Bwj5*RkJ0l zsOwKzztz5~Vyj*WQ@!)eL=P9zomq|&^B91LsWhT4Z+>4+^U{zX#t0SUS^kY2@k@b1 zTvc@KlZj(k)T67&is&OV&^8oLjmmOS3+y9t9P5)QVG>7Fe@c~LX5B# zT$r+g7c8XCSteAKvi2LNlIp9bXksAigW64X;UfY<3_BH5!FG9(MSPH{x1%#DvqpoeYNyU)I4sd0G~6ws|KI(MLV zSPDVDqq>xdFNap!tp6yQa_-oDR52jUK8Hip-J<1vY`k zf7&4z|GAXO7<+%EeX2bu@cwFph-Vr>MMgj+0@D5`s!Oz#3KVHRx{j<75$XRxV8t~u zcOo)&-;Hh)ToY;^-BX2^OU(9^*(lDfo=x7WtQsn7iyANOPm7nDY8OoL#BBM6o`-8s zGy~>DGrS7}z8tz=3B5kt;T3{=&qvvuXhFTo#q&!f84(lj+k`I*=X zCpI;(X6OuM0MQfc(@WK{iA=Z#Di-0ydNw7HQ6n4X=W*8pQ-ox!Nor=PnP5hci62gA zmf*w|c7}2t7+flTIkC;SlaNC|US%+#LKi;lE~`oGuIN!#)8lSGs;IT$1-4LWtbhowUd69;fkKhM4*==K!_iDhKMg^P3u-%THZ2jMIcT|jOnZS@6< z%eYJ*nX=(TCF)BLP2{5zCn{gp+|Wy$8P8`6$!W3oaLjxo%eEQcfJ}~-Nj=6s#2`pL zje;NC69fYeEHq7ZV&e5eGyG&qiEefXM07+{OlYM8r`?a@+)Vb}rkzS4qtFoq zKChKM+~1u6yH|5Ql17RN+%19hdG3NGlfwkyDg6CiHu0X7kvPk_s{s7>tW-_(anY)N zk`B9X@Z$?_oszcu2CYrYEqAk2-#Y%pq{=<1%G_;v(%Hjz*X>1px#(2`H9~^MiOc$M zS-Wne%546|r=dushTZp->3uCc)-|nSq6hpQPXLQ>o|FYCXJ{(jL#2DDvYogV(`7Yx zZ6Se?8|w2^Xw0HM)UY0z0+0Ai&pVu;J0iAPEbO)RD(sm7qkK_nHN`zMSkOrmRuYRP zJNN?CXFK(5N1N>yyN5+zCPyr%^Sd3{{ekkkovMv%Dg@N@_-h$%tD1#QS zi`94sF6+n|%t}RH=jbgNY410`t3&=KPMgQ&o5~^faSY!PYA)hT z^Td)zFjC3AFT!5MJxlqKPw$H!p*r!%Docl*3cC05(rg{fwnp=-BnCEk-KrgV)s=K% z)`@rbtP6Wqu^KUq&z8d_Csa*+T!vQ?C61;({`maJudCuqv>Eqsrg(Q%l6-qsVn5-H zVuTmUArmUAauTk}j4VZzk=1!#Og-M5k_mLPaRjdw`1Y*OT2J6g!t7~eUuueJ8(v`` zAXV0RjM$yW?B=c&HHYywXe1o$aQT<(IWA@xH#3Z@*^V!^Y4<%WFa(|O(h0W~AA2-V zuxE|auGN!@N&SgEl8*jGgyn8c8qOb*fK_-yMv0ieDQ#!xZ?r;oe1#}WiJ+OR7yE?R z3KfSl*k#P^zNd4LtU+DFIx#D5EKxNqmxvx;CH>KFpqdkvs*!xI}$k~HP&8?11!HJ+yoEx@b zr0Xqk@c5drM;(QHBE?&>gz9zOOp{W{DTw21|1u4h=#v!DjVJq z%$)`)`*3=QI5@&c;eVl4qb;bSsuR^*b>whyHoYvur5gD63C421c8;=4Da5>h&THY` zYE1)0C>a6r0%p0Snl(xiUrLZ3qDE8GD=bAE-<-pFtZ;P&NK8;Gzs0lSPw>k=Bgs>vxhzdZ?0E>Uq!0Pa=3QNBRIc?}%lpOo z{zs~)zOXe`p}l25Gil${#DTGq5JD}o<>#MYuW4Cd7^N7|5vxn>6Ya_3poa@bRJH@zH;8YoDdPKoV9=+z=eUCg_$h&lcu8Iwx>4~J)LM_e3ZQMpK5#0Bkk<+ zY{U4N?bdeA;XxdXkK+q_?qkGCeJNB0vO}LIvd@gzLczM)60N&m26%Drd;vGrxt2~= z#H0pr#;0PFR{{2}*EzmoGJWS_F~z#*=OsTq-Y~8K+Aw}qJ2kq7{R4$-VXqCp_#8Jc z?cLy*H_})a7u<<148*WJwCkw7Tgi2-k-t37`1Ck<6=$3{tJkQcAL(_aBc0a8m#0HhYr|%-_HlO zO!qZ^Rh+}RkiFOP*Y~1NNL&NxLydKIzX+Qsi$}U35KK?b@O*`qR_whRPSP|)!mHT6 zb9iYYt_jMgP{fR=f(#;t&KO{RT*Px+{#&(WyAOB#@?@BZ<=gnls+}H;d$veA1H)dtfngL0e#n`4u zqo4Ej+IM6W*8|I+xemya&mDvx!55@^LuIGDV;a?PCf(sC*nI; zJ@vjH<-b;x5*{li@W3g-gY*$u`+g+m@h}+Q8ot4CAEA%2i|!Xm!(Qb9J^Ok>zXs-* zTI$=Hy)||4SWxCy;roHJPnEQgy&3(B`6;`79W;_~a%t0FaRg8QQp8E$QBL(}51s$) zhaAbDhvgrHDKd=~`2x3*1Fe5)CQG6S-M_|u7%%!yuF?Te{i`@AP^_H-S32pcm9azN z(Q4{YOE$61vVrV8By{~0DVRRT7T3S3LFY90bK<|)3Bo2zQ`h>0lWUw=|0<19oLrl7 zPA{_s-CmCG;=tB-VR?**qc$Xr)oMLYuJd!fT5CVhXpX?`U&VT{aB_pTk!IF#=FqM( z2KXBg&SCE+Z*fOfsD9v>uy=zSEpOP#9YPZ$p|nahF~Ly2DWN^d>X@J082XMoM&B7@ z(03;Eozc-Je9V79+`HkMOd^o>E$VzChoLle9*8K&j1(Z&BwmzIjX*l)WJf{;u?R-}O-IJu)S?7N#a7{&v`{*@|)rX)}9qYXnzhdk z;1fR)i?g}9Bv$+OwR6HC&Gclac-ZZfriwHm%|4C2=wsi$@9Bj8q?(uokpPAj^g=}D z0@~tng-#x62VNS*-InQiSTa(9Rnz2$}A5gmI_rKFL5K zJnkurlapM8B}GQ4CK4flHi0~+Y2EbPm!DI5Qm3rg2st_72z~*qYi3k=mk51E=PBx_ zxOgOOgp#a3XALu_S<#qiibO{5vJNOAdo|&3@=7>))%cKza++b(w4;~V=WvoLgo}vW zzY$>&PTCO1-w-08_$i#c5l-H8X&u{Y`xd2jrnmnJQGN1GRN<~h0h!Y*`6Hbxa z*3@G*fM!2iTr-ww2;7EnN*7jO0KC~?Yir+f38;M>GO}-l<1@ypsV;;y3d~aX6eWyi zeYe-0D_nDq$mOngiPy!YVM@Ahr2#Ca{IYj+zP8>HW^=;+BM9Tr{WE?IKE<%>m6)o} zM;P_}igISWmp**vS(CosQQeXgR&;Ig>G>Ae6l1}ivA$nvFdEs}^}BFtnHnn!_|!6{ zC|pglJ`ZsEl+8~+-)`b_ih61}K^{y#wZbjR#XYq$@pj+uqkk2C%3%Jd3YB5sA41Uv&^d)} zrm=Lh5~i*)XU0+XQ=5$I{!6UC5Wkuq+BvazPS`gVVaBIxe(YOlD%(7k+<6qscjE6G z%j*FtYm68yBt>2H`nXvn4Pd0sOMFp}C>~?*chWZCzi-m|3~s;>fsOIfh1VLFGi^ADVy{fDq`O7z+{#m~*Be&vP}ZjsPBwT+}0F`d6r3&uu) zRv_?*5vQ>4Ayyg}+sWviZUGB26zF8ZZU8f%Lj9((eU2Hb@5LxIuDa&#N|^Mi8FzM> zM~;U6Q5j1bKjRP&?AL3d>niC9`W-J6MqT#%TVwXMx1jcW4ru@8uz#~v4ou(w$o1>) znTO>t2lZdOJ;;22YkQk1-Cv0!OoJ#`2!Jx@;Ijos;g!Mu?N~qJ`Rt0l)CRM}YfV~<8#l@&hiUJvio zz>DH%2r=kZ%Xl8kgiclByqTR`c@j;D}$K9%<@a2{Ibs5 z?RqzMbHsi8_H#)X>ZJQkF@m}qoVhcL(nq)H_!_dtvEPP5=H#t-^{IO+2)T59jJ%|o zfW2cLph{GgbtFnlF~q75o!pg^&T-nm%*UO)t@fl$`!_OF)AhUdw!ey12PAd&wnAHt zyrO97Ua63gz;f5ZCAFY#ip7+gWklHfFzmaeLhaw?-xf~bH7;gA9&2=+9YSUAgkSx) zqHleAsY90?V!C&NSkTX6rvFwTuAOlJP)B}54sYjxCObDdr0;L|uxu{I#!BpZ|h!w^Ns+k+*-jTTwX0nRqT?dbKxk zSaxGLb@j1m*Rt=x0mB!Q-WEx|C@ae#2Ay<0(4WA@1-|}wGQqwGLYDtD^yoV3Abi!w zftBczhQHM;>VG&nC)w4P)#pFEJ7??c27HqwbuvZP59JpCB)xZfhONZ?8-@4j*}BT>^!(6&J25qUPRRc4{LHe} z0RMx1xuCjku4b57WH)m$0uT>|5Nl`QG(SeSVk*r`*cDfG z(=YDlWLd6ijiItd_g!}b<$$$t8Z8E4VuFOXOQX|TC<Uyny$65$75Kz(AM`Y=jUwQo=-2b+hc2|m)LD>becP#Ac=&-n7SU3>8w0Y>-MKK z1cGHFdzzEh`IR`g^XH-TLyeCJQvaBrotD#y$xg2b{rzAW`a4rmbdbvFm7$*-YU&x= z*`HpOdSRzm8?`kCIPT{i6AJ`I6q6p8)|YLNYCM?7|0WegodC@5#95$!dfT5ty*{5#!WuX6?eJ zT*bLg@hlsyq~TUmU%H4@MhsmIi0RRFv~$0vPfgz~nr<~067HEs1;^>R&v`|Eb!Sia ztEFc4%Yn_BjFeB-O_ntJ4m<{*8B8kmvB1cd+No`p={ia^42fwyIciPhVPhvV+z`lbQ zY4ho$3|mS2UN1W1RMICn8ScfVS+lh8U}4yQO}+D((S4)NeY4JetImD9&V8rOeYehi zug-nH&i$ay{jkpcsOCN}qs~3M&fQq&o?qu~u5-84xm)Yp++vcIkx`JEdvTq6Nu7IX zoqJiGdwHE(BPEG$*h)3`syg@TI=4qbBApMctqWgQ=U#8O)yshmjJCK|4s5K8(NX8# zROjAY=iXB1-exz9TRgy7Ry=4S#nabdTQ=o%4sd830^FS9aiwseGkZ5=3v&;2T0kCf z=koM*?TS9Fy;x~i^nt2|^$&E017qR9xS9pxVJ2%H4~*%-;b#m9bpN2Kcwhn_LES&F zki1nazk%=cEw`%Ma^5y_nm7Qt16u!BJbk|y_cWd6o-O#8=B*44bQ+CE^=P<|*Hy^t z0y)qe54iGH@$bq~q@g1?rX7C3@Dnod@lcWAb75Yl{CMci8{(?MeA9YG&%wMZH zqKmT^bO1-~lP~De-ZsaGi)o3f^Z(HWbsw5ISUav<_2(Q0Ng{N*%QeVJ&Aor0RrVB` zS(JfA*&C-#X-UE?R}tU=xz_fIIu|Xfb@<=KU9$KdTAtUX{US^f*ka`&KgygPrR3B; zwBAsoG)|Fy7824j^NM26&k(h0fgWU+w>Z(}+oo~{8!;U(`@kNJ%;`l8H5f_F2>3M0 zNfkLu4F3GXv#EdRPj$y>sL>WD76T)?mf#XcPAv86?o_|z;MP5{g4Qis+VLFXnG?`7 zh632Td`d5CwaPuQ^TaAz$OKEs8;TRF6WfCv80ZpP4)D+)1_oMVVr@8}%{vooc^r7; zBelwlq*NG}OIhMk+K{QeRt~;}eUE-H}hC+p_a}CF~cBGH2 zZoASh9J{lQcI8^AT#I;I%gT0*gN-6CoQOv1+VD>-g3F=D+GuuuQC@M)^PjH^u&OJe zXCzgm%lu-tYD1T&uVhvduieN&i#t_E620lmv_;L8DTh;cv6xfQ>x#^JFEtwh0KcZxrNLWNb$J3b#9Y7()6T}om$Lnv42L(;TDE7TKg6%7o+LrKs7%H ztNO#4Mbje4$3zuWt*dt{G(>T+a?1qa{*&^3fw2RFy0Lf$jn0F<872`&)1O)8L=Tx= zapA)EWh%WcDh_}aR2*pJCm}yDJEQWzZ28YfKb~3ZPef?oR1?7(z&4nj4yIvib{$|2 z<>Y?)2EJ+JK=;#E9$JMC)OCXfVmHW;*gjPpSe~tE9k}d6s?uQ)It5cd+3ROE6}x_z z=~jJ*6GpgEl_I^up)jqf(zoDDGHtL`xNmtCt$1cz7@TlPIWFwXcK75tF;*vKM2od0 zd&d(>9HF{1N@txJ)+tcicnnU+wOyTrGdoD6zRFu{x!3iy#WUGSu4hXA#1UAsPtNow zpHGT9Q;EK6+24*N-%^=6hnE^jA5ItgrpT&K)1}2TI#t1rvCJ`!BcG}q*n~;?2dXcB z*FKffr~F;})R{iz@7kx)^eKNk_bD=U(JvZnSHi#%@9sp#3>*mq`f$7<3_)X>5pQUS zBQhv4*ydS0b19{kR7o*S3sivYxBjds+di-F$cy|8Qp*@@VG4N;vw!5Y*qa<56;aJR@_= z&K!+6RG^QWYegKm?2yHu3t5^%z+mK}<1G7JoRyyoS^c??HMNkDDZ^P?7jkh0XW@Lj z&IMI7b^A-N@bUWa@rJ14YLtV1A9sZJH$c3P*Xgld4|_X2bZi6Zdk{;a=woEamiRhaHoiktvF-yw@9_+hIpN?Y*n$HZDaFA}IwRua$f!Ob|0Pv z1J|>W{DJFubYf`n?9x=)vyqtqB;dO}SaVjJUe9_)B^sUNIutqLs9Cnt|L(ldb zs18JYMUzI-15)WAlWG1bDn0j1hO@WpBRF1WUFHde?d=z3NB!+IaMOKGt0A2FV8(62 z**iG`pq)dFR`|$8LBHb;rz&5c9qaQE3CUOlR~po5VnqkD8*t7G=3)-IxFm=Vw7$A_ zgbGKD}bgxt(k)bEb-GK9ICR(CR$qh03LTY0&PF%X-gdvXn8&fIO(uZV=4UTc^Sh4pH zjk+S9lP%w6WSE~CgI2y#HKgofP@K#<>TtVj4DSfTJH}*F4HNMw(KkDUkM;T(H^r^DqTSX*T9PGgZ&>3jX&gAfS6Irc(avOG^L|^QX zD4plzz>nP?^HmhS0BmT3eh2S>9p*)xt@IboG?6^HUE&?J5>M__COaA54wFa;BFb+8 zG1)ICH*RqW;T0Jy_wmOo9yxVPCt0;Xb5}U400(p2H++Lz=}Jp7dFVRj;X0K( z-682zHw0mlL9&aS*r>c%s0tTFG|Z-B8B=B_fK6V`LZnqqCD~q+*Rna(gImJEEg*YP z#5;)eoYgeTJ(sTEX#+=Ghc|gsX}sEF@|K*pbfU(j^ehu;+1%RnIWk4N{cY7W#7_IR z)Y5xjaLDw?FhJdeTGrfqUogVruE{%g-j&llH!0#y-p_i#%&XI$cL^?tH&XCJkCYs?~X^ctQtQzJJz_}jdkw%(JfY{D!XQi!_PH4kO0-exlBTw zlZ0?rZLqGZHdr@fHCQ)eHCQ)eHCQ)eHCQ)eHCQ)eHCQ)eH5g~CAV6_&9o@gdb;6Hk zJh;t{S8p;+C2darl+iIL z@dLF*R$L5jo)#sK3U+EhH;AA7rxK>$q{Zh~FKfbQ5Il_(|JQm6to=M#b)Ta(!)avHkp?cN$Zdx#s z+FDA$C;fmW+a@1&_x%kCf{+-|e$hn?1!=W@aqzBXG$TY|1S=4J2!kA1DW)BXyQx@q zIkHZbZwKp&pafF}O|0sR`ef2HjSEHJ=Wu3TxX+Tv#Ad6u z*7Y#BdG3aSZb-WJQJyztN_GE{t3s2P))PemnCgozdxh7C3A<&{1$(GPShI-N8AIsH-O^m7l? z!TluDkX=93#Pf4AIS-W3s2QUs-Jp@EPogSBK%{usSRS=RI<>PJ=aW{?er6_C1Z8++ z{7EY;ADf>G{Yjew+2k|3*!z2JEZQ}!D%>^9#C!3RXl*;K{n6g8`Ii}g`z0?wS!2KF zX}f;wkd7GQ)S1~z$?T8P)v49KKiNV1wqqv_S5H5v4D~l_mIr>(zD>0X-Jc6l*~uUG zTCnA_^SozenBxmwzvXA3GI3%^I<(6zR}$yEl7EHn_9y*G5=ZXzDc!R5w6R8n>0hNK zzt6#?1geGcg4=zZcM4FLRYyGIJ4VcM`b0YalZv(=NQ8?ALgz7+?UN(=a>RrftC7+E ziJIS|)a2+k-Q<;9sG;t+zf&kiVZ)fEKLgv--a6x(#r_z_Qzu0MkM1z#)fT%9hpsTWM0f-d3aa7xp_h>GoI{%C*!r z(;Go$HfTa&D`mf|)H*$m8dV74CgCTS`KD@%kwu})zF|dv+~Jc;brIVQ$nY9*=ElM& zmzD66Mmj#ZOu>>vlX&Im7z&-bxpNUTbM-_zTm;V0Mb?OEvEn=)oEfX5 zeZ4V(6hm02zeDfP(&ymG#MUJxyA(GI`<`5O=xO~U5ZchdL)QT6jE5#!qdGJ}Wli#% zr(_d)SbZwmJThNX%z1Qe+7&PQd8)8Omcc`lb6H@?{0gO@rI7wc`<%IR=gl+x z8x)~XAO`i_`I~u-G$k@HcQSnKz#B185X$eMypM(QyVA?$cfZEitV@}Q5v$g;pe&#`cWwjc*@aJT zrU*J0fxeJq0O6CHf59}#Q2E~jB7rtkez5?=SYLn{nEB-Ao^!*HmtTD%o%M+&{IJPU z-Y0zbsqK{CVb)D6T!&kz^I>t?Fy}LRhhsRj1n;3GUYaD1MFh)cMbOSdZi$k^D)}(y z^U?~_`dBz7j3{-99~B+Xn5V<6rQciv^czipTNy+e5yFSRDZx*3fR`UjTr<7 zEN@l%6?2|Y969~)_ESM$-U`RmC171ogs>bO7PDVqxVgPJyaC{wc!5Z9xC2V?YixxU zR1{5uFM_;Dj}8(Mt#xi|V~$!VpA6+Qp?p4+e~LK?WGGfXqsLE;80Rv=*LO3;bPtEl znXt8KBb5CF$vancIdnPn{u{{;U8dt99vn)rQuP28Prih9Zh1e z2?*Xtcj$6Tc1hzOhoT@Hz7&QtDK;!AHtc>&?=fiyne(vfW#K307dszXEiBb0X5yuw zTKqVCHI)BZ?OeAGzvZ!;;j2asNpWZqKcQy#KNOxT9vVt?;=K3bAr7J@QZ+IuXHFl2 zM%-QWb4-)=VwtWH_p|*{iY94TJ1GhQQDq<6sCqB|LWW)MKob-PJJs57HUD%m4Et1nk@%uMU38n4CLccHp^&XJ+d0`qs&V)TzX*q* z97HXY&(C9lKzg-KS$U5==~t>|sQmgWdKiWuX6QXs#t&T~Fa=^qRWaOR#-ygCB;dFC z3O%%%C(#eh7aM&#Bg!uaeLABF{Z4&#(I?Zt$Z+Lf5xx8?_zX??sfVk2|4rrnwDFsT zVk`fOpEmP*75wHDKb1H<)C|cv!aro^_mdq>@6+a4PdtSzBGQ)%^PO-}@zX_4=m+gp zvWhVF-#MhU5f3@J5pe7KQ96$#{K6cN#xeWyJKDq`I?fZrmVX_}zYXQjRE*Ei#&jPV zX919Az7qD8+*z5{JvJXkp@su?tL zr@s9e29>@#g#FR`Z{g4k1UBi1A5g*2uKerBHmYB93q0hj2>uF_UdLhE1p0J^WBEgI z_(t(i?1sm~l*6~_dw;qDMUtCBpRSQGafeIADbGFI0htHiYuOhO*`92GwF@zcm7x9siRO>O_Ax`L5k8Db<|;98GJU zE&o9&|6qm?H-z0<`4KrI?B-I4xqRO}M+co5L&OT89_Gq{}HAxbNZ$mqh4&Tw^HdRG7Q3_kS=k#?|>Hywymky^d!=I}SM@@5p z&l+Fu;GM@85uUPO_SAQTUW{!;~s*okMqT4PSMJ zhbEv6-_QYms8^f0MTAX>O$9+le@x1kLz|T6p-q`|KI9Yer7_0TZT@sKjhuPmLnZsR zC*Rapu_t@?HAFOF!Qm2Ie+z9=Jk*ctDz1#7(aML0D2~|-l~m=IMtN*`fl~J7BA7Vz zNLZu_=GCq@RDp-aRGEjy5H3g%{(L?V8|!VL%~?>Grv+8|2SqVwSYK7#^`=NDX}9am zC(KDn^vt4@pnit_JzJPdmlw;6svt}i;+KRm=^CFdD7j(L!JWhH3~$a!Yg{qf;$pJ8 zv;fdX7i`c*ok&)AVopONS6duHm(M0Q&gLKuZD!7#20@oOIcn0YH=;Xx)t9?&T-jw}x& z>%z#2;>gM{vL=jZbbCZHYecPlWV^r=ht{e6s4MCo>UXs8Z>lA(ZwF@VRO%V~l+LI7 zhYH|OM|GXqK1OPBTS}HBVCuDkR_q2(wUX&ntC(-9O-{x|ur2&uLvhB!tUbcL^OpcB zN{y`YbFI2mBO5$!FtR}~MmDMpo{}5cs80;CsKBYTjKxha|8~P!?(%8eVq~euFFerG z&_+JsLhBk?$`2pE^eK#NLOrPo#bnwPfGd3!!MnIuvrRXv38p=HQlM=Y0R7fL=rksd z9pFg@sU0di4cDiBi4=r)>uF?QYPoqq0|ZcNw9S1C3p;h?2|A5OJb*WYW}` z`kxCe?P26LNZpP}>F23@5B$5SA!m9wzGKWKcZ+0%k}eg;UyR(sX0j8vu0XN}W5CIn z2Ucetjqkcf8jQqksLJ3Tzo<}E%Of7O8+o7>J#x#7{YP%ik8O9>EVhxdgt8DuuFFIN zpQ#QIR!^61T0+JXP$CrRbJaU?3xHfm?#X~SW{OmZBpTTwPgjDKatn*Q+h<~3ev#9A$ny`PZ?&xJ>!*&Gw7OM_lKaA`xK6Mv z(KW^R6PFvp6WPa?q|{s#CE2PZEFI;_p~zbafEs$nZq>4Kn;H|iG1;JiGoc(8A>u)x z?msHlAp0knlVyH3a!HAa*ehfqF|1LkSNY%$RXJ2wNRU)lXx^!~`z12u^)e&|A=Pv& zOA^g^h}=!Lm8L8i(!Kp_?EEZw63b5*$bE)s4^;gtNif`kglgXQUwj zFpm*$;1U3d(GBJ@w+(R_5--)i7n0_@pgLSr2$|ori~<&QFa@ap#cJGq~y! z-}xEoMs=2`(=wwLsNA<|A``76sNDYsO7J|i%L7ulwGzwQvs6SC$l2n{^Nb?!;K03b zo;D*%kwZ`MJXKg>(#@7a@D<$u8Bt!jM>AN4M6V^oe6QV}m<4NosV?g#+L2-O z$|`x9P%9}3WsdZ}t_w8JbI>shkjUzNwWsrK-VJ0-!}&!{Qe9>Uc|oOfV<8u%iB-JN zwb6^KNU8+Z3clzajRHoPA~v=|CGr@WqyC5enLcK(>T3sd9kQmX*WNG*0 zK#Vw~I)#0s5eF)Wo94lCTvDW1olGravew}LN8veQB^%~WIKP}E*61XPWxp2AuOJX1 z$L?22IyD&8X-*kw_46y!H=vC>VAb?$v>kDNxf(4dV^y}n`4#ydTZ2{5NDGtc1>4%Z5&HV}n554UNp0xKC0x0$0HWOAdK54VAGl6;Ox2ptN}qt77y$sE#}xh6oT zd}I&;gzM_PT~3nA`DE%ldsDDbx=X=O_=soCwYmmm7Jwa#QlcpKQiWpokXN>M4>3$F z8T0-vi5KlVerz0eo@U|C51C*Q9%rKvqA_Z1q=5n6Z~SYfiKC?vMlL$L!BVEm(0;YG5)z#&oF+k_EI!YFBrxsuU_acDOCHo}d8RR*C z)d)WTVFy1P_=%c znety~wv%=cF0@EpUT6)6uZIij9A02!Hhq&7i%gcF74vP8ZS@0uqdJR zErIImA!T2WTH1xh{@}e47na0G-7l(07qYzy7nWsdjo*sQC$YKvM`5>y?RWo3k9S;- z$gdY9gD$MpdVvdw+{B7rGIC*U?uVfrV|^ad#2pF63mcLjiPjK~UUSA99hTPGU+BnF z5RyqJ%#jP5lrMV)fU@Naplpj!j!rnn_USQ9k&?r_bL2ufx}AR|ro6DjzS!Wxg&nkt z;)Np#4rhVIR^fv;E{x{E7Li08ivXQbY*-COWFo~(1$NlQ>0xTs7cSQz9=&Nqt~ewd zWp0b_SHB?5jruf>b#rCwKafi1*aO_g*3UPFB(W3^Fe}3Vj7{Myt?B= zQUYKi;oXbQw??S;=F6bc#T6pJ7lkL&Jb%*5IA#Y&f;^%!SEHg0)(O)WndqhQ7L4SyZz#sNv9Pb+gMF zJ-K4Ev1NK_rB++(!J-<}G$ZW_b_+w;;*>EHH{PWMtGBX01_nB9W#D>((Fj&=4@(F8 zq1X$zdM7*FC?nFmo2S?&6iv zsL(TU39t=!y=2=f0W|bkz1K|!p@?WF9z@cfys*gZYZ)0kDD$B&vza@{b(0VvqA#CtL_=X$=8+s6I*g>%22f;=j1RHe_ zY;;SEMzr19RL;0(pF0|>O z==%LpmU3>Qte-CFH#m2fk?bVf&L2xZ3ia#a+OJNl6mbfmUQ=rIoKmabOtVtI-G!G* z;Z7l__e#euJ*lf$Z)+s#oLpR8RND7iKlNKWr{;0c16FHQk3|!9hus4QuBj)=(2$jq zpkzMdp$L!ouCk~VaSxb(IzwOI#|w3hD=31zFz zib`6G(-?>`tbWuM_krX!^v4*0DsLF5fPEciQ`IoUVHl^#`%wRkEL9SQIdOEBv|)I} zeKh8+(DFtFa{c;-G1ikkN8B(Lnb|N_!i;kRl7{h?qqszo+!*q+t!l9Kg}D*7Rm{$Y zNn%nDxM8xTC3CT8WnS$j^z|W=jvh9Yr5tRpv|+jsG*2vV^I6E|tB^HRM_G9vY(rgv zZIK%4B;jJ~vs9zlh9xO>JML<*H3)Mr5@}`BT}XWt!M;U2a)vz|?P425aS0CEvB9*h z5*jDARNKR@%65v}u%4=1e?caWm!cmOvZltzps-Q9gp5bMM)7HSM+JNa5WXl$%4Eskg-%tuO&jqN; zQYp<*jk4t+=m^k=|XSX`OXd`E@moTg(_v+fAzH>{hBU%``r$1 zc}LFy$H_}}V4;5pO(Xh_Py;xNSE0cU*7W1SC)BBnvBCC6{6$E*LSjw7d`!-j?0`ZP zyxU(Nw(67;5PGY2NQ{Ws$HRgL87(D@o88uogazF{4$7b_64#8RL=a7vr%>c6~@w@#_Q@?Hw>BFaGEMb(|4hIG6X>l%E32WzGW z)ul=lZtcgudmIm(t|b8;teG+>DCd12<+I6Aj(bT_x0+B~(BKz0e;3c&U=Nc;>UuJ};eZJK6cUk8DdZ#Mb3+86(FH7*0 zVmrs9Tx&v5G<9p5RY_%!YkK2aLRp%cAyi~;ux3318Ug8svS6;KWHm#>*Q1MtcIqTs zy`^vQtC0z+U^bxlmWZ8mMWYDkHg-Ql3kDgEP1bN!?beeuS>V^x*3gZ45Dg6sEO*sg zf~hwtWxJv&6Jl+eO=}mnvTLk0LpU1qcHtyOqjCOd9jXmIHfA{J8fFwTu1ZbUlxx&z zH)6+;>Ov!r`O^$Gc8~{I&qiy~G!NR?TR2P}+w;bLE?BhZdLFpn^WfAo)81&?f0zzE z+14MnL?RAzyl95zYI3o0xDdo8iAL(lN-@g1%*5esggVCgL?>j%dLd&|2#=70XPgi< z)QvYrUBy2kg6Lk0#z_&xhA`QNpwNm%=k_^xxoIrizuYty291RWmX^jsl*-0JiBJ}e zQ0kd31nyGp4DX)nt4VbsG9J|=^_K+*^|Xe4pqjDF=*C%$W0uHyBkrj zv)RVNJ^%z;4;U5{YZg;xaOJvs3v3D7UAVGtu(jg;I-2P`Y)-o?XxtDq?$qxtuIe;y z;137Sjk{Py8b4F`VHMdZtYx*)5_`=k=0j6hQ&5;}m$a#smCyF7n_BBBjU$#OTQU4N zrg5LpE-pSdx@p|UJ3yMcN7;CEEdV=v0*(9dfwWECIjQ75g~Oc2ebj6dhrCU_siCHR z&=AZamV@Y@$D#p-wMSyJj5ZCof{M0vM?--oS9h@VL$1*XOvIRw7#k97q~$a|ByI$u z@NDg1c!&h^H`8vy2TM(uMg-w4|FoNsgP0#55tJ_v%1=cS%9r!!R0TUZ47NBVrYn5R zG|Ub4XFQ+nWV7H)LM-bnmNf>}zPtU;42v`s46O>E}^%*f%6X#B&vLNKK( zKGYZs4YrM6!(!L8QX!zjq<&yieX6rzfuBoENX=4fT{&)Y# zdEXWQ4oKN%X;V;xTrXKgPxxpb_Dy_%T59%jx0ZFoB9aP6c@yq%LDj>lC|;TZ{X%s? zQ!`S>0?{l*vdXZyV4kH+w?E|7PQr3w?IavTWwdNBa6d0Q<9x&&-H0cIxd9J5Mxh=? zh&#azc8`MQefWo?bg_Kl5jz!m#joYy*(tsQjeA(F0}Fy)istzAr?@IJKWN$>OvAF9 zCPbTdGG26W=VD(S`j0`=uAm8B9u(TS%k1c)cCpf}TZo*4TOy{wP?cqnelcYYZk?Ye z;T<^z&sPSsE-Vge>m1ec57oDwKRdbCN4M}aak8!HGgd<%nYQHcuz&4h;yze#*4A-| zg0*>;7sZa%ehM-ATx@G=LO$Cu6IT4feHJcRBbZ)%w5vjHcd(n`7}ezvO>y?6Mohxk zkrsi`xb2h-|CI^RHYcs+^M1%m}XQ;eW(op);Xe`F-c!ISZTpntzKax)f5M!d+ z#n2ygg3NMi5r`d}%NTG6-le$~8!gMVe+Y+W;f+ORrQ$8b3V*P6Fk?UnS;1O#{gm_c zJDBQ{1D}R;XGlXG609A*fAlOy*ilrwnB2qZS5K^SSlp@IeGKcdg0q?K7GajJzu zE30*8CL%{<802406O5N+VIMl53oyM4FH|>gpN` z`EE(hBLhMIwETPw6N5|X<6)Gf3}Jd2inHTc!I)qcX1{Ji7X`B6d@s^>PdGd-qneZ)d zX)qPe!@i(mUrKU&PMu0dvup#lp@r3J6BYyzTA6GU;-A*tx&sc@LXPjP!;4TUR_13g zD{gd_to+47*2)RCMj?Ty{N*p&oGgDauY8-eHxt~R6S(0M!sRbg|7#~Qz=__FU*gL~nU3l*li5Zlfa8}EXp?lEB)W9O4aXkM}IkwfZU?~!>ZY$i$;+hcSI zr(6}(p%l&rHv+`gRw_^WV`8vmn!xF>|H~`?80+B(H5`249-fjx9a%(5hUDfwh6{-1 z^bg9=wFVEB2+wo+L;ZPc?b}C6N@?+?Y~vbaUMb$Vq}tpjtxY;?i{8RiygUmp(8OGs z`>*$YY0C^>r-mUtU(ur1|J?f~I(T`dPmvL}N-qgJ1Mgdk;b;#g>GMkK!`q-rH)|K# z!gC4Y6eFF0-l!0?!2s8{AJCqR-5e2pdub&FJ?4iGfTT$Q`^JaBqLPkt# zNgnGBuR66nq4Q@cz)HLuhXHU{ljm+wh^E!EBC^ji4cq&BqhezC=t|0d6_fTyJ~CNk zaJ>>5AOy4(dDa8-qR7rEJt+Dk%W!7jRifI#wg>{perTN4@pw=>IH(j_YvR*}*qZv`N9VNuuydj|z;xQTJMU<04hc6C#*4=`17(xn*m!T-mDfZcOxD z(I6cZvQ9J?WqTnn^Tk4=Xo?rjDv(@ipCchro&&J|6`9S1MYE2D;c-P3>Z4pO{%?6>d`YRr_wgJ@E3j10VLe&~oSr#I|E(IpUmXiV-

    l# zx}i*}$85t8FOuX&hm@J^beWb&!$eJ+#G#veqGLB-&}H1M#3)BzdPs^u4@pEZyqR6P zQps{JUw2anqM!u$Y*Ur-F-NZ`uZUx-GLUc(kG4^_rUSW8lY+CZlf%rk8o}sy+A#Gl`3J(y8?9wJ zYte8(FHN&Ch`>jKn~o?Dd)fQNXp0XT4%h|+1uI#CwG|q;?Ub4~zA-nD7L_NLQrtAe zA`<2b!=(an)fsuM%U)=$Fm%CXps46A4XT7;)#EuXsTJ0F+AxHw6e2uk;IQJ#%)=R~ z6<#W_sqV*{=FrChG@}WcU*Z&uQC_6X?q7zXVm1f7+!8M`7!Cg@K`{6;XdL(RImlWz z*3VR(QLX}le6+kN9;bgeVeb$m-h=CItq!HoM=|k8g(6XpV&^JVyvHKg;~rW;HrN?1 z9s4!jSm=pzp)Vcwx4Tj8tnK`K^_{G|-m6m}kbM3I9B-W8-r@TWj4d#{h5_;l?5#pK_ zYj&8+dpXJ|iQ$^)Fxgi4FcB?7-H$DmA{Pl0iO>o|v3x=#zIy`0ks(7^758&zXE05J zr$?9QT@8bff~}}Gwscn%hJW#1k6_wnH(H#wl|^|oXJOY?g997AD_Y~s3@TBp;L&-Jx0Gu=kchC0?U{oIbFt^J_6oW1utku!E*RH`qXD?7_>$Y`7rt zq3LW9F1Pp9G$W`Pdu(083;>o!A*r2ZPCLQ|shm-c&71}67u4{ej5P$UYy`E1L2V(H zF*q9ldt*g+Ejm3q!QCGTuX-Rx_+oGBF#}CKhCVmQ43Zflvq-8B{#YxLTG4x~HAyWD zKg#ulN40S97;bYx{b#ENyRS-(?qD!)EYIn)r;Q6f>lA3deg`uU+_-|idcgjoz~MOg zgXg)sdCq&@1j~ujFsQZRSvT>G)`)ZJ16Bc3E6|&M04b3n#8bY-r(i6zjZrs^1sOTnl#;<6ZChZ5Xxt%>f6LDLO(7Ap!k zDtHVHBy7R`tOS6WQ!8Dk>i$-pWJqAPAr@i?MdqkvFqk!6@K|#SAIi~+4W!wYyG>4O zENol4NNbbO{HW;MWN%R6pUJ5#V&rl;9Nc+#StZR`8O&M9G$|KZB2f2carJ;Y$P0XBN!VRh5 zl>_Ib9-l~Zaa;X%;FUHdrv_6SxGh(blyFUKY6G3u;&gjD`nmUS!Ud`r@+u?sCSbWP z$wy&dyNr8JB9m!&W%3_};e#8JvI_F~pS8wP1=7$Tim47v&Eg3+o7gw2KhYY=;N-;g zrnJjBT4TS1S*h-jCWqCRKvB{gh$Rc(bgY*oqp!{BV{oJeYf&T*@W%eE=WJ*OqZ2fTaRn7v)ni;cwz}vtLChoePFt$YZYNF&nl~kNEgleU5|vJN^RrI zo_&%LLwZ4QX%;^0>0+@9rx$TU$^#QQ8^n86lFG=noXVgye;5<|VYCjvLI-&9crbmR zmUH|Q07bV^{_qBM&ZeM9m0p88>#xB82)6yfG8F*TPCvVwrWe!F=-@@OScSx76PuAu zYMUHuqya|7_FkPUy!2=5^x*n1DA$Fp>Ea0%*lHVnP>mf+)8WY-BgcF}7Zk7RGX6|g zFYL+B@~T}g4B(TWwV}~K@K5f+3aNT6Dwa>~>BCm;!40Wo)$~?Xr-&#OHNEjkN!gmZ z#u`UEzO9sVR=FV%;C-KN_6RRaZDTaKWlNh}G{x;+gIfF`2MRMfD2AE5MV*H>X*Bd| zQS_hvU-dtAHdf4{@|FyS`%d<=!=AYFjGmH5>~>cB7Sb^67O&S0IiW|byrukx>sw)L z$0O?%1ja)gFa>xGDkDFb#6jgG8WAYHa+0PmXS8JxL#+yX8rG}jn6c-Wu!Cts)-P@$ zLGKk~0rZ9xpJSFe5{%cQx0HwVP@`h7%Kp1iT`E+IM-t(KR_AQ9>qq4eVKH1X$tt@Q z@;W@x=R&l-Q0joa(t|`pRYp98op1Nn)mt6@kZxha=iVT?NfQxpwywOg1w5$-Qk(&nR|Tl)#U)%6TC)3i4xeW%W6bR6mNRaA z?CjUU|0s-|xYHXRv6xL}iELFw-{O$%c#8eCr!)=x)R3c5hr<@Oml|QPwbX3oQj4`oqY3#d7N@46;Q?=uV{yT&<<#Feo!0iL zjbv60k}teGE>&k7q6pAp-V_PKDRE<-I%84)DxF9JQ--({t9&{yOV}}&t2$-DQ`@O( zFxqV4?ud$Nb(LHoESssWI1X6wvk4$}xk5elTpFrzoo@|n1v#zk!-}F!F7EUuw{Ypf zFY{RUL9{medrfiMjPcp!Jwpz)uv=0sY(?{Uu5uzSs%&P5;AuNAphtHGd~SWTs5OTI zF??v6x!WB6b>4!CQP$2xqt526ow!>z>MTQmZBgRPXi01U((jU^rb)hB###EHiHjb; zsA#^7jb&7csfd){71TH1UTL2O2Ni?S8thXNAh%jjNb~eS-1^o5j@CFWfsG)|4}Gd#9|UBy>@40Mb{Dp0 zS#fJS1o*g82M&Ga1+~?T#o9^5=b}Xx22<{1Ih63V^NwY|<{--P@j}+QD{T-QxDQ|j z{Qt(!9Ut_~jHHQwdJNSyX0Xf+UU=7BfNx8^6OkwWvHFN)FvGz>wW7JWF|#YB_@7~T ze@NLH_mz$a7cwR#l?Uh_0VBA^agWs=uzu>s~f2$`_ zJW~4cD)YJSf`=@c|8tqoVpr?xOk@ul%Ltn~mJw>}p$&mkfP!(Yhii4AM-1d?Y!^fHAvR0het86_nxF zHnR>-_;BD1PdJ=?4d^Q}+gp_lmIqWsI&u+^0UyxjGZ?5EAI=yeo#+PPy_W2CP%uvO zJ~TA1qDdtR!MklvbSSRNER=c_a)r!Is@5w|Ej+*`1Wz{yPosxq`}UX|kr%fjfN4(k zXe#isaN9GIbEsyP;uhM5&Xszo+u1tTLvk7l6=h3kGxGqi9J8&JlE(46rlBfaI}p(~ z+dHkmR@8^NMTqO&p|wz%J`Uz1(un-`ZUt6euvZ}!gsT?-*sMa2Ru|TGyUEP0wi8~l zPfN*9f5wba<&iZPi6M2~+MU>sdQ)a#J3lo3WWXOQD+?hF3&bYK0fQ^v{viZ)&Z&4d za|E0J=xcwLyz&Liz{o;;eo&ilwWxZ-{t3P7*w)%zHsv)-2BOEWW{2hvR+4`pp2{g! z!7)f@1wqs;DkXx^I4X3vF{s(GJs`%#%B~nAOBAxhu9-t1<7EVPt70Km)oR8Mv*FC~ zQkZFZ3YN}ul&$4UXW$K))F|pOwYanlAJ%7HIIy&u0f5nZRheHp4`qHScU<}yT3 z5ss;;dAwCqJy6XaIv3mChS`b;xyTRAqwwNtBrfkG40Lv^fB#n+lX#)FKeO6iI-{35 zbi4WTR&u$Z!x*OCg1#~QKp6ZUr=E5wva z-N8~?Q=~Ao=E%$9+0MUgJe9;*Qdbt2ZVCC}h0>%}d}=#Td%9?3EynDOr+AyjEKA*9 z1&x;Xws2ZztY&>5o2!x=gOUy0rmf%+Xvs#Er-vq6!R)(TO74QYy3Z|fxMGHp>q0PJ z)2y9Gpn&;$N;f)l&swqACxLLy5_0{ac~14{KhXf4$|0`@OBy);2W8LV5}^Kq$NK=B zLg{-H+m(l`wX!a-YB-Q+a~J+++7@ai-_;Ybj5AAJ+k<6Jwkj<0Xex`To7lyf?e-6p z*3t^3khGSq__AzS@n z+S!2s|ZV`)9(ztjSR~n4~(~5eaw%$AJ%5^TYo|*% z+$G-TuIxe2w3A=D5^e;N%{}h2Xe?0^85U+-b$=`?%sOK$Oli~4&{%|?mB*M*TOaP> zXqjV_l8$UDS#yH3O!-PQiAYD|jT5l(*T5nasl;~u(38#!1Xv+uc63;(ihvFeMOkLkw(O|;C%a^FM3!OUe38I^>fs8;Bon$)Cj>PD+ukM zt1`>?9L>6E4dMPe@aC}}wCx=zl*HES&8uydDAyjf%WW3*}0FTX08JNEN?fX`D}M)*HGQC+E6*`BOJlxX{KRTqlO zivGch!HQ9JNbaqo?!$u>qf+jiigM{_S)`yA&aFAwLwzw~ba9x3mh1`xe$J(xUNIq< zH9eS97tEO?dxoV+s7gCp_-X*|X~%0Hl<(o9BpKXDsk|-1;aZ;9VAsmqYPD(>W}tai zewC`viZZTkqHW_Mt_-9#o~&Rju`BSpAU`o0npgzfBULyMqqVO3!s*jap5wUvmKj}o>>N;&&Z@b zvr@Kq&Q4f0XXT!0;yP_G=QBX^*(|dLk4zI((Qeo%|%_QUxZ*sk6TaV1VN!G6zmix{>uh`agiag~+5=^C~NH07=l zQ0yxQ<4hjw=NY8<((=IO-YT zy#d%EpcU-7cy|&vgwNYa=T$ffiVU2GxBJY{3jZpcgLnNXXcCRm{NDIInr%-d3=)TN z^GD(%A!~T`4w?sTq*foKwQLsJd_A=dpN9GdMqLM)jqoyf>~L?w?p%Iw{fWj!m2U9B zD>?BhP!ddKM5ZZ4VJ^wd7?mG~ zTRW{%;bsf_2U3+^ue~>w(9Eih;)L?t zW)SN?KjlBgx@A9B+~SMOhU!;s4_$C0DL#_ag$KP_cXjtFn}SulY(kX5R_ax|H0-Km zSJ65u!3@le{G(2n2j!OoS4Wevv#8Z}0hntYwE3*k+FdFps=NxjBcL<8W|f>_<8$SV zyz)g((|FXL0qtIL9QWBcLZ>hYG*6YgqvEZhHaal5!{~|LfVcI+zS4S8>1qHcITv_!~EguNrbl{lG#>2=iz_FJbzR)W+`}nTSVtHADJ-H{d@EFV&MWWVX>cE ztde-n*gJ5%rcaqNIy9-pudkYbvwdRYJ4?C=8Jjz?~7n{Qd_-tY(0_ z&O%3)4ScFr2DQU%BXMAc+wv7Dbq7Q=_}~$BWo4f~RF3F0*A7E5*WH)wTG}%|NY@GA z5{oa}`$rheAH^~diB<-1{wQZU=Bv4jD49ue+Z-cIuDrv=>b-?`psMsJ&q`@naJ5S} z1g)~%Jc6(W-MzR0S$+r@>Cy-NGcd6LS9}f4C;0(8bl-$J@57}xjzFmh}%n3Go zZ|u5$;ghYzqkc*hz#{Z%)?CbY2xR@pdRTy!nT3EH|=u*{M%ePlUv{yy6}_svZ;WhSM+8%3C_aj;?EhnAWl^oABBLp^F1G8+H!N+1CLqUv z|54i1G9K7|XdbevKL-H{SW`L(PAP-QszOy)yPvwSl@(T|;24fs4`kq_u2K~S?l@8* z16Dtso8SIlm=&*j2?q@@m^ljpsjwZ?o#x>r8$(dPSSLRGg-TAG=yQ12QvG!J_tJ7D10?BVIKyY=;8$uw-6uA$o zetOhydD9h^#{(*f+LEeaF58e-_14BJ%hZTFM>z0P)9iE0X4Mw0iML>Kb4y5^x|J>5 zY}TlIn!=eMYVsT;D{teoXKL0#K+Y)Y_+iyBPK?<)$C6{48?dL8sjRQrXhr=mX|`hp z+f-@F(H2viX6wlDGd?ZSY?B*x-#@ugw`g)bo=UEToD7VLC#f)&sTvv9uom*s-Kx~P za}5%ol2}h)*}VmKHt2x#AJ4l$szzhf#gishNwN@(3^%kcaa|71KJb)2Lhr@CZ`J5j zF<~LS$##^;)}$Va)u2YARbx1f$19823Bx=CKH*?W$DG6vakU8@?fxK2e6-QGY7E{= zgDwoprkxUO7^R+g)mXbl-G))UE5JuxOEjm|yVNNKOW986z7OFQs7_K4EpJdVYZ-Ol zsm=LumS5A5mPDWmx36?CUZBLG4npgOr1K#%xUps2vLhO1y0u7=5d6_?x^f$6P!(|v zr+&C5<-)fbwv~3m0g|X=WDAIObjcIWd7Xw?P)@@v>p$#3A_*=f;_J%oOI4GQeO!XD zDzI9bGBGIM0bUkEgj2r5meolik5vlnl|lrQt<$!H6`}}=OOb3WS~!I6C9#vaLvDXZ z6*3t;7RgW}TAQ-4V#gG`wMhR zx2g_{ws=XBYzdsTY*AGgH0-hv^M-2M)tzIOsydt8hOM|6m{X;+KoEhW7-8TJhb#?S z)tnEv&3OafNyNI1koQEw0Kc8W8kw5H>aRSgJ9y{99gyO56&nwInI)ROS(D*r5V(2Z zbyyKNK=?Hok_3Y^b}VX^XrqM+wwe_7UYLLuL$t7EVnmfQsJdvEOdu+0Tmo9LmE9-q5Z^Q{*VCI zRVs6=p;~WcK=%wECqvbG%fYQu>&9StS;SZnS}UpMExSG#oq7Ni1{<=VT7;1FQ$5ZG zdL&ID9`3y+Ew9D^lK^dS49f1YX-)Zqnzl%r^`MKZYV!G-dzL9KMg=w9RcmW{(?6So zn%)38t$ z=mr{NpHnvrJMNm%@KQ6KKeLWx#hiMEJj@8Hhr$o8Lmo{ZVc=8GAmI1OE|0mfihCl1p)Eq&~<1?P!POp8=Z7=$6+|7g8Oy3 zc#H^xHMW`byq-WLzt9hzJQ>WF=7*#I{su#IH;fADEb21+o>uylJ!4|LT zjR8K^oeN&o8?|4HmxLG3Rtc&Z9yGSHISltk92P|}sTnEnS~a1e(UZF1vIR9GgKBFC zg6bvs&~7mB#r#=f@lIoTVLJNcSaNZwOPd6GF+X8LIVN0d%o(m#u4c+Wfi;yHW zQ?B`^no$TOOcm3sB-`vxrAW)XC^KC2#UHHCv`n=GkvkmJok*pH2IXXxR~sIa=h`p@ z<``KL3|!I8glsci5z&`LAKBdfTA2y+))W(rlF$z#AtzQ4lUm(?s>ZYB1>+ zRNJ;cs4m6_*%p?W$;`K6br78pQmmbq(mJM_4715lM-a-c-oF%}l(8#K8wJ;Sw&+z? z2i4VxOhHAnYMt$`nHW^Vdf*j;_Ha2@Im#AemBZJEYVMA#W*@3-u`SmVu@GYe z4_8HOm6!<6sm`^QOT0sg>r{n4Y~(1v^)x!QV{CXJtKDWKCn~p8RX4TcxV2_-P`!zz zy?S#>xJ`85+(C(kTDRiN#5=csZiP&A)HaJTNr}{Cmi#11fH7&;0gn7}yGIDbk zdTFo^;r*D{@&zv=)Fnp@O>Pp@jX;`8)u*DvjMPX)dTyGbBg>eKZK{@-n00lwA6T_* zaD(bS9(2{NvJ8{+0A+4nU3A?$-PMWB==N@BTSnY0Vc1}AY{RBGb*y@0N1AAyI%1L* zTgA}S@k`O$1+~TKVWC#>wq^d;;qj)ec+x(>plJ`hqsTOAuSkig5E&Ezr9LIKdtu~6R?Pt;2#Tbc;ZD6?8z&nNQK5gN{=w}#D`WJtnYb!bm3~NG-T{1ZuF3vDJPfzEkJXJKyZ$)z$uRGb`4>d@_r)^#KAbaJ(($)$F%Lz+D&r1t! zjNwpUo|iURFx;wcK`XeQc2PN5x>uaMWB?%wT4qh4gES?w4DV#=Z%1dUJ^IO;HI#)R zvMpN!S=GzsZEgKJmblE@TF)^skyOJ~ssC)pD&-inS>tz~&Z~ z2UlG<-;RzYUOTHoqJ0)%!3U|bSlBV@%(<&zRg3K!Eje(rb5`J%_oZdN!WCjWsaB)8 zkE|e~jG+};?J^aETS*YE1gajdt`^92rk0g7(7+od@nM3)R%(k( zP@@HS(c$Czdf#IFui50TG@T88%#Ni`R7$M!yII)!E!^K43R1sCee{Msm}syigG=7+ zf`)z2x#V!FmuPERwH(f?BCeT~cYoKs`#UpvDr0M=hasVwubF`dJO0DqkQ1sGa4yCT zPdJw1n@-fS+gFCz-axgD+>Jln9~*3F7&$6Z#c$l>0#I+ks(&XOVQbRb;5QAoo~E_Q z!24#>I>pjDrKRCAkb-zdLEeIq=>;&41#HM@m(mM58QkEJhbvA?3epS6gCFdY1Ar@> zF>x?EYxVEP{QU6#+sm_-oqpv#d2c39&N}Y7fj@t0z=bCoV}7@2_MYnxzx3Q`Cmvoo z^6l8;fJ~Z+n_v9r+0(O+#KRA<6HI$!Vj#atGiLsw#?+o+%!fA{^TudnHfI`BbgVHo z_*>B5m|XCYHxJ)`A8$+#{2hny<_u%5JldE)^fKm=EMpSDzHDc3=QaG{OFwP#Jp$i% z5!NN(?~S-F#j`QrugVk${Y~uyp>mR-E@{d!*WjBl#rU;Y>xG$)TJHHC@_cok?{SYk z$xy%BHQtQwuz`+EeR5*-&2c)HYBjd_xV58)S8rYo7MhF1UV$Z^8@=lIAB- zx$AOFJA89ZZ~R)?Emv-l>>oA})+tQmT-SjKY}OPWJLG2Wy(%wY*r=&*$Os50r|I0l&2 z^9kPdWKUh-sZpM~+*21pdaJ){J@(H;(xvw|z-&710CgX}2{YgGEdXWXead62J@q$G zHsmdy`lqKp_k0&`4JFv?u`|5ZUT6;Wm)#uZ!qK2?$R~iZ@(uCWWuEUUPn|U&3}u8* z+j#Ff$y4`&nvZY7)Z>>qlQ6G2D%ZRV>QlM?+hJAau;+7BYulTnK=sDAqZy3f^YBfW zFS}(qt&PI-)xvJZuPse?d#c=1^FS@YH`kmyH)-lYRhc*N%YTo=!q^gKJ1`r{N1&_@ z_Bbrp{0PdH;`BdTzABRgivJSk2vFVe{f(DCVa|1YNizZzEknW-fwCcw14X^$nvV`k zn%nV|YqmKmVHSD5cD~N6ywR4i3|g;hQjX9hPerg0eM!B`6!-4?VNY3*y_1UmM=Lplk`- zS()3MogpgM3<7p8zPaXdMu9!sVx8$|K66yU?DekkmqOPzo+|KEe@~s^sq-B5T%^tv0i%2!%^1&j zt)miVA}A}%k>6%JSxP+LOwac)C>zQ$@7n084UWn+ogU0kxz9EEu(7NGx#mr{TIxej zeX%^#)sI!q>UA{V`A`xYY)#HJZ5-9nbOB{`)z4A6<}^?j;oH$vpsPdITyrCyZGPMX z%H~HWS3{37Gtf#-2c9sW_6)7#?XKlam|uAd{5wzm!Ba1JYMZCN^i-w~Wv36h8Zoie zO_=OsZR^tBEC$9JkTAu;{P z8kDVhH+k2Ij>A-(gQ?BQPt$3YYS3 zW(TlO@$F{J%ORERslZbOo;t=+33CD{8{S!t>S#uSVy-64P2TlhP*&1PMP$~v>Zx&_n&_!=Pi_3hm?}Ia%`+Z*36#a&_S7fd zHEm<)+RjrwJaw9gp&^HXZ#Pl`v<4VjMlqMWAe%{1qrG%dL(|no>~K?`%&!;;EI6N|@(CS(#r2 zWkcEOsgJ$u*A7D!do_%=Jt)?mp5{1@o$aYHp1RdjfAG{Jj>$qC`2iHun`_#>7W(K3%C=>LJawL@ei~!jw_J0T!xH8O`(qhP&VWfKn=qeqXhigw&xCqt@bTMu9*&uDNmRMpy)T(Jms)l zQ}3x4LA{4>uKCGh?OqRc&5$y zsXIM&ucziYDrp`8MSGPnPeR)qVW9j0V=LLwtOI3Ry1#nrbx-Z^)DNCYZni$!o1;Oo zb!l&o^VG?n8se#QJ#~eru6I;N^BYjs?*1N>Z9}JlvVHkk9$Vn3zUEm_8}L2ee1P9Q zqH^D~RA0l9CSCiQ4!}s|n$DmY^6_SX<2&A*my+x@y;g@^^%sbD=%pg1^%rEhK zov3pB+ID&_C@afyPrVGv>gq#KR+iY?A$ABT#*r{b)?zjURMG?vOPao(I^XkM>#2#J zn&PP$p8Augp7qorpCw(2mSQAg<9z|HR=zhJ^`tp@k{utNY_rCEbYy`pzOHuK~OAfNwWZy z9fLgyigBD~nn1mV?`h^6Pi6kYVjWEfP+jruXioCf8K5o|cC)8S9hES1K-qFt?WxB> zS?O1Tva=+A17)Ru2b8sIlfFng>~rt>lc%!Z34IiJs;8$;_0$MQooR|d-GJ|z<_<@l zW2S?u#rGVu0>97Ud!`wLxq_{tcH`GliFYl9GDHgfI`p_f(T<~*I4o(-^wiHiHQrIV z=C`0Y!adXU8yJ?2dx23y=bI`}Pvd*OdD&ARc*<-IT?;(b&r@f4>KC56*;Ds;s?t&I z&AvY-UB7rGFqM`GtD1C*<4uSs9duKl$CTnC`zAe z-Uns-<9oep`}a`p@lBW!__e9L2Gm4hGw^Gz%0Fj>wRVZ;YXHUiJHR|LJt^BXz^n&G zD>cCU1C+HY=KavpWP-BR>@ZN4ud|~PrVpr6gok-TP*#@HL9ut8G#7%RR0GV_pse3X zpcuyh^RT0GO_QhI^wj5``U#Ykpv`t0O0MY+%G%=NJ$0t1E^!oQ8$mI)TyvLqo$aw& zk3H+D^`3g)Q$KhrXGf@)!yT101)wNP!VGX&t~uXfxn{h_Zg*(x|y3l+MzO)Z=%!MYvZ#Pk=dFp&n)^)U}Zt&Dy zo_f$z3qAFery4-nkPn-mbm49Et~)%n$MGdh+ke`Y?i_O@DB7V*%!#1R#rG0(&uK|V z-R!V)%w3M}95WjfZP+E|F;FYg24(fPu6vfF`U11Icn~Po z^IUTlC|je7oa=?=I#5}n#S(Uoc?T5rg8l|5JBHl@%J%<$0>w}+ zG+F-&)6vCIM`wq#p}FP^V6=ZnB!`2tv0V+yrfo7PhH^~A^#SktC@8DHW{>^dQ(t>3 z@1u~fD<~WC8KA7z#(L@wP<9rv3RJ0V$x`q7GAJ9{2cRfbC-Va+&Z^~_zQQP&STQPp$IQE1r4}l$CiOD8`#GxgUpE4^T`;t{DP~ z<*Jh@0%hm=eh13>_=BT5ngyWPY9!1GP*#?gKyAktt6`tmP|i1pfjSo7^UVd0N|+l! z(MKn9FDM(wTu_YTc=Hq}+gEEGK}ndKnGM zj`*(g)V-h>TVQ5`;%ZA^o^n*eJm*{!hHEi4f8PLRHMG;Y=9;e@)zRFt&(0Gj%s%h; z(9c3km=DU%LL3ds$}-qfKXX*B`4uQzr+*8I{VUAjfwH5H8c?>U`k13IJ_2Q1jdh;d z0E%^CfY}C$ZAC}(B`6!pe?ZZKoMR6AKU?#jG+jX1Ix`TI&FSHw>}+k3qjJr)pcwM` zW)dj&f^to%!_G97pllCdk)v|Wv!IyMC!4>5Voy5PZ1L2`pse)&@l^16h;?<;Ip&6M zGR;YNN|;gjrSt>Lc+dBHP?RcR9stGIPDw5TWmEJFD4U}7pjaX>_X>)7KHq!-%9hTw zFD#WX1)#{serkxztcOb; zkVAz$1Rje7rk5!O&q5$8@Qq_6aH7cq@|;78fD9H7`-7I}aC1JQYHc=w=M(ul+>8*i z&m%{ekp`Q`OuHA7rVqYxtU3Qov7hXafk1v?N)XOPo~N6cEMLF$2;>uTX9Sr7y|gyv z9_ek$gqXoLu7McWJ|KCjz_SM5xH;B5BxIvU`k7fm-t)-uSmk26($!N zG0HQ+A#rdW8e0Ia84fY#D$`Cpl|JOF%n?E!b;yn8YSUTBO3!nHIYmgbM}A|@jl~LE zn~e^MoBPZyG3MYlk4!iB$k%rc8EB@PDMAu2T8)i0kD9*;Y5377T{oJi&HF+E&r@%b zX-wDA9%(SSX<#zPI;1GJ-t-aA8J_1Qhm7>dt7fQp#(3myGeXD>9{H#FO&Ux1B!|SI z_ep6?=>ral#Xd22$k#%Ll;wV6?oMOcpYy&xHO1oLsvz^LF#A(eD&!5%^SOCa$Y&ne zZI&snw7*#WtT*47<}~JDTZb6)t@$%{d9*eK4w)BxYu=a4CpaXIlJTMB8Satq%`V0D zOOO0uK9;ZRJYr&BORM)enTudZ_KD|K&yxX!v6n`WpTtw`k$CJsifeHMG3m^~Mvt_L z#nYLCf4n*-{S?LWVmawd=|;z6P*QW`%fKSpTG=jkXgc-0)rWIJ z>yaa4ld_qonJh+Jih{R3%+B` zv9ZZQrhDZ0SZN-ru2~3#|Kg}i^YRw7Hs*OCZwejk5VIkLREno<1eq_S#{tN|2vVCz z9bN$BI(&^89IMTvo~JmZC^k6uuz3FHc}@pH9X{@mxH&!cw2*p-yqI=IY?Y9|JLJaL zIk83|yS=Y-W6$SN&tH4wyx14wN&MBy6F29@z7}${M=p&0N61ize3LdZmJyUdo~s

    `-OsUQ!KBOEd#Hb3^d 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

    - + - +
    - + - @foreach (Folder folder in _folders) { diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index 167c0b3c..241e5082 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -8,10 +8,10 @@ @@ -127,7 +127,7 @@ } folder.SiteId = PageState.Site.SiteId; - + if (_parentId == -1) { folder.ParentId = null; @@ -136,7 +136,7 @@ { folder.ParentId = _parentId; } - + folder.Name = _name; folder.IsSystem = _isSystem; folder.Permissions = _permissionGrid.GetPermissions(); @@ -149,7 +149,7 @@ { folder = await FolderService.AddFolderAsync(folder); } - + await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); await logger.LogInformation("Folder Saved {Folder}", folder); NavigationManager.NavigateTo(NavigateUrl()); diff --git a/Oqtane.Client/Modules/Admin/Jobs/Add.razor b/Oqtane.Client/Modules/Admin/Jobs/Add.razor index 4775f8a0..65361e41 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Add.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Add.razor @@ -3,73 +3,73 @@ @inject NavigationManager NavigationManager @inject IJobService JobService -
    - + - @if (PageState.QueryString.ContainsKey("id")) { @@ -25,15 +25,15 @@
    - + - +
    - +
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - - -
    - - - -
    - - - -
    - - - - -
    - - - -
    - - - -
    - - - -
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + +
    + + + +
    + + + + +
    + + + +
    + + + +
    + + + +
    Cancel @@ -95,7 +95,7 @@ job.IsEnabled = Boolean.Parse(_isEnabled); job.Frequency = _frequency; job.Interval = int.Parse(_interval); - + if (_startDate == string.Empty) { job.StartDate = null; @@ -104,7 +104,7 @@ { job.StartDate = DateTime.Parse(_startDate); } - + if (_endDate == string.Empty) { job.EndDate = null; @@ -113,7 +113,7 @@ { job.EndDate = DateTime.Parse(_endDate); } - + job.RetentionHistory = int.Parse(_retentionHistory); job.IsStarted = false; job.IsExecuting = false; diff --git a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor index 5339a226..ae1f3d9b 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor @@ -3,73 +3,73 @@ @inject NavigationManager NavigationManager @inject IJobService JobService - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - - -
    - - - -
    - - - -
    - - - - -
    - - - -
    - - - -
    - - - -
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + +
    + + + +
    + + + + +
    + + + +
    + + + +
    + + + +
    Cancel @@ -121,7 +121,7 @@ job.IsEnabled = Boolean.Parse(_isEnabled); job.Frequency = _frequency; job.Interval = int.Parse(_interval); - + if (_startDate == string.Empty) { job.StartDate = null; @@ -130,7 +130,7 @@ { job.StartDate = DateTime.Parse(_startDate); } - + if (_endDate == string.Empty) { job.EndDate = null; @@ -139,7 +139,7 @@ { job.EndDate = DateTime.Parse(_endDate); } - + job.RetentionHistory = int.Parse(_retentionHistory); try diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 483cc107..2b495fb9 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -9,34 +9,34 @@ @@ -241,7 +241,7 @@ { _panelayouts = new Dictionary(); } - + StateHasChanged(); } catch (Exception ex) @@ -266,12 +266,12 @@ { _path = _name; } - + if (_path.Contains("/")) { _path = _path.Substring(_path.LastIndexOf("/") + 1); } - + if (string.IsNullOrEmpty(_parentid)) { page.ParentId = null; @@ -290,7 +290,7 @@ page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); } } - + Page child; switch (_insert) { @@ -309,7 +309,7 @@ page.Order = int.MaxValue; break; } - + page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation)); page.Url = _url; page.EditMode = (_mode == "edit" ? true : false); @@ -322,12 +322,12 @@ { page.ThemeType = string.Empty; } - + if (page.LayoutType == PageState.Site.DefaultLayoutType) { page.LayoutType = string.Empty; } - + page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable)); page.UserId = null; diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 2b13870e..6ac773a0 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -9,34 +9,34 @@
    - + - +
    - + - +
    - + - +
    - + - @foreach (Page page in _pageList) { @@ -47,10 +47,10 @@
    - + - @if (_children != null && _children.Count > 0) { @@ -61,7 +61,7 @@ @if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">")) { - @foreach (Page page in _children) { @@ -73,10 +73,10 @@
    - + - @@ -84,18 +84,18 @@
    - + - +
    - + - @@ -103,10 +103,10 @@
    - + - @@ -114,10 +114,10 @@
    - + - @foreach (KeyValuePair item in _themes) { @@ -128,10 +128,10 @@
    - + - @foreach (KeyValuePair panelayout in _panelayouts) { @@ -142,15 +142,15 @@
    - + - +
    - +
    From 3fc7e78e56ca90586b01b60fd0b89feebab06082 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Tue, 7 Apr 2020 11:21:33 +0200 Subject: [PATCH 123/265] nuget module installation fix --- Oqtane.Server/Infrastructure/DatabaseManager.cs | 9 ++++++--- Oqtane.Server/Infrastructure/InstallationManager.cs | 2 +- Oqtane.Server/Program.cs | 7 +++++-- Oqtane.Server/Startup.cs | 5 +---- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index a8164ac1..d058d2fe 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -74,7 +74,6 @@ namespace Oqtane.Infrastructure } } - public bool IsInstalled { get @@ -212,6 +211,8 @@ namespace Oqtane.Infrastructure private static void ModuleMigration(Assembly assembly, string connectionString) { + + Console.WriteLine($"Migrating assembly {assembly.FullName}"); var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) .WithScriptsEmbeddedInAssembly(assembly); // scripts must be included as Embedded Resources var dbUpgrade = dbUpgradeConfig.Build(); @@ -238,17 +239,19 @@ namespace Oqtane.Infrastructure } } } - + private static void TenantMigration(string connectionString, string dataDirectory) { + Console.WriteLine("Tenant migration"); var assemblies = AppDomain.CurrentDomain.GetAssemblies() - .Where(item => item.FullName != null && item.FullName.Contains(".Module.")).ToArray(); + .Where(item => item.FullName != null && item.FullName.ToLower().Contains(".module.")).ToArray(); // get tenants using (var db = new InstallationContext(connectionString)) { foreach (var tenant in db.Tenant.ToList()) { + Console.WriteLine($"Migrating tenant {tenant.Name}"); connectionString = NormalizeConnectionString(tenant.DBConnectionString, dataDirectory); // upgrade framework var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index 82eab80f..e76016c3 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -107,7 +107,7 @@ namespace Oqtane.Infrastructure } } - if (install && restart) + if (install) { if (restart) { diff --git a/Oqtane.Server/Program.cs b/Oqtane.Server/Program.cs index e46805e6..d36b9f9b 100644 --- a/Oqtane.Server/Program.cs +++ b/Oqtane.Server/Program.cs @@ -16,8 +16,11 @@ namespace Oqtane.Server var host = BuildWebHost(args); using (var serviceScope = host.Services.GetRequiredService().CreateScope()) { - var manager = serviceScope.ServiceProvider.GetService(); - manager.StartupMigration(); + var installationManager = serviceScope.ServiceProvider.GetService(); + // install any modules or themes stored in nugget, then restart app to ensure all is loaded in order + installationManager.InstallPackages("Modules,Themes", true); + var databaseManager = serviceScope.ServiceProvider.GetService(); + databaseManager.StartupMigration(); } host.Run(); } diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 714834e1..11ffb877 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -201,7 +201,7 @@ namespace Oqtane } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IInstallationManager installationManager) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IInstallationManager installationManager, DatabaseManager databaseManager) { if (env.IsDevelopment()) { @@ -214,9 +214,6 @@ namespace Oqtane app.UseHsts(); } - // install any modules or themes - installationManager.InstallPackages("Modules,Themes", false); - app.UseHttpsRedirection(); app.UseStaticFiles(); From 6d0043669c3fa8eae4e049828b261f3cfb864264 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Tue, 7 Apr 2020 16:44:38 +0200 Subject: [PATCH 124/265] nuget module installation fix II --- Oqtane.Server/Startup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 11ffb877..a2a798f7 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -201,7 +201,7 @@ namespace Oqtane } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IInstallationManager installationManager, DatabaseManager databaseManager) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { From c29195b4177a239eca355189c4a885c5c5d7f228 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 7 Apr 2020 17:25:57 -0400 Subject: [PATCH 125/265] fixed issue with module settings tab and module creator templating --- .../Modules/[Module]/Index.razor | 2 +- .../{[Module]Info.cs => ModuleInfo.cs} | 0 .../Modules/Admin/Modules/Settings.razor | 6 ++++- Oqtane.Shared/Shared/Utilities.cs | 22 +++++-------------- 4 files changed, 12 insertions(+), 18 deletions(-) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/{[Module]Info.cs => ModuleInfo.cs} (100%) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Index.razor index a0f03fe2..02c457f0 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Index.razor @@ -30,7 +30,7 @@ C:\Users\Shaun.Walker\Source\Repos\sbwalker\oqtane.framework\Oqtane.Client\Modul - Index.razor - main component for your module
    - Edit.razor - component for adding or editing content
    - Settings.razor - component for managing module settings
    -- Module.cs - implements IModule interface to provide configuration settings for your module
    +- ModuleInfo.cs - implements IModule interface to provide configuration settings for your module
    - Services\I[Module]Service.cs - interface for defining service API methods
    - Services\[Module]Service.cs - implements service API interface methods

    C:\Users\Shaun.Walker\Source\Repos\sbwalker\oqtane.framework\Oqtane.Server\Modules\[Module]\
    diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module]Info.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/ModuleInfo.cs similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/[Module]Info.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/ModuleInfo.cs diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index a98d015e..aeb4406e 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -121,6 +121,10 @@ { var moduleobject = Activator.CreateInstance(_settingsModuleType); _settingstitle = (string)_settingsModuleType.GetProperty("Title").GetValue(moduleobject, null); + if (string.IsNullOrEmpty(_settingstitle)) + { + _settingstitle = "Other Settings"; + } DynamicComponent = builder => { @@ -141,7 +145,7 @@ pagemodule.PageId = int.Parse(_pageId); pagemodule.Title = _title; pagemodule.ContainerType = _containerType; - + await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index fe7817d0..3e466bb4 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -30,25 +30,15 @@ namespace Oqtane.Shared public static string EditUrl(string alias, string path, int moduleid, string action, string parameters) { - string url = NavigateUrl(alias, path, ""); - if (url == "/") url = ""; if (moduleid != -1) { - url += "/" + moduleid.ToString(); + path += $"/{moduleid}"; + if (!string.IsNullOrEmpty(action)) + { + path += $"/{action}"; + } } - if (moduleid != -1 && action != "") - { - url += "/" + action; - } - if (!string.IsNullOrEmpty(parameters)) - { - url += "?" + parameters; - } - if (!url.StartsWith("/")) - { - url = "/" + url; - } - return url; + return NavigateUrl(alias, path, parameters); } public static string ContentUrl(string alias, int fileid) From 1edc34dca06be5c22bf623e142585de217c46a64 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 8 Apr 2020 11:43:42 -0400 Subject: [PATCH 126/265] NavigateUrl fix to deal with scenario where alias has a value and path is "" --- .../Oqtane.Client/Modules/[Module]/Edit.razor | 5 ++--- .../Modules/[Module]/Settings.razor | 2 +- Oqtane.Shared/Shared/Utilities.cs | 4 +++- .../Oqtane.Shared.Tests/UtilitiesTests.cs | 6 +++--- nuget.exe | Bin 0 -> 5734488 bytes 5 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 nuget.exe diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Edit.razor index f0fed00e..d65cc5db 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Edit.razor @@ -26,9 +26,8 @@ } @code { - - public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Edit; } } - public override string Actions { get { return "Add,Edit"; } } + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; + public override string Actions => "Add,Edit"; int _id; string _name; diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Settings.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Settings.razor index b657eb6a..703ba2ee 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Settings.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Settings.razor @@ -14,7 +14,7 @@
    - + - +
    - + - +
    - + - +
    - + - @foreach (Page page in _pageList) { @@ -54,10 +54,10 @@
    - + - @if (_parentid == _currentparentid) { @@ -72,7 +72,7 @@ @if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">")) { - @foreach (Page page in _children) { @@ -84,18 +84,18 @@
    - + - +
    - + - @@ -103,10 +103,10 @@
    - + - @@ -114,10 +114,10 @@
    - + - @@ -125,10 +125,10 @@
    - + - @foreach (KeyValuePair item in _themes) { @@ -146,10 +146,10 @@
    - + - @foreach (KeyValuePair panelayout in _panelayouts) { @@ -160,15 +160,15 @@
    - + - +
    - +
    @code { - public override string Title { get { return "[Module] Settings"; } } + public override string Title => "[Module] Settings"; string _value; diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 3e466bb4..fe8e8844 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -20,7 +20,9 @@ namespace Oqtane.Shared var uriBuilder = new UriBuilder { Path = !string.IsNullOrEmpty(alias) - ? $"{alias}/{path}" + ? (!string.IsNullOrEmpty(path)) + ? $"{alias}/{path}" + : $"{alias}" : $"{path}", Query = parameters }; diff --git a/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs b/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs index c5de08e4..df6a941e 100644 --- a/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs +++ b/Oqtane.Test/Oqtane.Shared.Tests/UtilitiesTests.cs @@ -10,11 +10,11 @@ namespace Oqtane.Test.Oqtane.Shared.Tests [InlineData("contoso", "admin", "", "/contoso/admin")] [InlineData("contoso", "", "pageId=4", "/contoso?pageId=4")] [InlineData("contoso", "", "pageId=4&moduleId=10", "/contoso?pageId=4&moduleId=10")] - [InlineData("contoso", "", "", "/contoso/")] + [InlineData("contoso", "", "", "/contoso")] [InlineData("", "login", "returnUrl=/admin", "/login?returnUrl=/admin")] [InlineData("", "admin", "", "/admin")] - [InlineData("", "", "pageId=4", "?pageId=4")] - [InlineData("", "", "pageId=4&moduleId=10", "?pageId=4&moduleId=10")] + [InlineData("", "", "pageId=4", "/?pageId=4")] + [InlineData("", "", "pageId=4&moduleId=10", "/?pageId=4&moduleId=10")] [InlineData("", "", "", "/")] public void NavigateUrlTest(string alias, string path, string parameters, string expectedUrl) { diff --git a/nuget.exe b/nuget.exe new file mode 100644 index 0000000000000000000000000000000000000000..3ae0060a9ddfb3ad505a82c642a51c1186f6801b GIT binary patch literal 5734488 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 z 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

    ;PM^BR=U& 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�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%0DW`os znFxK>x|loki$FYty68mbs4BNFS@dEoPy@{!haG=HTDAqE3a@cVmy3@)eIf(J`TM2a zKSl-O<{~RLdtQHXc`S!Y9q&&+FRwVCmi?1Z-KsXSPQI}nNwZ}XO@8Jo5*>6eB^-s` zsk>q$(rj&54~A4J12tz?_vcGGS;m=Jr+=3$_WqgvKg-q4ez|?=XIB#T!Dr~9|9GW; zCnsAc8=iQ2^28GxCAF1VTPK^8-JaNQ{^%3_MCO+Ndg_0k`Jd3`IwK37Zn z8}$&%&3{Kt8&TCp)U^?nZA5Jws&3;1Qw_@k$<33ro$%x8Bxkzo5ongL|MB$XG?Ag`2IkI>NXm&)9}y0Z)@_YHH3rLERYL;u~0 zzn-kE#Xs(8Yw>OU`Tp|ieA2{=rzap64bmR{S^wjiQO|XCl*J)eBoJE^+QSYpE>a?t zM)WEm+pnS=nl;F z-Hq0UT*>qCz@i)RE&hLUuPiniPK{LpyF0&_>QZ$A{+G(;D_E7!eCTvevxClUG0Ww$ z7mh9=p*grAq&f>yyn$#;0)BEvtaC_fmgRCdEGG!V;LGPpQ&PbNq=H*szDzok3fjLa zv?rB$g{xi-9Q0%}i#JGaE^LudD39#X0=5-q7rs~+*#iIK1J1H@avh+LII>jGE?SyB zKZ6PWsTi%J3^HZZqr-gN%R|Yv4Wb>(KkE0bBwiC=C2CTCJM9GX2Kr#*m`+c(P9z(} z&_!C43OG_hqNkEY4)>yzzAU9L#d@mcnbiWXskiQm6TC@9k-euN=*F*5oh@GU+}xS5 zT$(MkASC&EI<1~1QIAeNq(=S@OnD8RVLZ=%N|lYZzrIZ6e3l|>y=G8ztEEc_Fb z_-UEM&m_vLE{Fy4I8(DP@RY)NGbW~VF`uHTh6Iu;yHw)6l8YenTF+$%k{pp*sx-rV zNXbnuC8y%^#loCLZPT>-9jvbxXxm@#CAdQKA*huOFSx0Uf|vPkVAnc7I$B)q&4Dr2 z6Yt0fY0tn9WL>T%b~<&$%e(*}#cNK)hd{tfIZ&Q3{=7E%6WWmJ69_V9v#Bd%nPT6&3jg z_8b?%4%DqpeF8IyXj?E^Og^n;ZMYYT!wKUjeLhs;LGKu=Tb1Vv2L&rmrB z(Jwq2#x{zaA{A0>b2CdxZ7dXrSF4rOSf$sALG^NR&-OaGu1fo6J29F7p(5Fb%fH=1 zW8K&+9U_(8c$VUFODh^45eqE&%Wt2r9v0WECr9wka`EFU(~Z;QGj3y_>WtZSQ=5hE zZ*Gd89mvyvU{Lyb(IP%tpu6jaeT3S)UjERp=siaEU}reMZYkJFsxFBmQ1DNyu8TW1 z7Z-NiY8#97@=kx5nsVm#`N=t3n2X{f3fbH)*AtO!LP+wc$elZC!Cz|&xpm{uvB+=F z2+A%Njp^GdJGWKtN{cTXW+*?*WK!EjSBb55d9)Vy(70zj{(1Awq8u1p`*~HUN;>Oj z^g^YM`nD2VL<>tkE&41eB$F+U(izL9!d#*qjW+YKW?tXifhL45c4NdWZd-fW%WB|7ScR;T(3vYyF3W;av*&T=QAA1wpvWw7S*BGqIp8 z2XE{{wPQbb93sv&WZHS8NFJc;Nx9cIE|H z!fSROz^EpT>um9DiOHGp=fg}fa~dMkSl@nNzP`fRg_X@`=&)HTthZHU;Fj{%_n*~z zPM~Pz?egpj!*ax5w~K&0bT4mG4#zmnXFsBy;}KXf49YO==(TpYind59waOxK*&KyD zel9@><%(;MoWUzG^UWx_fS7X-XE-F>4N*=e-_L)TO#c7b#_US&vulv-wfP-F=F}hT z)ivmyqZ)2g0vX&OH);FJ!@})sSg`$6-#AbXxOSS5nEjPCHtD?3tkf+|>^0ET2nIK$ z1k6ulNWwCO+{e^P-2R}n(-u00;CNbAFj5QHnpP-i$ z3G;<(Lkl8<(J;R)VVa8;+koECmWwF%7Y^g88wb!D5P_-%4;_-QaTxW4hFdYa3* z%p$Y~o8d)?-zxQrjkz=985w{SW{d6^W1`) zQk60;D&8nfkb1I^OoG`XN>zh#e{5~7-B(jaNM9~t-V(&;%xtAmiE#(#+Hiz!D)YbT z1B)oOP+<;J=Wyfc>@O`8e-(T8;?$47u5~XHO;d*pDBg(ni%TvB=y6h?bw~n#K09^} zsajs+8El@u<-E-9?wPye$Lf50;fk`Y*X33e5+a@Q9kW}7nxv9S!}h&U_-WBwpB8$N ziZ7lOOAM(YFKA8P)?#g*sd4t_KYX`sn+W=vAP{T^4_P zZ)DQa97y_fdyZT_j6=wnpLTS~*+)C|=#Ul(EbP+G5A>y zG6LpQ{W6k-331r-`^Z!*i&+%EK&yYaKC7;&n~kHYb#rlZef2{jO;3}P)gql2MpHa3MNbHTYis*XH;tPx2S9BiR9-3Ch4$Xb;Lk%2k zH8SvHN*d?WlS@5bmdMF5gQsgbQh^C%XAmi(K3F?}BRe&?yR<8MiXD5ZeA(awcQ2VB zoL|)t3vWXocHUH z1GU9=vC=!G-mfC@$}E)G^!^&X!6K)`M$GzRLLe4RKNvzMs5u;SbBdeVzO~?>Szby) zk(u{B5lDNQ#)hXNt`~{l4LIh0G2lK=6GhPlixTr9>v@s&yhvt$VN9S37e$7-j$1;Z z%pZvJGOSxx8X^B$@l4|e2{2Nk#o%+M(?7cB?ZIVvKdt$?_*%iB+PS%f0=%S&NSXnv znp|Cje*n|Y&0R?(_aw;(gR@dOSf?L$J5@ge(`66&DjAkTlxG9BB5slgb>-D|f^cIq zl+o)u$Hi&zai(xS{NwcMFBD(dIAVwqtb#2+#s?P*##ZT}vA77Gb_CCSAv@qlGsb=} zT|&fgIj>MN2D-ktYdiXdRPD~3uwTX;5~P?gq<*9fXNNR)QW!*S#W3{YQT~D;x4sw+5-QMYV)R8ai=@f%4TzU1-!&IA zQ=61Iw+NQL0LHM;`a)um(t62INrlYVD$adP@>1@UQZ$f|Gi=M_TT;E$z|(7!i+1S4Z~7j%vfC@l)^t_X%uB`3 z@o<3bH80ID3G+=q5HR-2FIszTM(EI5hCdG-sA3&ovtv8^tKxDUWrukYLj`tGKiCm8 zqZ+=?{^+Q?^u1&<`zI1r!qSKp$N`@qVFqW>6Q{gKk^4qE7OHPxl@ zd-Y=>sm}3z1<8``*Iwh$fT`;{U1@)V;)0#NQfgcjv>2soL$Zrmx@06#qhKkpPrjAXz0Z7TRA1 zPon%xBMI5ETlDa!MWZ2QxR+w2dJJ_Is}dI1eQ0ldaYwm&g|&R2k=*>cTiB(}8t}31??<_?qC0$KfINA^K=aUy zS>>beSKPD%#U$ef=d$Q=1mhhqy0fbuoFmAaDd&ZjrofZIGk};ezq>9II0>Z{XAUr` zA(F~r7s1Yvh(wT9!e^=9=$IrVyOOaiC^>v?UOfJlIr2m@G$g^sxe&?CA-NS@A(z{s z*>3vSkee-(J@RJRBa_OnU*?)d-cwfZGpJFCrSij^>x)#C@xjjD7T@5mOW((_v5i1* z+{}fnm+Ns@%s^eDFt}waI=_``X7qb2x#@{c3pxFd@POF|?T<3eo`!pZW&=gUghGsf z7!*mQ0THy!+mL&3XtN*x;_BeYI*JcXlZQ-4DBiPirt>g#`GtIZA0=xqn&d#m=U`wQ z*U%xGT;@b}&RVH{r?%~1mR~IK{t8wdsK9oN(_O&XSDc;d-Bg?rF3;Z_o84z&o-U@4 zIcsavvr3B{%d`8%`H6w8qL(OdF=$U-pg_c9f!6=(cdQ$n;`#`x-vnu+>dR9vD8Fta zqJ6+mewh(NJ7HN+%++v58b(ZF?E5${+PZ|7j~i+2q@o%g^NFe<&z20u7a4I!TMW-H zb7L!2wk>+b@x9p7F_L7PQ;VsCEj;Oej>==;9_~`m+N^c!MqYQtOoqk+N z7{7+2@8Yn)xT(mlOT*~3fQBZ-_A~TIrBXi-!fr)Ny$SIY{Z}ypW~tB@OxvPJKB$1f z>Lo>=)C-)vkcA7_Tdy`+iNCRkLcSHoykdQ3ZVwVe_^_^erKkqf+O+l*>w9(gU-k%$ z!rvG&_rb9zMvJP9emKru=xc*qP(aI7ucO64YnZMbCFA-8n4j`woHv5UOf1r|3&vq0 z7@oheS2|eRj=>myoK?n+R;Qa858i1r>=#XpMy-%1WRKzmIy#-%`Z+8}`t{uF)2p)y zl;7qe9^+{Sj!^YaGLT$q1w#wIE4(unfOXInJ|J|{W=Pv+#UfPN@?2Ix5E3)Yj-;Yt z{GQ5B+SJWoT~4^M)&H3vzU4iVOYU>8WG-8r5<$!4xg>|lnAi8$%0%Gt^x-yo0$#;y zdyB^jb`b=^DP(C=(#1vfGdhexo!wWoo&TMq(^}_rQe7mPvgMk?b_Ro2AfX402}) z4wtYjXC$PGd#7VAOnnBt_WLt8ZpzlVr0wB3q+H-Y{*4VK^yG?2cpACH<#az}@;VIi>?z``Tsr_$|fi;jd&{;qq_H#jr2@65d!-20Xcs2x(uX?$Zzz#fdAe2ktySwp-t@fKCl@;f>E`W#9 zMtrp#nzE0WqO@2tEl&#e7x&~gX1Q1p7~>-vwQMbpfI5RTM{4%YR4FVOrANUz_3vsH zVbf>th^-nCu0HU_23sq9$a35^xtJn#XDtuq>Eeez;gI#>hdZkXJ7|8x_QaIqFo2oE zd#0Z*Zc*Dk68B;o8#ar`8Bda}?{5{E1V!@?EY~+b)*rtrL~{J#ydS5N)sk7HWDa)n zQ$+=9r1LA)a(Jv?_RcH*J}bX`P&cKz-d_K$e`<+`6K++7|FN8-OPN2f^jMf~%inDK z`xKTwzPZ=E3UWW3o-O`7#qs&_dhcMJ$tsr~%I`?(>sA(5c~3iezh5F&qS8OK=Q%ja zpU1xs=|4Z;{Jr)3Nfznn0XY1${ZV6wB>car|F)j#|EKoflP&w>InNB{=`(|R`rJyM zd*1WNGjQSKseR%p15xtX6CwMh{kNh2VT+^pykY~gc}JC7u}+W|9q`G4Qpq*k|z$T&#AIC1X9=c|NpT46tZzOR4a_vWD2-# zZGP|Uvrn(S@xNIKr54#euOrkNx~eN+N{Y!MDFag^HESa&&X1(*Ly^=Rh@_6=BYBqR zAQeXi!}g94W8F)LHxE1P;cmXgZyd>_3`__5I+x4*jCua%!Gj}=^o8k%zB{c!)LZcr z?&+ErGWGccXtI5M9$(^NH}dt3$8%&I*5PE?QwKXtvy*SZc@zl{w8xCouNSBz*OwQX z={i)15^%v5AQkJ(RIHo`2Y`6^AriZ(@}uw7M;bInEc3#3@qpHC2YTn*c-uUDWAoxp zcj?iI{2CjuHYGhwIsO>;X~Ew3LKQB5*3HH1e<^%YOSv+srletcfql(07iQ(|-D?HE zuv${@%e9FzybP;($YMc;w+Q~UVBDoa9RU%F9NYQ4eHzG-6Mxtu9}=*HX- zkUGyGna>i$?u2B%Nj%S)o4&}=5x^glKxG9=H(=9Vt7kl*sXUGx_59!0` zLeqC<$9f)OVxKPVLFI^N=v^NAY*(*D1TP|FPK}W z9U@Mtdv$a3*L~WLY_CN1S*g#sIwQDS?7xor=bbTdrBXn9wpRYMD~@ zCaY|Iv8g307c$AfSj9K1m3C92FY{lS^u)Wa3|8x-Bx;?9UE6}#ST9%vtXXt{Fcs!p zXAZ~ahNc^w52bWwLeQa%A)hbH1F~`x8p`N+^z3jM=NqQg4-km*XjffCn%BJ$Pb(YA zK51UL2>D6F%0>I>HmNf9YKcdrlp>TvO_MoY!5|}QXN@?@eVB_UanEB=Ov*6OH;n|V z?3$N|abfHaO~Qa0rZ#*3d&8Lq0DfcvP*SVi`s9So@L!5Teyx=L)A5=}IBnh6N4pT;wrM+(T_PJtpGtoy@wwnE=paL@!Hz7Gu7JZ2l@L6e;BQkyO|kA>5)){n!u1x_ zHL`{vAH^@m2@4%%>X-fw;4h7L46U*T!3h}A!Lk3CHowII5I3Jg6<>#XAY-Q=*)ow( zqX?mT{f5A?*K&kcrx0aVk$jmOADBqN&%IBXuEg1fT?LSOcSqE?ez`-l(9zT&#^kmd^XTxN}CH zxK-2Tv%KaXII^>6GF(GB424}4P`#-dOk=XknYxDvkh8Z(p|W1>(5FLJ>KF2~&}`WL z_L)29XmJb0okxK>fhausvf@4zmwGS+u;BoEQn6U)%Qd}*E`x*@KBNAZGNkp`?CAqIM*F{B*#BGp z=c)f08GF!*DsvLSGU{I&`jz&d`iH|s38(%880ysULq-nPguyZdQIG}h=Odj8MtN5J z0-hId08&~(DJ4Z6kzxZLDNqopKpJp1TR1r1fIzFVL4KCDX-V4!vY!agABF#TeRIB`1 zxmrLfnjy8R%SME!X1%g&gEu%rWHQb)CEJl7<`-Mm1vY*j$2JvH8-VcX2}K3a`nYia zL=Gi}`4f6QXUH*6Oo59JY~_^SE8NO@YSPV82XSL|JNDFmZeyQT^ODi8{L7KI69;Ia z(8M+M0#r@7 zad9u`NeX(#(mQA~x~3QHc|a7eRN_zB95?u0CU6!*DvquBTdU$%3>ya*AMoWz!iQcr zG!%`2HZ8NsOiit9VG zIAjr?Va{#hLGTe`MZYxu*ekvoy0Z>$G-Gjr-*YZ6GpWqYRF_682mQxJgmP!4^bCI* zQyHrP(U+JyrBZbYP72RW(24lIwG%y+P-%4j@y@{8?9ycDI=GlZW`8c%juh-p1uxnH zqRWO_YLHMq1i-P~-irG~^l_AJuwXMLc!34?Hl@+H)r#0QMvwwn3PH!bA??8Ic!S1k z6$r2Sv}%Z!vU1qn1NkbtXhK@sCt%|iRTf8p$%ocyDH;}FVis9aK(tQ?T3MZ-#XBAN zkV2Be&2OM+*pjOTUhx_3o#E#FX}JE>!vM2IaWA^v(k6VaD7s-zlBLt`(1rHThk-U# zAIamJ;1aY7vmiSHD=RCXO&O`7BowRXcg5(SSl41~;{w0&UazfAnv1?HI=`$Z+)|KS z_K!m5OSfHl7ZnHK6wi&*M0a?nl&+gp87M&?SA)wo%;ZG{Jl&yg@efpELl?_*_r8w0 zL_NVAWg-tkP1QVg$yYwAwp+%u*?Ylik~>!POQ%*`$q!i;IAuSgpmw^GJC=gEuIft( z5BGfy5m^_~$SMs!vC?`!b9#QVD?gf~qDl(gx(Z3#S~$^OQAN_znr1S(jCUd$A&|!z zThH_Tc5FWAM1f{=8nPhe@y(m14n+7=aGIFs7{pYW@1t{z3F{z3iFhY4=BC(BLF`SW zD~}mj^6cTpVX{g_IROIe7DY*3efrQgFtKJR9a3Ji(Tb&4WU)j`GeUzh`MG?jcN3s} zm>1;vot=uAITW%3EESK^^dY)iBa4?*MyPU2yE}|BnRQ(*&X3_uUg+pE)Nr*$mR6S4 z(3wH5|AaTbo!Y<|>ws|xlls4&S?1DE#bnSXJ)2{cxhDWCp$AS1Y-PEtD~XlF4t(5( z9kgM3Ywt54PAf?rQ7h(9As9S*k9U#X;bU)6=BQH}a?X}LTi`%STRAIpRQ?;XIM4e} zk)58L4oZQ|!AWPeR#jU@eFS2AQxz;m{nk-if3#_|Us`YOZGPwvp!)L?4}jTem)cLw zL*ofI@Fuvm*)rgmfhFVCnc;CT$f@ysNEJYLp09Zi*{*nmTsPy(;>*>|XM)Z7Y(x-= zaoM6WT zrFS=PLyo8wXf+5!>70qgH6`N~C@NeY7yV!z>{v@NSM5CdVdLYL~S5HD+a3V~5W6tYiz;=0d zF1b9%(yfCK6r-^8dy^%eGwGP2`x6YT&vB7r@CwnJXL6MLc0|CsCsChfo zLd`h+CJk%;0|O2gj4dIXaKU`YGP|o=>v2{z+>!inx?-{+xmn|#)Ep!cd|aXSY@x4L z3!{o2b6bY(t%Su*qQ)A$j8DHe+Q8ac~PC9}gi6Tn(#d}058sKcbx`qmQ zFf!0vR@fn*PcS>n=xz6Fk}^ePk4q5d7inND8s+dIGXv?N7@Hc{kdFkn1$vyzoUwZ< zqqdnon5xV?WUgeP!`R+41|^bd{bZW4*vQ|kbj1$3tX%^_w-|uR%2=S+10?7fAfp}i zwgLfD1Bfx&1OSQ*0J?8B=?%zJ%n_Vcc1q9fTAKA0+y#xt`kEF>pQs_&k}0%j8V zO|`N~nOYd*$X%7XM*lC@Z<}t)lF1RIm8?-v0Ur^2Q!L;%9)~acDcFu z-*vipMT&X&EX`NCVHsGNpgXgD8V=XGGz8pU&WtY?1B%6HE^}^pGC4y zhf0+AH@t&mHfj_AzgBL1J9YkfeIOJxi@Nuv*dn=nv5`lG-fWx(GH{-9paap*a@=H+ zPt0;n^_T3>q7#-6f1lmZDSZTA+sE+k6jsGA?OM+C>D$<-3qZI>?Fk6wJXHV?W*4UZ zJW|y)LGSlWksIbf>415i%tixWq&~i*$F|SRrJ@8l;xeI#p+&=}rxZb>5f9@Jx3Kka zc7KVn23M2y(iK33;^0EGYqP=NCJKYTF0DK#ipvZefmFE{lC|nQ{aUjvDL3&p?J}bM z4EeC|NTs&7r+N#dYYfFw;SO{*vRB0@Oe%Pl6l2tANd*Qr>RUWT;ZtsaDp_EQ-FU-7 z@mXEZbHYCtZ`hnD>LTC5JC-+u&DeNWqHidf?QkNXO|s?`M*=0cSkN~(`YZq?(B#JR z66aCL{ETFF`uR}9i(&!ac%eS%(2LNENng>7owA*T@NU`q2ov?y6_eqk)3lv+QINNb zTY#8F0hc?71`a|h;Fmfe!lr=KS|`;>7gJ!2?2f=B582@b&&2GGW(s4)bcotV* zKU1diVa*?PSqf8K4b?_3`_&B1jH-Fg7Vj1V%eF$K5x*}whNTWSFx=wIASp};`>{Fl zzLg)kdoF2h5YY$pny;3rPmPF8g_a8``+=8|=($N1$)oN;UxjQVHb9rsGnc~J=E9#r z`glV?L7eu@VZ+2nF>A(xD?deUy1Fv%pVm1{Le{Aea5f={;TjW*82ofWlay456;dH$ zNyT(06~bVc?897tX4BV%ZLr&L-w?mPTpSW3IlZQ1_pX~(G`YHOdL?l{KQD3_TcGwX zeEYR{{ZDLW*UcmxR3mL5W%tqZ5@wa*Rc!xj-cBUr)_0(HM;C0<_&yfjvQCjxNT5E0 zw;-&3fx9B|eYz?4S+R|?2oZYc;~*i7>+-UAksFr#$u8pR%H%>9o0luwnN6xUA9sud z-)_}!bH4@W^w1cGjUKrrNS_p zuKC*?u%r5YbFqt|eYrYVy+J`<5OS^}R>S7wV(DES7g}EJG|fk}cV(neM5hpQHSK)G z71}1LED&b=Yx*P(4aSj>Z=xe0b0+g>m?_Mr!r)xAJTt_^#OlpSB$Q`xUAA{G2c1isr;Ig8A1oY!i_Khl%lZ)$`*Lm5B^1v$ms~v*f)@weWiIK@a*P?Y z$;GGRcP|6(NFE=WG@O5O8yzQd1tX2ZsZYl{HYNPAYrC>-zu$cL7!W4T zKkLXzOUm{Anr+r-MJbAaWQfMWP_?wjkW3{*wMyM;P_3S%;Aa;Uf}d1sfhCS8d92-0 z4LjDXuxN8Mh|TQJUB4?PWk9xrg*@EZE`^J-*_VT^?*G26Hy?kOo3*kBi8<)T>>hGO zA2JOe-IClmo0F%3jdF_Zr&6`GQl?3$WXJo5X6MkQID%y>Hav%1zP47sJ&KxVXZjX0 zKnezz+Z^r?+E2v;){_Q28h3R`{4DC`m2}-0Mh@8fetI%QehWC0UO)K08__PEq?vTQ zY;5sWSsAxI0>2c>W}XKWi|J!fAC3m=L|aFcjzbKoQ=Rq#)?A{&*>}^5TR4Vir01PG zJ&tuAoqOQ5bK)^97g5ckD7OVbp{a{Xq5F=*X=(6g*&S_aPXY^?BioyY3ec1n2ge2( z`VXm?253x7A~8CihDfML1sr(+M=JHCt@P6&FQyBrm@cH!X-LVKib_T^&x^m(^Rka3 z6@d6P4XOBoyyzII=oqQ!7^&zODa4}&Kq@*$Dmq3gJEml|^0d@gysYq^${L@R8q3iE zrCB38@sJF1xY~&KKfDVY!^>HB35Qo)r}jPNza9?<3`s zlMZC5GR-;1M!Q~Lnm9F8Ia{nt5THy?!mptF==tkW)5U60`Z3ATE?D%r-MmGri2ZEy zz)u;;d|o?7M7v_@RH0|TEL(L_K^0O#ds0FBm&G%VRKgVpu+5_QlOp8r>MAxFMLTr| zRu}1ognDK#NmHn<%xHk59fE6=^Lv02)%JAT&2W@*Fj`gvYyBZJ@}1xp(xdYs+a_nz8Kefu_J$?-)BQ{FsG z)NPem)TI>3abQYoxDWB1v*$I5S_^3RH?PT-<~V9Em!w}+A&u94#8M;zXU)<@|9-&S1x#AWs+>;8 zX(C@o#+@LCSJmmGD^iPl#d_KIyGx7s#VZc^{({LkS})#CYQO`sy{#_IZsi@XK|HZo z*+tF8>(qzEJdLgw*F)Dc@0ZVi;0N(dd8*IW@w3z0QwzIa_`JAMULnS@17H?$$2}+> z(dx>$n!ehsJQvS3%^KNcA${oLCn;a~xv{bZr`_M3?cZ3e6eb!-TS?7CAl%>GTwDl- z=@#!U>lfl^SX&M&(r~4MsE3#gdE1FqAeabyN%0j)Pf}dsV)$$x&7q;((p3xec4$Ev zTh*<1CTV)H2*-2`>-G1g(rl?Y>gI_NI|$*0xzoRgqd`Mn)PBFZGDEuBh=y9(4JY!K&T?oO7BpR;zM1y5Mgnc zVM2;Mby8;nR>wiCi+~rkej|R@)pCTDXQo$73c#H`^(Zv+DGE(pnRQ2Tv$j98U#&}w zR%ZKqi^cl#Y7MFCatP>S2oi#=(Qj(TY2;S#d8XK%w9OZK-T9MG3`H4D2drWf!mGMe z3Vq|L7@Kv&sHn%LV_faDA%!sH;Lj$T#vNJ0tTBW!Xc(~rIsTMOkkQ@Wvjc*lq47Kc zM-@{DWJ_1Rm@+;v0=VQ#72g*@JW?O&Qxxa|b|wFX6mxOXLyF97CJz&YNY(Wq^#<^u zrBcqOW1Le>;OIvm1=Q*>5S}jZBD=xMD~}2+Q03w^`o!v?5DXTTbJYLDNwjlUtu8x8 zoKm^Qm79{@QM0q;Dd++LNX!zBPdExjH}KUz^q1)Q_7zIc`NKz7H8_}O?U!E{S`ZrP@O^y zh=J_O{C#`+AV+6;AzriTlf~%bR4!BC?F+zbb=CrXmZnXMtY(TapF*-8x`J61X{Tu5 z(VXssnXX54#50aM?c~$!Y$!@ZjLGrnwV>U<tnuM+%){FfSC! z#%04_i!&^3YG*NIpy_EbOT6=Xko;!N+4APc5wY3KOCgn2D8rRKKS)}fo}UL7*x223 zyUxksIZ|_o!G^kWK{!YA1Su4+T)VV$$Hdw6WWIW#)n-QGFgJJ>$~>VWegb-d2@gQF&U=l#J^3(Wb^!O70{&To$u zar*P==r^nJ)6wq9-tNIJ{@iU3MzCIC2%1O zJZKQKyz|d7Dn})|`)^Om=$r2bZ1;;!cK_UY|B17(!-IoB;P$)S!=Wd3@^-NQK90kT z2^iwr-FMrc-XEVF9Dz;mqjmiK-n(6$-Pt+W-u|ycMTWx@tLf9>`|3zeRleOj+J5tX z_vFLdP5wQ#sy-YX|KTXz%QL>+KX-RN9XlmHmE663s0)LheP$K7*F(f-Ki2;#lj^Hd zi`y@DYRsgPHEwYm$Ly%~dScTeBi|nFecUm1&VHW%poyD&VL*(zlUqdj=Yt}0){#;zK4UT3b`Ft0T+j? zYkGW;yX{9Nxjx?GH4$?2_{M%|%ltzrOm2F(B-&F>Iyw|x(=}LB9_r8|2Pxcitjx~m z_~sY`#t%K6I$Pu7?%QpQFC|{Olc>w4HidHfJnkYO+-y-oOF8$Y$mBRqcU#ht6lz7! z`=KcolEe^)#ihD#LlaRvC?PBjk(XMK%UWD2GrGAYJT9jbQo7qw-1RU0W2Mdu28kT& zY&nwSU@r`FcEnfg&<}6$zqTC)ReU3m;6InkTVJq>=Z73Wa2aKnOq^xKfgyQt@&0tp z;iiMe688d&#NU?bjucUId#WRz5n4;rXmaJE4N^U6SG`MQ)}=tSGAB}ZnLYup^MEm#n! z^}%&KBIfFvlNPoKBJ=J~C_1F{veFMYadC1Laq8qng^Arl8gaejdW~s*q1cH!7yskb z{&Jr|UMrWMxmWm}9Rx*3xAJa0^dF^gF}%z9?h26rV1x&X8YMMg$Vud+=kXXs6|QnI zeQ|kf8tsV)$&(0%M2cxnMM^3asx#Yw8@uHh;x~NiK?kL7q{RSJmMgC>D*9D zv3O@k>hqW6hGE<7IjS@mLf!zez{I|y#2GWm8O8^@v+_#_{P)PuUo0mwep!W`0q+-G z$ZP>=*Hr)Y&q052VyqVyEPQ=)ares~%g?|3`R&S1oG-W%`ODE^UC;8z9kz7=YESaZ z+lOl&=05vnef?K-$4)}vWG(6Om z8RjU8Lx$eLSmR%Z_QW>Ie(K)zKKI9LYevzX1C6s4i;4{!d=ym-83z}m>eh?i_s4n` z8X-gE!l*!_gYWFNtay0AmZ9)6=>y+ul5)1*vm+h3H3%}BRC?&j!p5*CcQekdc)*2s z7o?)_M^!Vn1X!AeLN|%sGD>sTo(ORO0;BCkzryuccQiO;Z#y6`b@sqA&`_KK*8&YP9xJ+|<{ z>k!vIVu0|JAarwwMU#7I0c;bjH%BG}^A|6;*x4-}08#J-PAZ6qMv6oFdch1G8aF(Q zf7;lNVowPbxKQA+f(7fCT>NrotCCtejMG@dk*38cLlBT9Yw-JIf@(4a8J+c2p!^U* zvIYsO4{P)DeR{LPscNQ>Ev_nLvWx5CLyxiS5&sRdC>zc-CIez&lvXYp@3WDwvP5!a z11f>zwS17TigHV29G z7gd&N&)3ELrJj91N`g2{o1g7i<7&VVi1HNCaP{5rOVC`uQ1lKxg@IFmeK*Mx6ysoP zVeGCds-G%lv0#zB{(_If*8|(&T0Na?tE*`7M}C&$m}+tj*5k{jG~>Agwcg7-O4R7H z39IO%NtU1nHcJ;L0k*k<{osKU?9vd+qxH>gkAvA^@A~ZOo}K^RwP8MZXW9xly{t`$nQ zP&G2i5>#pq<1x3nE%zR!Kn}etSl1MtkIU5sMzr5IDN`&jUs!+TEXgLxAcBkBbG2*0 z)SKnkQ=W%fNy>F{OJgQyO7%U}A0)Ck`ozq%Mh-&n;(Z=Y}2ni@!?1*3(@GA&`P=r)&SneEZO-oJ^J zC93p#+FX6lLg3;gjZjusn_Km8mv!SIIYsK6dKpbMW%@4gRis!6_G(|wm+D(sLz@>h zqn}tq{9Lu36iuce{k$?~y@j{4=6d;7vtL^p)jQ6rpo$(JcZ=+|%EugeeB)xHfIm{K zuN_(~YU0W@IFl`#xWl(1+zm zln~3(kWapnj!B!l{2GikRi{jC{ADk4iH>qQhG2*v)rK&4#1&Te z*1vjqzKll``@kfjZeeDvyk~j!5Kak)@EQ>e2q^Sm*^497bwd)^c5vUE@~S;ChLWhN zNP{BRHODEGWkkm-wheLg6p)8eUoeZAUM^(jGg(74WPT93N`^IWxk6ShTl&P**frS>HXWezlE_c>E3t)q-D@ULf7yMj@M zAi}L9M2cSoQ9js}@;w~WYC1HTg51CHN^_ygU)Dap^KbLLPiy07lp~m6sm=JS4#61) zJX2&}Ioa2_l)3l|W~g!nTX@|n9e%0IHo%teDdojRa;SPR<%BXMqE{S&h}1)Lp)&bS zajZ85C;Gj4i%f>)|9^^7&;Cqu!2;tc+SNSzh z*5>Dx`7ww=;MuA8=N z3TjnSeyGHHo;iDH+1wc@Tlv{SeQABsdA?4xJZ6@g=GDe*p}s_360^X18OhyiEg1)^ zSSDg&^5r&uAe8o(KjMe`9tIX8ui&Ow(4-u|nrR|lvJ}fovqoVdghIc}mr@SR)X_X- zC^L4H5#7T~_3cy>hiCCniuGN!#g#9R5HiUU)Y0jMlgbj7v%KPQX%#7<90?amCn7rC zh5n`TPPU_jeQgRg2|i;E6Hxrf0+)6+rV$=vbpUeG*(OITMvA{$z7CAaC%H$8lbkOa z7W2n~$;1|S_z65)Fb^+;#m5;bDH;R@##7SYu1+6JSYghw+j+%=ZMilntJ9y+ELZQ- zsJ}QtTe%|`qq>LbnUMSMmlw{P)lZf3*gdv24D~_L`nSsW))UjZ2-Rc?Qc6l#tI+K% zwYyqj?yzL+ll%uSDU)xk`aif}f*@Ga2#61@JiK)IL4WOZMhTpFIcy6rh-rp~&ev<5 zV8qc|bv%GFQ5IpS;in4zP@cN&yG(g3M`$r+22;h}p(@nqXA2Dr@u=09Esg#2y{k!) zzH$yw4^}r_j{Sw+k zA|~+O+JehirZ$_IyUVlj<)=-gzPN1B=8OsvuwBbN0yO<~fdF2F_fTxJV(Q{i z5$~-1M?oVUQaexPf(JYEjVOCrbNV>dIfU`4=YtBHxuW=5_`=M6x-uR$@l|3*e1S;8 zAW<3;$V9l6I%jc9z!#KYLHlhso)tSri49bnJKdJi1&iYwl}qao3WlAFy7IgBL5t&? zcUL#3cZSjm2GL*v41pWI68`-ZO$N~1?Hh7^0Vq23u*~G9qa3%g#>aN0B2K-5iKUFV zc62>Hk31HwwG?K1t^!QU`ee(WStJOHZ>}?8I~p&YYp}9Rkf|` zvZVZJTW*bL1|Y^i15qI`&lyK(_nn}zIqsH%4lp7-5(A9zQM?eKr7ngYG&W&nC;idq zVKP-Mil!fKt}*(Cc_I3$-?|ZY_w{#JZ)j8S#$Dqq>6Afzn*0(S^k_I*QaS^&mYp%I zLP$(UeR5Xmy$hCTXS7&KBn0hDjY^A1J^I@$Z-_127S_ZQU>UPN|GK}?4sdJG+AAJ_P)ctO4dpf^qKx5iKu%UcJ-uoIvbZbgU**v9q4@RDHU6B_@%{J-ncB> z`s}mlOc-O-k5LW}3VRjP-0?~F?Ikkv0M-t}_- z_x8F2`xh!rJBr(@zk0cMpF6oL;vlv)+^t46xHekOiYxfoW*}xyC+EZe4bT+dv$X*@ zBEchoW2(#pP6#y*IHBD<;Dns>fD;PO15SuO4>;k2dB6#4JOVi8mU+Ml1I+_Ycxvpb zFrEG}0V6PG?lG{8FWq0c;;xWQCKKE26tY!GdPKBYQ+^D{DfgA^F|TFzgtIbx!bh1s z;hM~z@JMD)I3lwr^v~=GwM+JxwwXPlWM)t3hN(HjQ}~s^8PcV0nBE^1wkJhXthdFZ zx!)9zsW2^`P-9v=p~|#)LY-;xgi6!m3ALuh6RJ&%C)8_-$5fmaPpCO9o=~;F2*)gJ z3a1q70gPzY0T@%Q1(?vS1(;B-1(?vT1(;B;1(?vU1(;B<1(?vV12Cpy3oxN$3ouYp zzK5x~nA4i3W~}brvWkv#e`9Q;d`9Q*c`9Q&b`9Q#a`9QyZ`9Qvz z@Fzm>0;hX zdN{X|F1D?ti|5g%fvw>%d$OdD@2a$e^P#kd_fp!$eJSnYzm#@qP)fUWD5YIml+rFe zN@{28O`xJ@7K1HIi zPmw6>QzQ!e6p6wENxgZ1px`V(QgIewprjsf4vnp)WWTVY253tJtJ-{v}^Z>gY(F5#qMh~#dAzi>8r}O~39Mc2r za!#|kvz6Abvi@{0{!_3H&om7dtM*T7>+(xmTbEl}ur9B(U|mjW!Mc3Xf_1s11?%!i z2du{-Em)U7TCgs6bPqyCbecl+B}xaX!x}AAPnI-LUB+mjx_r?T*Q`)n$qX zs>>4%RF@@Ms2)c&P+f*-pyv4@w@|I5uu8J{5l*N;=Gb5WndN~3GS2`7WS;s3WS;Z| zWS;H?WS-~+WS-&$WS-mu$Skc3$ULD7$UK$nGt73zPj_FBLClf3)GiVxF`GVwSQAF;CcpNV@uE`Kelnm&{rEmd7G*lUX-H)0!K% zQpZ_kPB+<3F79BtF79BvF79BxF79BzF78mFF78mHF78leRNPXhF78mN zF78k(FGNr0K<4-8RFySE8p>Jt5K-jA?Ds%8XFUQDyfqJz+%*r8{521e95xS;JT?!JTs9Apd^Qh} zoc0Jr@Y*~?a@#z_z;7eU`7Psg;hdvh1J$MA6sk|f3DgWFCr~rgoIuS`bOJR))d|!L zWhYQG)SW=hP zeO{V}?{n2We4oGO;rpC658vmxdH6o}Jp$k5!+H2VN6y3ddGlR{34NR`(=j#Gm$ASb zN_#9+Gm3-?Grsq`y?){y4+F$^m(KL=yOH|(C3E=pw9&rK%e#%K%e3jK%dSv zK$p4|K%b@+K%bJ6C@L*A#{e@FZGd`IodR_!I|1rbcLLO>@C2w&$61* z*XN8DuFo7TT%SK$xIT+?;JRGW!u1)Yg`49Q-B7JmiCI2q;re{gz|HW%6mE_Wrf_q7 zFom1rgDKn`A57uq_+Sb*#|KlmIX-CMX82$VH^&E4xIQ1u7`bYD#S~M z$RlJ|rih3L~E^gRlRNV4LUEE=a zy12syb#aIEb#aH@b#aHtb#aHXb#aHBqvDo^b#aGqb#aGU5r`Z&S0q6Mn5@V?n5L$J zU?}O4a#Y$-vMy~2S(SF^Se16jSe16DSe15&Se15YSe152Se14tSeLd0tV%odt4cfM zYpy#?$(;G=@7kvnWK5`^|}Pl!dYrp0RNA-H{FdR$+Cp`KpzrQOZW5ug#oCwSe;s0q$&&Z zM+lKYDn_9QRxCejk5JD7WQyT-kC~g_b8I&^=TuLCgy#atNG^bk;R49$ zEr5*M0?3FhfQ;1w$S9ov37-X!ky!v4lRliIM8;4rhT~P+tV)^eoW^2LSPXkcV%Red z!=6zX_Kd->X9R{l<1g$PeZ`)z7xs+2uoLbuT9U6g2_sb}t7nm;Sh-A*1G5=&ywK2+#Vlie4wi7YqBE?2-5m6+qX7IQpTV~*!~%<){2Ii71W$8%NA;HfTi zJXdCp&$OxA2VI2C*T!Y`q7^6oO8Pt`k!_z6U`(wejEZ!CacvGTF3ADLl{mn-00$V? z-2mfq8(>^zBaDh`fN@O?FfOIHTR-pB8)jC$4VXk9495gQa8w~9j!R_3agB^PE|L+) zRWjnZOhz2n$%x}ZAvmg(5yz!6;xes#IBqWSak8iVitD7Zry6N7TptZ4(MFBQbWvk6 zP1Kl74>cy!LXF9EP-8L;)R>I_29t1KV=~@r4CkD^1WAT)43;P$fifX*DAfana#>I) zR|kc1kx(et3WajXP$*Xpg>nILDAf^#aye1xoT{3yD{hbS0bu#AB$1bYF>tjU_RF>% z8H!7Em|>X^6D(CC$8srhEY~8(axro&S0l%AIdUx5Bgb+A9>qn{qqstP z6qiSj;@ap@To?n2s-j16N%SbLhkl15scd>kq6h+Gk{DrB69bHkVt{c~3@|Q>0mgMP zz_>657+1ysGFN^Gi&O^vgVR1qbWTnz!4Q9?jwR1lCE z1q5V<{ea9cACMW=12V&Sf#hrlWQOU0aF%WNMt#TtsRc$*QV&od$hAR$TpM}L1j3lIT>&`N6CmNa z05XybAY-@yGI|RjB}W5>~E?uyPHAl`|ez&Tm*bn_=bL6)R;btelsyau(kI@xAo9v4cjN;+6zo zve~x>QlG#i)y0q&aA)1KY$Z5#UrzUUUrzjp;}RHgTnrKKkSCn6Xyi4GW!)g%N*)iC0?AVwV5#E9dv7;#(~BaVw>#BqI$ zI4%)_qbeD3Tqq+h(@Hljoa$i2aUBe}L4 z47fxGJucHhkK;P%XN5?W(L)lY5Ev862%|a~U|c2xjH_gTaghu#u8{%8B{IOcLIxNY z$OxnQ7+_o;18hbeG0l`o!)G#1nCDazb0NI5s(CGtTrB~aQA$8&R1%OGg#=_q9RZn9 zMnGm%5s(>06iBXyfXpZ%AfbZm<>B&i_3z84WTkn=UU;6zSk*9#ai-@mLG>IaxSqoV z*>jj+dkzzH&tZb^IZO~fi*d&1FhThoCgYqh81^X*sFZ((*7Gf5j=V2eJ=)Cd%8zw_(d( z0zZEKpUd0Bi3l^fjY4JRW%ABn7yB=l>xb=bb@=jaCjOh1E>B+W9**CZn*)xKSU$?r z_UXMjt`Don^|GQV#lGF1Ob+WQ6W=ol`Na7QLKa?doXWSy#o918QvYt9wP`Eaf4enRRM-XAoY` z=7^|dQ$(p@Q$(p=Q$(p-Q$(p)Q$(p%Q$(p!Q$(pxb3|08DWcS*DWX)P4Z>b*o@aN! zBpfpwW18S7-y@E*KH@m{BaSOz#Bm*rIIe~f$2BqHxH1Ti>SM%lm5ey9725DLYU&V) zPDT_{$$+97=}}xEJ&Nn2M{#xZD6Wkj#g);cxGs7WSH*y$n&?qn5j~3QA?-;unjok| z7aYm7L6KA+49PXZkX$DW$+g0eTrUjCHN%ixHw?+OLy=TJ49PXbkX%Q@^pSO38Wjzg zL_-Y66hv@TKO>H-XT)*sj5w~G5yy2i;<#!?9M{Z<h?%JHjF^co&xn~Q^^BN_cF%~JsQHYTiN4Q> znJE68n7j;}5i=?=m|!@!V7LuYTh ztLk?hH&AG`K0C7>PotidewAptPEAv1BOeNU_q#aktjv5^K5q9bjLe7CVVd&wa`%08 zyS&=mEr%0_X@sU!<~~=L^RK9nk9RozC)uQ%8A}a31F)i-1H9~}fGE5vAWClvh~k?9 zqWq?SD8MNoN^lB@BAf%f45xr7#3>+3aRZQ()8%`5INa%Qd0VzyoWO8i$FzVW^fDY{ zncyhd1V=e1I7&UiQ3Vhj6#~IgJrEp~1;a6Q5F8Z=!BMST4ZE5enKWjukRC$B(LtCl zS_qXy3!!RgAyf!0gleFLQ2JX4Wxj<_(mM#_yM<7yTL@+O{cgEiu9u6wz4_ZvJlQzE zjh&EN+ZnT!ozhy_DW{d45?a|Qo0XkXS=lL%m7S7U+ZltEozhp?DR)!nfz66FRHjhQ z>>QjBJp*UF=Wr^698Oh{!>LGeIMqxJrxMEHR8cvc3Ty_>^p?Y^{Bk%|Zg}K zR{I0q7xH(Et~TFgKZK@Hq1<}8-Ypk*Pa4NQo?2mSr{ml00p~q_U)?QBA~MNyv-Kn* z# z!v|-2c=+;YBOlX6m3e z*8{VK0+=m0XSQIO*@9PQ3nrN@xMQ|pi`twYW(!7`ZQ@{azj|;4&P_sF|7v^OSVJ#M zdCXX`kphB}3{ZGU024V0U}7i%Of)5ciK_%K5taZZ))K%(T?Q!pC4h;<1W+-#-`eu) zYWM98PUl`c*rE$)94$46)LIB+)`>l0t*9f`iZ^1dNF&yYF=DOgBG!s4Vy%c`)`=xz zttcYaS$-~WF+t`|=-E~>d7aES2j5f$<-EQa1K?__8DV!i(a+vn9C zE}u_<11OChhDl&kDxWH&D~lRKQp{Mi#EeBm%vjXJj72ugSoFe-MJUWz6he(54Q4Ew zV8$Yb+PK^Pc$%fG18{~$5MgQnF~)ijWv&NN273@?vIkK{dk|%|2T_K55M_D*F~)lk zWxfY71<1kJINxPX10#g0fWk%{Fc?t+fiW!*7*hj*F+C6%Qv`uAO%NDU1%WYLFc?t= zfiZ0m7*j{Q<2F>~VW3K=j3fV(k+5EjjO$`#3>PEgwHO(j#mG1;M#fw*GQK7wVW}7y zH^mr?yjb2Za6FWcj`~^M)LOWiLl{9b2xlor1XVdA_{tGMT8;?jazxOVBZ9*m5k$@) zoXs2&l;((zSO2~w_B=0lTF^XZ9o}=<*%}sGTgzW#>zHe79cPWLW2>=sJT=Ry2?@o>I0z%9z>S;%H*x~p$o=m|?tV9N@4JyZ z-;Lb&VWh5iBlo-;spDTj`*_>Mm(#fa2MnR1$1pZJ3?-$*P;NR5rKrPDraBBItiw>= zIt-<+$1oN<3?;L}!ZA8^M0|z*%?E+|GrwU-|Q7R z*nZtE?&MSizva~?(mEemL|s2;nl8|$?laLY^YhT5_w&#p{PWOJ0?tE6Q8*7BW#T+^ z6pr)IQA*B4dvQ4r9p&acbW?ynS$PPZgzORtbJ(o%+7xGH*F$GC*Fon5*Foph)>9br2?;4#EV}L6}fF2op#TA;Rb&Ob{KU6~e0pTF<+iEv8l> zg%69(-S(RZL6%Ye_`Yi0d}Ew9%456jM~K@dI8;w)kPl5G`{r7kR;-y{ZE#fg8n>;I z_l8%F#=eU$sG88?%<+(^?Ym2CodSoro&1Qs(cWlpg*Vz;)s6O6a-+S~+h}jaHriXI zjrLY%#NKFZw6_8q?X9}PZo8gd#g?avy}CSXX{Z5I5WHud=7FGUHn$vcqqGwU_9rq0?v^tE;GbW24K?*ebF!wp#3r ztqMD1tG~|JDz7uPn(K_M+InN7v(DHmtTVRSinc?vL#r5Xwru0}ZX{0MsewXeU$>ik z`Z%PnV%SZ}C%H|d6w(pOZq(7rARW03(oxJH9nlQZ(aj(o=@imZ&mf%u7^IcK$Iari zY{l1)xhzp1$SJJWHdUo`2KDLL@^*3b*Yl0BkDITX?eClF0FMaYA0HpLJ4{v96B*Ip z(!P*ad3tYVC=yGICN6ID8iE=_P}CSwqQ(#rHHLhsF@!^nAsK26v2dfygc?I2)ELqT zql}$j{x~c*WkQNtUL5g7qK7xjdn|P7aX~SCC3TB*V&piN=U=us8Ay(=Hg2hDVmC9- z#}0;h?7}fO7nZrX@XXDHX>KlDb8}&vn+xBGIT+{W!Z|k=)?t=z26lsC^i#i^BRme4 zhAU>Bs3O*isnJ>!HCk()Mr%#eXsuZqtu;xbwdQEF))WzI#n5Q22^y`1AK6#k)Q?rb z#6}zFBs78H`#P|=ybdg$t^%ijII&T!0=feSX@;HG7s5+O826> z&-TSrlmiBa2+ZoDHhUwCYHWaUaSbr8ssYC3G{Cry1{fF6 z0OQITU|cdIjA~_oaghu#t`4u3##hW+P8lpwNdjd`;ZUj;3gwESP_7yZ<;tN@t{w{I z3ZhW1A`0b7;!vt43gwET&{&`=F&)W>dPX{>M)BmtH>N!TP2-#pHjQ&a*EG%vS@Ssas-|&Hh?>UXnvN-G z*#_s8J?Sj^)HBwvmNMdm{idl3iX{~;l~+ri%Xd9ZgrAMBm~2YbiGVDESt?JY-xz2j@Jcih>cWX+g`^gNZ& zhMfp)Iq}er2@mc3@1dR7J+$++hjt$J(9W+O+IcfVTOWF8=Q$4zfBlN1#C!(n)2~%DQdwCEla-YaSy>5>m6gy~SqY1km5^9j35T_H zq_DCQ1}p1C;P<7>4LezOQL#`0!toJMeC+{~dp%(Cs0U0=^nl549x%De119fyz~qn! zC_eCj>9IYaJn8lp=T}74qTfcww}6f~4`siw+Ybe_4e~(?Y+tUSyBFaI6P*+SX#H2gXF|~l1uNCYOb(ZL-%*ciyj2pU?e#y z%uDCJc`AEGYyy%aLDo<1OHXq970-*+As;c8x6G`M{OdNgPpX^6M22S432n}#Q{*~> zDGHv!6j9G$iq2;+Mfx+CQW|D3rGU&~qRh--N->(jM9K1P%Y7F}^^n*K*SXzzPVLTd zW)FTddvKfCgV)R+oM!gmGqVSmnLT(+?apCl5B@TH;m&rH}524!DhQ%qi&goF}}actDR|6YV3aY zC3zVZ?Wb9()H>-4$0@8Hv18G8#!m5paE0ZF&{&EnDoYVXXDOm6EkzWqrHG=o6jAh+ zB8uX2L})HW6xF4O(B0eZ<>Ly+|0$P+tv=Kk>O!5BHZ+)ML&N)RXn4F04KKH$;n_Ac zyxE3^2fI-BS{oXkYC|XQlwN{v>DQ_hy*CqaRRFrM%8?DWh<(5pXSVpsoo)E!&^CN> zX&XK{wGE%#+J;Y#ZNn$mcHzajZTRHgHoQ3a`1lnsN*um?@($8l2{1PoQ!@en?*4vt zi|&pSW24s6lS-|HkvW9Pd@A~?(u!DfyKUUNh+oFjtk8HBT*BZB`N z5lXOLOSEj52i{?OGBzBm7Bb_+svhn<#x4aePa!zXQYe&WfWl}7D1>H!!e<63bY_6U zW(Fu^rhwow0~9JVK+B|;=igR%kNqg)mw1EDm|*6P%>?3wRCB>04Wks&a!MgBs}$1m zN+B(?6w-1_AuYQU((=n84Z{@Da!etG<>LV_(dwYPUaIkG*@ySKC0Y7rBCRvChaTn`MF?X@f z)N~i?OlkMA&Qo|7>rA0{vAEu)(UeJ>X+y--{`r^1=D~Z!Z526#PINvWgK2y=2G#dm z46g0D7+lwLF}SAZVsJgr#o$_=i@|j~7lUhfHU`!2Tnw(=xfti`7UTDylkODZC)C?S zI#ZJw`J*q)h>eiCG&=^V|(oT90UGc=cPhUU7>&|I(?nkzL!bBShXuFVY1MVX?h z8Z$JPVTMlhx8EO!?_syyfvXOZDQ<*OsSPl$u^yI)tA{13>S2kTdRU^P9+n8Ghb7AC zVToi07}rVJp&W`^^d)qzg4DdB3IV>c~3vb_EJZ2O}e6lk@uyn~=a8mwjEENpBUDcg|e03CNcm9BVYpoYx^ z)Y6)OT7DByOLPKi8Bahh^$DoeKmuyTkbxRiB%oFv324&Ew6D_(=@jq@6YZi5{Q0=K zHS=_8vS{N|w+Ya9kQUjY>$tg_UZs-rDpwEhb`F!Xmo3%f{)=}7-fnHXXw_9njFbLy zfL90|v4>z(`gLAY`=wLRJePWVHZ7Rtq3xwE#j^3m{~5fRU^gK*(wVj4aTCs={%s zXbEu*Vx+4>5WZRjBdkR*###iUtVJ-+S_C7lMKIP{1f#7(5Z+n@Bd$dd=HwF=*u6W| zm#BQ@tC?|-hwQt@>GoRBa#LYDH5=8L*?CUPL2_aah7)tpo0x;!#2myX=3q532c?%(b9ro29n?)50=TH-qGpLHt8C1pV4634c z234^iWJxB zBE$8{$Z)+jGF-2Y4A<);!}SWuaJ@z{T(6Q8*Xbm~^-9Tbl~&}70^XgnFSp5?sk?8h z&9Z%i{?&GkFG+-Y@>t#h!1C6A245@h+Nfb zpB<-C^VxAKWuG0VQu+CDCdHo}r&9mfad=7aG3)g1GaA_{$=aMKwu3N%wh*ev2Eygn zK)Bi(2p3la;aX}STsjSeE2V*OVYCpcg9gHBZy=mydr2+!UA$OHr)j%;y4w3*EmGN? zijt_|jA%?Dr=wBToQ%embTS%O*U4yHaVMj36`qX7m3cB6SL?}WT*0TKQB|Le#+80D z8dtwJ)Lye85zO4yr!KZt-V8d?{Co^1_t_X!>2oo-$me2keb2?<(w>XKRXrDj3wkaF z*YaEpF5}r4RKasGxOnGcB)YW;WSLo(uZ)mN+9!ttk?FHd2Pmq^9>sOop%U$Ns6=la zD$!VnN_5qs5-oM8L_ZxW(M*rxI_Xe}HaZm7gVj^h#G-7hi#DBzV-B7uFy_D{`9 zkM=A<)0k&#JBxju%#+w>DxSwaSNuHox#fVzKDS8l*yolG9{b#a!egIXW_av#i%$~! z%#y@opIfMS>@&-koQS%-yV{&vs%reXWvPpERtak3oL6p|IA@lYCeE2IqZ4=>Ej+DDz7S3#{eoJq6b%lBk=ZZds3pJm>6(yg*6&0Vr6$PKb74@FL73H45 z71f@=6~&&yg<4PGic(MDib@-t9!3s{>Jh^Divx_m$eOeFN?Pos>krF+9bUh9NavujCtURy##o1;ytNq4UW?%zwiwQ2i{X5>7|v>o;oP9}-L}tnC=#Ed^%GXJD#<1kBaI!HF(7I8g`(CtBg)L^T|o=!b(7C2??~ zsRYc`#leZrI5<>1x- z&nPbTgw?QTM24Mn_hI=UYbfZBJC1NTU=l7Fj&VwGl-m)k-E}A90-f5yv$! z;3 z>YvA@OmmTe=wwWoZvc$-8bBGZ0G#a#z?rT9oaG9@8Lj}F-3q{&tpJ?W8bBGX0G!PV zFvp}7*py2%&$3w{IiCTUVbmdWoH}HVRfo*+>X12R9WuwQL+03Z$Q-``nPJ!=a~wM) zVfpb6-+r9B5sc{$lCeF)DB}Z+v);oJ=6hJeeh*6&(8CfH^sqz;JuFc}4@(p=z_==U zSfY#`R@8yBKI0^U-BLaa`t~U2IGQdJA8@Ah5QM>{V7mU1cS_RaQb;WhIPN zRzg>0C0tcjLR4)XS*omrqRJZleAFElH(Q*h_Z>$D$&Rh*^ry^NSegMCQ8~c5N&!Jv z3JAthK#-OKg0~b9)TMx6F9igFIlwtg0YPI5=$U-^%CJ(T($AabP2KgpIM`HcE!uC>L&{RJe^Y;WkQy+b9oiqcnt#vEVjJg4-Ae z@m9$|LKr9um2sqDG7=t&kr7dhjEQ1oR1_oQq8J$&#mLwwMn=bEBzzPjBcvF6MtMI5q7)n4bQykPWMnOGu6x1_FK|PZc)H6y!J+lPC-5M z6qG2yO02YHO~LdeCTqrhgP_FM2+n$iNGPum3EveWA-h5(Ojn46<_eK;Tp<#IYXoPv zLL}5yh>6#)o9*vD$yE=z`{0U6VhRdJQ{iqfO{5K`iLJpj(KMJQeg@M-%wU=r8B7xu zqp5H(m?q~Brq=5lJ14F8dL7((y))HbZ%vKY8&m7`#?*SfF|}TAOs&@&Q|tA{)Ox)! zwO(&cjn^Af>-EMod3}F;eBACZkNaweMY-R%yRTM#=>%}h^vV}Su;Cmh8hq4n?~%`A zCZbN_oV8Q9pzs8)@Oc7P$UT88OrO9N+E3t$22S9LAWqPgX5Lm8SYDMNETWoWLd49&Hbp}E2` zG}l>%=4wmPRC5`cD=$M+{i)tQKHh9!pj$pxWsR9Dt%neKbr7bh7D9#8La2IL2$f6= zp*m?HR2(gYDx!r@8FUcFeG8$4w-Cy546drlppk0=;*4`3;hh2*`wU1mz<^W<3`q6B zfK(L>NVUO$R3QvVbwYtmEeuFC!+^8Ov2mG7jkZpaV2hjA&w!ngP>-EcQHPxsQiq+@ zQiq+DQ-_^ZREM1vRfnC`RfnCGR*#)iSBISyScl~r({4_;X~ufa-lMjQ36bckO9v>X zr~yT_)T6kHdKA}BkK)SdQCu@UimRnZah>!iu8;vmwb7%vDtc6|2aKPzwju45BogKs zB~*}11`{OJK!M}}D3F}}0?DZ_kev4d$w@Deobdw5>7F1d*9DRjT_6q12=(QUiP|Zw z<+BN|NUXza=Bn_9t}49Ys0we0slppJs_=$_D!lP~72Y_z4zE32g*R@k!iz8E7_GO* zJvJXvf2i#n!;O4>zHy2_ObMV8>g(hxp z*osvT``{;7!*@cL8yt*wyYkbe^lY?QSj*xmXvOFWXwB|Cv|)N4+OR$kZ4@vMZPYLi zZIm$&ZB#N3Z4`3?TB~Os+9+ur8mjtjv6GWi@x56+x=}Wk{(Z4N+BiNl76r`!OgA~e z)sg~2BPk%1kpe;wDIipk0)qP#5R9jQ;5i34yD1)Pbj4TPbsDVPbueSb?~LtOg4%* zshL2{3&v3=)MBVpQZdviofztrNDOsKA%;374?~^OhM`Ug!%-(xVW?A*Fw~%j)bihD^3>auo0Rs&pprBp@ z3^Yi9fqDhV-U|O%s=lqzv?0B5V4eV4)<;0c_yFkH9soVl1E6Pl0Q3wGfS%m}&@(## zdR9k3$LIj)*&F~plYW|=cM9d~leW! zL$nMG{O-D`KHlsNg%x%38Ysdf%GaQkY0fV(yNa_I^_{auQ~$h758DexSOV=_OBLbX=!7N= zbV3jYI-v#wosfcq=5=786CyCspn#`upSSC4`KrbaUnjM>?IRA*9P5G^WJh(5?DJ~{Fe4xX%;?1cGx9LNj2a9uL%suYt_?6ltpS8deD4Y~ zes(?@rUAdN?v^{PXnYsv{$js8d@2_YxVU=QY9E|$Jdq)Wz&DH4=0g5?4)Dcu zd%@ww_8USz#qlusvY+%ZFYI*9cWdozUR>@Tj^FTp&2xMh@PNG$tGf#v*goA4=LO+N znf3Mo@$BUP$##LSo?_`*LIDPCtZal&zNwV^Y50t3moh7?cgZ>-aXJyu<{~ zg=Q8mco`|KZsF0pbcDvEB)b(<` zygj_dg3w+x=W3ImKE0x{-0kq3qEGoDQkevJeOT-c*GNND3-aTq6=&aDUG zE^n^hz5R6g?eps4xIOMApZeXP1o&)mcX>ccpO1&-wB>3{@Rigq$mC{!zukSie%c%s zKc=^JNBq?{dAqbEEwd?D%du80L%&(=W%FSWlH}M@cB~vIYYr7hX9<7Y$m_?OlAA1L zRDXDh@%Hd+v0o~|NunEkdljI&f0Q`@a_0sF?(j+C^uCf_JiS-+vsl!#Ypw2y2gj`V&+75rX8nW~ZH;~5@6bOu^%U0S z)@tjw1-@~u-!eEE>99B)_i~pfdzhb&@OJy_YJ;BK{r0@5bM?NrHPU>P)6U2G(^3Dp zJKQWDF0SFAsxJAkyxpSx-YlbL+oKP>`eqG2`n)m(c)!5g_NJkC07eu3ez}uJ(M}1v z9jFDqYllXkO?qnR{U0Xy{^B?EN|%mYPF;b0^ke!o&^LTUSApFVt;PWZnf=@4@0y3# zRpsDe-e>VdO0HY$uiNd{%P;7`L;bDgCH8#mrrfti-0lT>7?+3A_rf+3bcL4}=x$@c z^uLz&u;s2(SE@6hf?Tc9B2<97u(!9pg0R<574+5mxc`D4>h|mL5stlI{jlVvQ}M5V zw%^auAzt`T^zU9`M1T`D z_xP~9H}k9GdM%Ao4Z%Z`z7d3zZnNRR#6#%o%_>zqx6Se(ew3IYCuC5+x6}4__;h=$?X1eb60xadmRo{cl~X*c$@}h@O*)y&$_^f zSRz=4#Bw7al-8`v6;dv>9!6gnS_8S)stb(Dp)M&`cEbzZ zK)SrU+PvGX(ED4gQBqOUAO%^|lN^+Qo8^y#Oe9!_V{SyCsK%CyZx`w_xr>CoJA7I0 zisOWN+*7{agxl^M($rlZYpN+N zKx?LJ9m!jxB#-6!el61;;vSJj1r$I3y4WAQ{kTAz;xXQ%#fYEVNB{M9xxCx^Z%nPp z)PddbYA-ZjSRda2&zAQxp*RM~Q+aFa1bK>eBkMs^(=LvLBINjSv)ybhNZWo-sOLmd zguAa-pQY{eG?jVjs-VF84oi<7LjheROY@n` z-z+g{2c1{>kcLzy#_Wlrjz-NO<-Sx5T_qrwrSwZ}Di!8Ct?IPjAl|X=Zp_ErY%xVR z)p|C+cLE}*eB5IjgFN|VU1v%e;ueYH^AvVB9}8)*qXx}{KK+!v>hk#)Y`zuRy=jUE zl8U$tdrAW&q0{iD36^=Y_0zje=LZi30ov2h%g8fQI<9?KzATAJ`w`!vvMhVG&^(?# z!qpxf7EJNpUA)^^3AAfmUfyHI&abqYh~}oW5Lf$ud|T@*_Qjjk?QXl@-XDJXZTa~Z z1eeVGa)qlts&{05spm_D7-{Ni|Jl6Q)dusD+Wjp?9T9jcdsG?$h`be(7lk1l8h^CD zVo5VFGILr2>lMO;=T;A3GNRZWR3e%G<)l!J>Y zt2S>~S36P+rIg=kGi`;Y4-?0FjfK85zNd`BtW8+e#w9;mW<>92m2UlTG8RJjeX)BgOC4R1 zM7-I4U2eP;mnW*P#csI#%VDBGSWHA+lWApdyOWg0m*jaN+iRH&i7EUK@ts93Jm4iD8~;h#Dza3!WxJzB}&!7Wv!!mSZE zCV7TvgF#}%%k9g9Og9?`eb~*`dK$V;E29(ZUA$Uv7Y8q<7V-kh{kB-Y*9l8h*jB|` zCjmXn=DH--gN|1}S$&d!|LE zx<>>}m;s>y+JgUWk0}WmmFbV?3#_DTKUIq4_3anDt)UeL3<8SZ8r3cg?gd9^DMhK0 zrU-_GTg#*gX_DF<6Yz8;Ny@E@sa%NV4ZZ-g+{KgHTBF2g;ewYk8oiN9l=i8`i|32= zt$O~~<>mq1Yt6;Y_Cn-`Dn|O?KQ8oZBHC>>0Y&#ml9wt+E3HPsW2j(Vsw12lBnB@$ z^0?e0aZE$%XS7~Twi1)X8p@m8WC+ysWz?she5b!GpYBh~nqP)T56;w zKoH(Hu$e6lSYyiZYJ*8qjeyQyT#D6qMMQHBjbX!xArq6t`j6w*CdV|%=}K(S+42Va zDgFoMZyI+|L&SjA_anRw6|7b~ZLm0r1V_pyG9NDqR`SsV3i8e32lhxqvVtfVV$sG^ zsMAtrEvkg;uM%#NUt=CA*4sV5frzzYsZ;8U!orvR%r`^cX7GXDd{M2JoK}ieba; zQfo{z)z@Ozu^Fc2Wm;sh^Y}xcrUa)+4G|j`OzkRd@h34!ER0N`iSNsj=Y3OrXUr1Y zN2AtmG!eN);=uQNoz~y(niwox@8KURi%PGF6bE$;M!%Cx9?3w#FVT~{#cXQDl_;SJ z45WP$88s*_d11#jiqMh?Xw8Kee@@m$VL z$3R$j1Yvwp)$)l+rV?13>t$3L-pblMM|2>hE8)G#=yjFOUe&{@J8s2}n8!ycw2(H4 zS;kyDX0nb}4QSq$S-S1IL zs|=)2vfNfe zCDE8`)Z8k}vLUU8J%V)s1$z@A`t10sGq!fxy0G1~ilh4@vFMDRi2F$6l$OSpV4`;~ zJ%=dt7uv#H$z->@_arl3J41im{8KtDm=3ibl!e-(x-+Eae+7qQjz*pHe6hng+h%%fs|jnoZRGHlw?I z#8dz#9%KV+`K^iG>~I{J{!)(g*GIG~_jn3Cf_UW>_Ky~f^z+{?v8uWHE)%_Im!U|H zFE(rBxb5xYt=4+BWULE1D5Ro)n^vF34fXY8R~n>PnOQHzsy)Zjmz}P(6-4h~=d;YQ zM2i6HEKPu#0u7bEh5iVpHT`$qhiYq0WJIS+{YYGh#q6)v3yg;~70a>xR;FGo*Q;+U z+5cGCZ6QPh6jZ9JDahc;l?F~+>80KH&h>89hZ^c~_U!&J$_mW|>#nO&+z};$nGIfIe3_B?H!e51$Fd{(x92ee;w#otBPj_;d-K$aT z5sZ=*Ms0*I^k1|r@3*TB+8)^-FU{1|{(mfY+k$NrceRg8w3H}qc<@NG&8k0&tTU+> z&_*0kugLVf%qjeGCI80k+%NS^-8@ogt~#^lflF6cN9o!@x7?~|WbEkQ0o4|@x)%El zMmvf_{{kM8+Ak&nCdy3hvZ^klp12x{aTOrlrVDH+M<@OprkpWgLYwp&B9hyyGSK}} z*VQ1kHqq>6*g0YhM=QCH56?&vQww>Gz0I;0$%Zvipd-Inr9c&c2{8=_rnRbREV56f z3D8=t4YKSssA{806CMqRRl)uy!m}q`jW1W&X1+xwhy`^t)9-fH_dxIOgH(11o~GT^ zY2JhK%~%*uJ>RZn<2x#qiUe6lK^aDm7n`OLt~;wN=aF61FYtw&Q$l}M%R%avb!oFCWRLrWs5#4T`VMT>f!R~ZHW_loG>G25@QexL_ zYu1Qix^{sv==w=A7Q52W=w91cbBe$Jt>)bR`aLsL>?xH3nCH(6#h30ugz zJj^Abqy01mamUsE)#}Ic?ppSxr9it?Zr6k`JkJr@3&;?YqknOtSWYUGWgNTzZ@=ue z8)Th(x{D!reV66$m}tI#QYmRsz^F$C*9WX##Z-sfL2Z(8{ShJ0rT-eCdhy!kE9&2} zq%K=#QJ7HjK(-7_Bzfz4$wV3Y4jZ3{d5+x}jzQ~!9&_nOUP$?@# z-@Bb8?45Oa222d#%^Kb3bR#?L(cU5N)1BcLd1(xZYX=cImn3ET0sj8!k5l;O@UMR^ zS5sbIAMrZaUza~*U&zfCapXdJeRg4q>c3jI>o0KyCZ=TC5Pzx)uvpDy0bG_;@jM?7w>AL=PsSipnb0%deA&V$&n~WiUmb4o4w=4PC3z_!NJg)3wivX) zo#@BhtZu*R9uV=aJc0mjwtriEUtovY>hWLz$73x+uSbA;yo+a_46=JV196Y5Jtot; zFndO^Sc?|e0XPJNcRaze?60Ksw<*>k8GYws2(`rXSY|JAwFcJdN^7R$DZHtH6+`HubbduW*{l6HQ15S3jO` zPvu%<`^D1+>%g}*M3K&x@_;#1>?(MNg%PV~r5@TXL2Oy0MF@s%=s9XzD%{znCFEQd z8*Vfia8w|+BnZTQO_pohF84!h_&pi$#hyAl*pQ8CVz;`(ti*-P_fF}{)f82(ntVxs zR}mVq$$=W%$CMz?ul&USO-3ZF%bNrtJ6F;_(_55>5=-qfps)u-_txUCl{VyUkKJVG z4`OJC`PVPoJDI#dhf>}x(-8!ol#V{`nW{|az774 z8(|wkqzj}?fD#uO?rwmP7#slA zCIp+l;$5P{`5goy0cqpHP^F8Bp7m$!_d|*KF0t$Fy@y>Nm{~KsuS@)iW-&+CUFu3YmUc zm7*OENkJ~rcPKx7qy$g6O(kB5%lZ`3*}}f59p+c1+b8*db&V$KQC_l-H5q+G5-jMe zJ$ep^DPD-*5{}%f0XIO+C)PGZFj%IMJ}h<&DHLz>NXzQ}PmLsI9?dNzPE{r}d)@tN zv&4Q~J&$yhv9`Hh$ZHryO7jSohH}=sMiN&8iy<#Ou?P~*@LC{8_sc!pWHOl4s8e23 z{rDE|mt&oGbsL4$YaUa>tK;TYYbvXuu9yFeN(}pMO>m3HC>yp>TPk7fD3uGXm`l6& zZ2P0EA$;6?uCAskSeh?Q_UhZW<3Yv*m`%}SK$k9vQN+}i7np1)^Vv$P*ZsoWVHY~Q z1gp|ztLIvM@2l>Ho4Uhw=g%id2P4X-#=09U=o|Vz6zwHq2!frha@@raS+v@1fB2aV z`r56xo2U}~m(~i7Fe+jXBxbT!Y>_Er~q;CA2=vN z7AN$L71TA@J!#8_dPn?3^@}$3YXA4u?f|SLAQSI4jp!pu6Ofo}`YfcL$v~98ZDS!?F32R7j}JABi9|abpoPxiYI%La+APF0F3s+{rARtZy*e25+UT ze#!#!bf!(RISu5cLmUr1X%SsY^`cm`uVRhG)UDSjGMq(+r&c!yV@tG(4_Rks#tyMoMt57H9F zP8g|N%Y|6b^U(EBl)@z@Dot5hGpN=1_S!>&q_(gDn)XxddBx!6TSIZ$3uc>9fbEBs zDGl?QRL`f-(V)z&=%7&=T3v?(`T=LqVC@(s?RtrcN$LN*LMK?D=m)IDAQK6j#n+|& zzS{qF_3-67-W3GXzixk@F41E$UzoEljCSQjYRtuI6UC!+UPUdc_Ir8jyA&D7w zO47R0vycKSb=UNil9M?*k6#L_=3Eu8T{C)b!J36SotJSI6y`h@hK1@?5~#`=s|@_){Z||A#V&Is24UH_ z3i&FsQcWu(=GW8Lnb zUC>qJL3GgBN)dDi)D>O_Etgk&SywC>@H*rQc5$`V8q;p(*amJD$?7irXwFa(NqeO& zl-;~)FD$?mr_~T_w$rA8>hWKp3@B1JrHevJVG8+l>en~)5CNmuIPdn-XAE2k6DKl< z2Mu|6G}Km^A^Z(?I)dYU2yj}ll0~lAa|ADx-`ckq=SfbZI+eS~1d3I#{!^Xvfa>7a z<@eZV;>glu+9* zU`fVr3HJr~D#uDxL zJ&fz-j_NwnZxmOMV95kJIP!YaumUMjGMmM-c`?J;ZwWWOn%HeKt<7+-aKWS8RrQb( z-USJ&)T&zl%G4MpPQ(z7(i{3@N*^XT31-#gn$%*`*V0k0jvL zBU&QOg?Ck_P|e8@D%f{5r=ykv!=6$oq*6^f`jW$rKGjFJ@zMuwmLwUCnEl(4r zsS1l_h@&ly`nXO>LtE@LjA+qQxZshCQd$L9Uw8GvltCrUA5lim1{Bv_uMQYm=zMV- zcx{2O=}fg|0%Nq;?}cA@y+Q_X7(e1qEDXv|dtFU_={pzrf%@wY|7w5#^hf)P<8jdQ z@kFk{KZa6R1`eeBP^kO>Rvx|Wrg^O_KA;AP0Z&D@x>UzjzV%_{aMSXZtKdsjKE<=r zgQ^d!Owu=u^h{0`eb;x>B~zaCV3YeaBLerd#9`qB{`EAy-{3ubz)$)aZZVl878<5_ z|Lh$;^Q@oeRYav6+yvwL@)~(;7L@2wgc^Acfr1<-quUU{OwEaQJtXU0kf0)a)9&&% zL@=d~i@Z#@F;uXT-BM_q06|z656@V&kA0ZnY~X4wLHw3*ncO~Dp=gF8aybl3(^!v6pBS1 z5s^^UVLT|^6B`?0i{jp1ObC`Fy!@7Q{SnKMq6|U+wAR6U*}5;j3BgR796K9*cSHzb zUoY-;5*js=&Ynh_gPx1kp?|Z*J-_g-zFnz2BO9Fu;M%8Y2=C&AliMX{;+3aAuAYu2A&2KWJ{T8)3cG!BK z&G1?_z)moah>!GA@%Wg(kH|YTD_&+9amM`)pwBUYxEG}Wtb{P1o5H9 z4feC);AQ$=Uc{_2(=;wlK@+Bew2b>?K||C>Ib==E+nr3AR!vS-R=h&d^6!0#a3m(Z zJ-T{&4;uBnJmsM{%vE7tt`fe`myIGVr5)Tg4TmP!gczlm??pTrXGV0Hy^r5mg4xRn z8F5;N9@bonLYe$P6o2E~67`3xhWS1hq_fARpw@wLM|%`h0nb-Ec^4WlT*k=&U1Y4g zxkJnBO38*5G`uhrA=3ApuAnINi|keFxUu_PZ(@GdS#jU=9GSQ(Qyyjzej=3~lyf!4YUyuENYwL_?> zWX?0H#=+3HTfmzK%@7Y|yNh6yi8FW6RGrpgWdYWe-^~9OBT|wqVI=@><}?s}2&r>0 zdIw7UFx?LUn8M%Upb!jGqjFVO)t1`OOrmL@P#2f*29ccVt^rV~@A;0o-&a^ckTcum z{xT!wuj%VFYE^Xg+=F<9AJ}l#vN7oT1NMYMr1}D|-_ti;@6iZKeW5$BA8_$k+bkvF z{!>Dc4Xn#xT~Qn8=}K&P(?HrewZv};_amNtok$XFLlm?4A;&=1m?lIpFR(bIODWH6 z{=UAh?xXkjJLofFv02i#aQ`iMtQ_N5PAoA6Sh6+*w#X`}30bA-Nl8_2GiFVI=F-p= zq7D$mWU^lp7HfAl4I?W_{uYpV+n4!9tHi~$slRkOrQC-pPTmdb4dRnaW{?=@;s5iv zoKmf?+dF&JB4bKTv1F+-NDS9a6)NyCl;pRI(`CA{O58YpTys+{#3CDuG*o{`NW3YgKc0Z?}1uT`R!wB0&>BbTmAIc*g0r}&GB;?_2#$mZQndk>z` zmf3y%JIs~Cyf=5b|C>|=SZI{dxZFjzaNyK89YaYf%g5NHBq!$D&`KJ7?`!K` ziFfzP&YL9;YtvuogJ6*l1x}8@!<$3!vZRE;)E9kwWr24~rN4)XGkrmJXUARCK>`s@ zHxC#I#T%znfS9Mb{w|%-paXypOEm6io~>3PIz>Jr+d7^YmrkPafZnB;+p4M zJcfsIZ%S`Vwk4^<)SLF=oS<|MvbtfS$k%#2n(tG=&ysFFA4n6f8aOQAH z6=U)<@ZQMCPS*3`=Vn?mBUqeFYOjKybBl-+y`{6`*cWdJgY?0F4ENHik5PL-b_4I*O%#o2dE^wv;I?qmGEes8w2R4;SwQ$-lHi` zNousIhx-?Y*{tIKH`_?c6Jthdk9Ca6hyTY?7p{=k>y7ZQ?dhnAO)|~z{E0IV2fUAsnW%0r1AEYQ~ zqpB;o&8!#+E@+w9(i(V-!A%Ors@NVcGyD5+*fWV)$#^-m`q?cXSyZ`oQAwE&6SEr6cYI%UmQ`R{?G{ieHx1r z7s!Hs%0Z`-aABW$(5u&27m5j|iwRUAwvU*y!n;Mc=t$Rxi-<>cf(Zb55kvt+Ii&HQ zI!Nf1Ec6@4Z=nrlxWI#GeimpDKRYmu_h zWwR&M?;?6%kHi79ao9j;88X+cIa2vm3U^0c;lQcQQrw7<6@KZ|oc$8gHk&OzHYW4$ z^yfY%T?)>(fZ0K5mR)U7u%$u4<>;ZvhkxVD2vYwhCI0LtV!?-e_gRT zSw=-`=blZ6D)@w)A6#fxzDjKuA|ju*E7RSQQ7v6QNA1TsPC@$1=Hm(hcm+XnV4k!R zgn6*y<>~zd;XxAC8B1oLb^>xM3Ua9->lYt>iA{ezn(hbWCJ=!=gxu!a2DuRtv6GP7 ze48ORA|iGka+_~UyD^dD3!$iSPwZ zN7ARBvSLqJU)xHAY#_3{>DIA$8*l=eU#Alikd5;GwC5CyIldpEs>>&kHvu?`0&y{> zI)2;84i6QV*{~dp1FTRx$Y;Qg@eLmh)p=~pnqwCQbQzr&4b}Qcf60uN{;2vfxP?ml z`tY=N=DQ$qVFY0h5Dxy*XYGQfSJsBd)XDf~SeDa!>sMo(X5n40s|=MFQ{_Moy;B<^ z+0nT()Dm)hmsPh4B*Er5NlHI&VVC-4(DuXi)!AHzxn4bNbjoH^pO9-dd`!dk&06C( z1^UN9E6TuxEbw9|B3-!n+^4$K!SOwz>+0QU%_~%3Z`;4S*RuF1FVIR%Jt$EIH&&GK zuCH8(1tlFTaoSQ_sVim>D7L@@}u%KPjW3krW$Yh zwwT}ws2UDQ9F$$ui)ytOq;(ZcZ&scSA8FEn+O_f&^|ho`zJeteDiXaz>S&++K^H@C zC1ZXs6c_t0i*#F^3(1mc99`|iENQ8*wT_!ol^nBh!N0=^G;$kPyFj}j@gdZu$6mP4 zq(TcB;$0FeSSv1daj=l!Rkc;+QS*<38^RGKMcH|E zT$W2~_M%$)_tmD|uRsmTls6Yv3~USG$HV=aA4P z(9#x%^q`QVHkH#<;Z6DA~ z;@zw_+s)Rm^rvP=k8y_}bQEPXJF%@1$u1t>T-(7l7>P?<{m2b!0(=Pd568!iSy@9KH{V=cYb{xWx!Vo4I_B6GFC(fy_#cm(V zdnIr4{J!0N#e=TCqN){WVwz&%!&--Xh;%yJzKTbTV@&J@{JWj&Nn%#UF=ieuEK{8F zbk;lUp3CYT_sd|bd^XtR4!(cPqnDP41dr%0L3KeJu}4T6aY9pP^Zxz9Ueg?m64OWd zdd7?ht?W*+xHD#rtp~K0$R4K;LI6?dPAV5e@8+6zG zYm}rfGO=QJ8AByQH~7TG6k8SBgb2oCUT=?hU!+Maf+|#a_SJTcX`BY#o2ppvah${A zVTt#a{ZmbT&nCRWqXNHQJzyY)PemTSGLg$~%8?E?PL2mGlI_`?ACa}W8KKHv|3>_h%*AMl4E(!ceP|JDa! zdh#!o6ZDz>70)EjjB*$CrTTsdoZRjeKvg+RG8k6 zs=CGt(iqoXKA^r<=IvY5`zT?ac{RbsG6}Eqw_Q)i?M}74fG6uCa_S8_+nw=74)xV; zcJ(w^p}SpzC&hl;lrM2aKBih&B2@sEk*ZU#wA9xU9V5k+^xL)DuZT9J%9nPDqLylV z8olW?PL##y4&>XJcDSyGC6cI*B;@D&FHd`HZCY!+A^|cw#4cGq4^q7#pXZP*SJLX) zSYHNz7&0v}tU=;ZHsydn`CX7;ST{Cb{8pxEtqX|Fu$n3cTe0vjca`IjzAr5VV?;eA zYmedN113F}yEa_oU&{~+!yX?G%1?a-312{%e&V5IUO;C!99V*oTkwyya2#+aqv7HJ zc^)ep_6tB8AFAr)Gl!Dl>*a$UD5)1v4QRYO-eM$)Y?+t*PU?vt@@eA8YGRJ;+l(2G zb$~i`%wL=;lL6C(=Heiyb?T9Vaw+$ctx}pPEEAZ;6D+s4Hz)LYbrQYJs71#6i^p4; z`@BEen-KQ!EA-iPq%DiWHb?(n_NjqXnVrBa%|c6vy#IJW&)5`*a|j_S_n1DH-WQco zgq3Ll^`A!8-ETT4hQ#nJK--o0CE{C%Jh~p48V4@w`w4QOBtBiL)25&h}7S&j}Oq9wSH5ubycEwWdkY9m0&oivHp5EJoZs;a14_J zk#2K89kys}jnq0+H%?*o`wP2VMU*Wz^)6~4cXCL0Rg9{0#{sd?)AS7yzTTzd}m?)riT6jC*2rIp*8 zIxwm78OKIkcwVet)Wl3?82#!Uv6Eo2e)?bNM%krG%P+3>dwkTTP67A)b@e&0ou$#Q zG%Z!*SFR)@suFY`j*mWhG7*J_05^~`z9z>>>)^Mn0;LRD`Oy-tk7g(Ksix{{k&^A_ zvm7a)2@nL%Za~vxZ~w+`30IH&4g1%c_Epb9_D+WsP>~T9e||-cf!Crw?yL%KEO=#Q zd+-lpdra%Q;Ks7fsORL%9VHg3&qw)AoYU#gZ#BI=wiJ21*W+~RGf{)2*cfEV66ZNb zjg{FX9=bpBwLNp&%Q*WJ&9CQ2y=+#m5rmeXk2QO*RRFc!Gf+}6$4|aVDS>cV%i9^dfBgrYWH5*6KpDxz0`+TNfw`RWL7 zNu|K-Py?gHe0+S^VZW9eaAv!l#Bmkhx5ao6FQ4H6+x>olPt<80X=4{;M4@5Awd=R4 zH6_q0`6^JqzbF-d3DZuMooouDRhleJx?EK_QosC;;iQaMOIm2$5BTPj><`~xpu)yy z#B734W)35=E@$b(=*jC5yw;CEpn8yA`82jd#J#8&2HY|XnuhfBw5rt6hyKzE-s%+Z zJ8H1!t6C*^a8-z7<@fk3mdY63JR>w*vg*FWTMAQ{Ja;&?F*q|UO#8NVBA&2@TQ90O z+5l$I7if!Kh>59c?JVlm3{ zRkdaofZZ+7+VZZ$DPdYU;r4oFifmm9HR}%8cN;5e#w}oy+hm#b`*2Qpu+{_5o!}7V0sa z*54mDsQR>ntCvwbYHwe#5ujFN_xoyl#0m0VhYG^y5xk9tAnPu~BrJiVN|x{cqAOw; zFS=88p})-L9VG9@T**l`_t^4l6G3rdQ14)RL695ar#@5j=vLB7-d|&^2QSFc+oe?b zdbo54y{5+V)QP~)G7QiTV~TLKlnKXju9h3DQ187}iVS(LRMe`qloAZp=o!(^vTyS4<+wAdXUKxo2>mB4C&CS6h5t8Xo8M!6F7%qTa8>sc?a0hY@re}gHbe&I zRr8{r^a>z;+d(EaI|)-NbW_tnm_DK(o3zFN_PW6h$}P8R$*y}UE4WG(u=Cy2^Ug@$1-zWfG~f&{#plA zF?2;%yP8YP@uNGZO`YEk6iCY{(Y@&zQ$Y@gW=W@-;;A0sJ)D>IV)?LJE~P%*U@Zo- zgE$UEOgfFOJs@u*Wr$|ehvHvMkRQ-x!=|b|deA;r*G@wO3gcF{BP%Fjx|I`qi}qL= z5tl}gE3ERe?Kk-Nc!yh9*I*Aq^~IRd7D>oAY+5u=AUSkA+)3Wl()l`h2x^c=)=$dd z^i8OQVkGh@jHJWX+xjrUX_ML5XpRUB_+*xTT7h;LVz646PhCo4jbE4VaU@Mpx zEm_qI5r_>!yv8TP1E3 zU+;}-Gv0>@PFJ2puQrVIVV^uFw@4g$p;Q~?s647Ev&{Bd4&<{hX{f^Q@1@uCovR7p zmjMX+ynIK;s)ht3BuqYZXq^VEOJJAvF80ewNBJ*Rreyi~zx@|kQcqG*z@L46T02%& zIm=dR{S}3~zQ79*4{(^9P%bw(P2du1ByxJKkJioQGR)JV47LD1Ua~)Wp-ZBume*SceaY7g79efW)=_*bIs{Skt59ltd zt3a)VORMYhwpo&>p}%bp=#i|yMu{((3BTZz#?I1ugp z>A$+ThP;aP&;JJNB}QGP3oE*5t6G&(QXB-0T%*1u*Uf_&R3`R6pBv{bca3~w+wit!$cN3QA;z_u?SA#vE z)}zx&MtpKg6XF($)6@-`O5=N-jRZzk;???cy?Dd~t-i6Rd6C9gYp~`@A6-K55^u<@ zQAJ|YdT_n2CPSi^1Um7#aG490o}-19qFV>XZ7l3B45?*VcV?NjiK51`#p#H(9_bLP z>eK_~4Oyb@o$g1Foaj7U(4PF?^ohCWG?$#Y!iKKw2=>WPrX0<#^?0#vKVmw zC|c0_&VzPO@u3lEaF+}m_Zg8+&SHxa&W3$&dx&r%7v`|JiJuR6owG4P#ajA{#Mt2L@nS#2E{*O2E4RWpi zMB@Gom>07xKe7dAfsH*_!<3E^kY3Trt+fW2g6dgGg#L=d36F~h>Bh$}xeAq}0^w`v zkx{#p+_X`f9hhP+)v)GE_BkPsrKe$iyS?=FWEfp`6BM~U`JAtL%f%+oR6ObPqEif6 zhQPOZZEd*=##Zlpe8N}q(pqG;$13c1vKK*GrHgkv$XORPti6xw$z&GOlV(#aKJ?_% zlGm~v1OfR70l%D*DM05mr%P`~Ep3fkoK!kXWk%1BrU$L*%9&R2SizR%CA8z9a~YKeGbt z^aWovabNB)?DaEzp+~%ku>hpK7cJ}lGToGe^F;8@kgf@NgQ3Clrjyp|aIFSh-Md+C z>@^T<)pnC05L)t#MRgY&0WTjg10&!6j3J&tjpa&0@?a6DO793*7Otz-(en#% z8ygRfMQ(pGUj(o)X9r7fm{a;%Re*Ml+e&s{JgDoZr^ITHH)#hCT;Ac~JyIPmFNXZ=1CT8OX?5QkgEFu8K|iaJsJ*qXI*h5XBrnOG7%P zP+f~1Jy4u(@F-g=weMPAi~ap#zn9t*{+;FuX`t=odX2R}*%+$FP)6rfCIzcd@rvj- zdX|GxAM@fJ*52(G5>-B{G_2R|GE>FN^Vipsdl~F44(N%Zz58;xe%PXK@Ws9pkeG0* zVkytuK9ptK8h%;(5u)lY6z;-vBVDt<-K(Vmpd)*~-5iX++>87cm@tl~3 zxACQH^!apo#DHq29~_B|5*AdlS%GZ>hQ7d2h1!iYiFd!F7bh$BmPQTQ9@lu6`+x?E zgv#bT66|N>$13N@rnGcGDEE*Jil{rMosRY_-W#~QdDSHQU51?1vq+$B(_v;Yzdj%yF;L}gQ4tcoV4d7YO0dFeMWM7^kH=uX6-C}?$WQ6 zHHa}WuZ6uB$|G+_P_tteQj{Iv!;QRS6$8Qw@OG=y&TWZn?0$QHp1pODraQci{n8Bp za+Fizn}kXc)3UR?LuNhg7N%Wp><=wMXwCZBzmu7oHp#Y?)s*qct+MI%Ek?o^ej`s9 zcZ}J}!0p!1Dx)4DdW!gP?fOYKEQOmccS~K@@;MSyx5ttqq*HEVf?9K1@#rfii7ZnE zZHiu|w@dWZyw_X#z^qbVN(sjz=j}9%L^EzvHVs~Tic#72m9Ra9)I?1cD*=W5>Y2O~ zceRm%fNx02^yG9ie_2A`H?flvpDktLr!MBw4Ty2B56*WB_o5A(|9{TjwKt9=#{$(a zYK-ma-5H>Ba}FQz$jp*vfo`jk!FV$m z43fbp1^!b8fvmjOc*$8Q=Pq3Raot->2dXN;p!ai;2$w?KA zo{pnnA>BY}abQRWS!z3Sfp?6Fnm4#o-q3q6)&l~Om?n*Fm`vgPLK!N4rTot0 zlZt|gPQaO03tSmFo@=_ia^*wojhiZ9fYOb%HWzn?W|Q{hX|BaQg3F69W_KAQ{=QsW zKsTJFKbs=OH&j2-Se9$Ny|TRcMwacKuokYSPjzaTL6VkO>b1<5O58J3PWxD)K2v6; z37~Td$pUEe!%LPRWamNbNZAs@4>Ze7+gfNSqa^W>z7@vXOe+Jw5pL9U7qs=sno{dL z%1J`=-IP8u*?>HDx#RaZ;p54xKFu|T7=OR>#YxJXdv36@jp>Qex3NzsthdUBI_ovg z6tYe>ApBqL<_q?*@#&{V$5QNwDhdLJIlW@wrR)+N$xyTFtjc(MmFV$$NzvT4rJp6A zc?$RGUQUnZ9gEX@kV2tiE>_6X5FG09?$UAn1`oudwTQV%Rm8*w&0%PB`Pe%U)Lo2o z)6J+o-K*j-sIl7COR=0X7+&}U3WBqxmrz&T6!si0l`~G`h(c9g9Ow##Xmyb;^f?Qj z)i*Sg;2%1DZm5BmHbrepc0#}e_&xT_EpdZ>U z7)qCb9FbD`?|TksoA$Lj3snpOFIO>oj7m@=0eN`_ozErx)p31;y2{K%$Pz1rvx*tF zV%cXi1@s-LQou2cc1z~_4Nv0GY{R*2!T1Q?${u4b`RoE`b(Dajea+{giOuF?QMX7g zJ6#qWNvZi8r^MD*?zWt6KCUhy!w>RXK&53IQ;XZreN^P_qdQ1eWyC*2OVXvycYw*1 zkfys=rH*^}X(O@BCS&zQ{X?^_YNl7r*kyOkcIyr0nDF*6&tX?`C2JApLRipw7B$}m zPZ>Ag6QcguRaB&J7*zh@LN>XTP80jmFpWNEQhbxJUHj-V!{^FSs)88A&8>1?0rip0 z_})$>2*<(075ti}fRWb{>AMRD3PVkmac)4-1Mr$!upNUZx`8MMP}ksghzZzjtFY3< zlUz@VuvcaeKsh&lz-K2wB-|A%{Rx|f)<}R>7pBon^*Mw03c)p7Po%04OFY*TksQA! zNw?>!*7PlKQHo9;@Y=<#Adjk0x)ORDXgyzlyYKVYsm+^*RVy4Pw5qU^Rc|9!c%mC{ zZ#H)p2|-8pC3k{CReCKUa5F9E_E+}|Y!CS=g-@fd7R!5)I5rYZLB5(g4$v0kIKG4? z3W44uv_ezOfK|b?@rgB2#(yt&aS0>!`XY^tC9yUGN=xZhQo(tUaMzsD_o9?j_z2B3 zsgG1gttwu(-@w`#m|c<0F&=!fqHG(iJqRw}i!FSD85?J4_?E@tvDz!?$y-hx>x?YRGx)-~Z{E3=|OrV~5$hyby%FIvKoAZJ24fRnq zu}z3B%*Q!Rxfa^A03%M%#qJKKP--GIv7}msGQ(LA71IMsM^qtj5j#~EsM(ajP8xbo z=;_YbQIkqvfSH1vcY~^=5Cq+j(PQs|a8R)Xn(47WGfh^qiR2+{8VK>A{#cvb<3;seqms z6gPYp!(fl}$BTNd8Q;P}4ht_ln>lK+`GEuS@+Ou-7<=M|ACMwL*_q_?Qa)XVG7W$W zyvTQWyW*QT_~kSn#{I$ck4R{2D&u6#foDzfca!%77!*;OggwN2;PZAEcPUDWT4t*& zGgi(Z&VVxsRKG@~aq3?@r(|lgh!f(6w$$DLwkxdwNtIjMPLwh&{TN#m4j?PzJ_ zW_o&eMy+#dse7oF1NbNG?)WMu+EU)-u@V?qnqj8w3kZil78H~DYL5z`{=TU8Pc~SH zSU(EUjKJB*(0)m)%OfVjl{}zDWJ9?BsCxvV73Lvj>6fl2lMW6qbY$V)fVp4@H2!2h znN<0~ALmR+7KcO@n{w^zUWp`V~`D6Y+>KF~7=(X}fWd(`g8JwVkt z5Y%Cg1^C8=k!Q@AFzP?vtZo@M113(aI=K7b_jgFRi~$1y#P%-9b$<8J8|+`vw&P?w6r|aOPn#dO zU_$y1p2*)E+~MFq5w|FzuW(2HZkd{lbKJ_*>rzv2gOMbl9&EFCZ+>95FTNdEFYvE_ zqgAh#U+%Bz$Otx}z*qQLo1yUuxo*Yy{B24tAX#24p)!LV2xP!J|1mH|;ABM_6%2v# zkKIH3Ji~zz52fu^H`s0oq%Ty&d33N#thGTFFSeUQAOD1_jI!e&bb!^#Q45m`MHSqd zsO^Y^(_M(Z#xAfjQ=~o{c+p59JaNMnD|ZVntSAO=SGalVGY|d)zMOLqP)G`3qFov) zre!RbvP~lgg$&ia5HF-sufJ`NFkDdtIb8nm9cjJvXOiF<6q{9L*URX14ns@qml!*~ ze&6C@5&!!qOtsq|8Z@!b)7?2vkUw1Z1%m=(8cglTEVDRF1R7qRAQP0s86Z+UVM;Ix z*zkN`6)6+nplO3Mc>3%930ubn?l+Pye##+LW|H9`5@~*f!g1#atOoNo-!YM7@>VMI{41S*terMM_^0Q72pLp@w2{|t%KLY#o~7P z;Y>xJ;XV<& zZ;!&5#%pT4-FqNI^~n`!)gy8uc;o~MfoI5tOyHau=6L;ZzJgzA^uo^`B$?Z{X3NR6 z>=VOW=s@8Tzkf;C&^S5Z)8?Z2#A(D+`U!K1+t{G;aCS^WHz3Ez0XeCbPHx5C6Fh+x z$z7?$=K8bJBR|^L)XF!w8CTNyoPS6n0+BAiKl`gT^!R;^otuj{8CdzSr1IR!fU^Sa zHYb}uR>M7BnYz2bTq)463-DeS8>W_f0gH>%vwxB}_)MFATLQN9`=184p9I(UYQi93 z^1Yu@q1Yc7;Pvw{fEHOBHYkQEEM8_Y!i2DjM;6kb*dm>R!qS-(g-{Qk+2$<4?^6CcDp+V8J za?4Z11A*5Y75UuM(AvW8@KG-{vK6jdBB5|UHaibNh0Ken&Htch*ztCY`(KG8j>Nke zqDwu4xdfxp%hDAZWMjm&HKl;{MersH_7!(_UO&hKq(5fkA|Z>GEKQEA8Kx zGl{0oYKjEG@u1kk`~cblNh)=#AxGkgs~VyRN;SaBw#6^9ZSl4EuCcy+kQgi5JIJyp za%f~R3x;(##PfRg9bYHI=@o7Sz)I&FK0jHRm@n=mV!!coVhb^xXgb&W2?A|%kgOy1 zR5G>xw!9D_c|k}AaLZpqh=Z%il&ixUgk@Q$i^ zYHC1`0~MOOnF*|qP@GG39j{slxpcVu@zUFAtGELqUn<`{kcoj!I*ROvH z6OBZtR8}yFP~ZE-7z$F9lRlsBKI?#{=Hxq!kXE48W}N00t`lsRXwKi(gjXvB>fw=x zWFA`~FX&h@#K7c4>|C8yPek@W^Bh(is)1+Wlm>qX*W}1=u+hbG7OqKr*bH|MvXWIo zQibR#cFv;f^*F3Dd&+o8g2piS;&&mHY>&5hvqIL#5vA{`#h)= zGhvWaw8kdeXMQwjF}{3STj#lIWgViK9twjRMWYvMm-@pfQCKBvc7>oV08+j8s_E(^ zt|PWn+!yFbe3F*K)F;yip&rIQy*`N?>M;-%!wnIrb|LL(S$Ifebc`Xb%%V1`?$zdz zLiln<<(1=!?fIrR;a6)KPn&R|H!Pq^c0SN1gKgvt#&JwrxY4U*V!q`7sS0N>c?9!O zRE}VXZ?))Wy=qh3o`wRU0g*M%X^F>>*gGXwo`ccghVlYpZZs;kEf;Wm@UgV4%^QLV z;*2_@GDqwbJZb1;4^TgPwuMVcS-(n%18@iC=4;!5Hh`k=JcJkpLa+KWQ8a|IfBFN! zMkWKA@VaUmzo!H;+ccVa!y+23pwHp~6GEGI)cI75sqZf)VsPh=aaPuv?=%z)tQ=D* z47r$f>ixyY$K|+RPNF=86ZysJ(MUHN3VL+}B>*4Hygbm}6UjInZ`GDCZ#z?PWrLEq zzcgS$dqwtuLC!ta`%RetWcd8shXeL3u3b4t?`XlHP6(u?clX+xD!iH=@*!%Q&BTKG zDu1IeFme40xh27PsDjP}Il`(8mk-Q$NWIix>4L9D_49>UPcLKqgfxa?t(dQ#&kvV7 zROPWgSrOjo*wXYV>AM#QDO;OQ{OZDsFflzoj-l>U1nU)?ilDDnDUsNGuv)txdttO= zxSZ60H~A_VD&g{UsKjb0tTzMI`V>NAe}cN?5!8yt})owbJn0*2})INLZoZ=cev}{Ia8*u zh)G15v_jQQfgHVoWnVHf;PfkCovuLgNw&6xPeAPzc<>v~>Bg*iczeOa+rZ|>hHfp; zrxJqU+6W^Yl#J}m(n8qb!a1d%LQ%#pu;3~x_sz_YmcH}(7kMbO^Xd}i75>mE>0}dx z0RNJBSwNJ4X;GU%g{YsCb71x}Yblc37(`@z8JlZmuJetrjs9q2MA5h{zoG{RdPuX$A4pE_+#NV`KEm*K%#9huF} zXSe)tWf>JMaL@fWwv$|zPfovEYygwG$KEr=c7Lf@uAHLu+=7MfmI2jpXv3uN8A0BM zq|MS|(@r6h3*imd4xLau!8!rOBdZfoJm5M3{Tp^@*=XZwn)0DIx=?6v-&&OSw+jMo zQ!n3E-}o}uEHnBxMRPlF$Je^X0`}@$2UwMg;e!Ea^?k7hiI(w+*x5KhB#=YxFOpM{ zj<2?ybEz*G?pMFW-bF4J$s9q|cGwEGn+I2GRA599kvM2FH?%HS;JY{*)zGPCs`!%N z8Lboxf|IQ`V6<3wfZi>1Tgp;ElP&?uV4nF9*ztl2dq2561*bGD#lB9bd{Mt$8MQFq=&CIwD8T$IvBAcFl1nrZckd%Qm7vAtUHyxgFw= z$qLZ-DJl`agKmnuT16waHt%AdebjWyj5b2`6--mj$EN)qeu{8w<7MbkycjB zC@H$$u6CBEkXhAC09N%Pq8K;O7CXPl(#X+(TL?YdYccLd{0zv^Z71ToN0Br!dR4~R z&SldI>&nud;_O0W;cO_j8u6qafTNa%dVa%N8s+lbn zdVz<{r<=m$=h(jYCF8aAeaU#zeP1%9fPKk)bA3=I^pjc+&BW_ z0Dp@u!~e+11o}?)bvb;4cZV9}5+Um|o8Z*u?AtdeqcmMr!TZ<#E-bEtE_Pia^oD`s z(^GSJ3a5qg_ii;nwjA(_muaX4hwu_pjVir`*?tq$3%mED!lzq1C|I%t=8~Dx;mfyo zTT*X)Gs};I)sL`k!GNm~I)o{`_oLNJh&J zo$N_TaP-mOPEiEDz!;8!7{4(vi_NpE3c&I=+!l=CF)#_1h=0w)Y(kn&SClMt>~E#t zePTl^LG8-cOk|VN%w&^d9Ly)INkK2FMZgMv#i;injA0M5w^LobkqoxEd+{7ADufA- z8nf_wZw&KDZ|~-qwkHk*pjAd;gM~8G$egkhY@4o z!lH<{r7#Pwc-b}gIP{EO zz#SJ`L^y?;r{N9Lh*=sWSJ}d?7uIu*ZoZI2{UZ4@b}imZW18tHm0*KNtyF%tIg6Xj z3+1I*ZR@cyPA;~a@(g+NDmlb62CR!zwKFYf zwff==6xDDI;MHyHj_gGQ>et6PLzokq(_LmzaG$pzrHuGuxk|mzmKw-+YpLz)--*gy znl)m3Z~S##JmARgNs4hKnhy&Hi=Wll@iRu5zh}phiE!$k7)PeWI9d_aI1-TK$d4Sy z?B>H6M&t~tYCBaE>FH_e&)k&c9E@w2N#bxfNh-gE5XTnZV*C=6x8|k8<31UFo zLtk20gjQELKiS9f4%eIS7R`s*>4;l%FrhRQEXYK21aGPzS`c?L7B8MDgE&O-wl+2x zG(QE#X=@6sbHca~ofO7kofXDloj}H5P&=z^daptEwq!!gM1w70Uxa`Y0+Tb3@M7Z4d+K{58VSz_P$diJeuTpZ>+_ zW1~92)iP+H^qhkI#s+x{4gZ6Ei9(S(piv<2GT2(ml6WG7)51h0Y?ht7=hHF%>6|Z> z(0i;?_gR3J#@!_bcezeCBP2A4=Cu&6DA(9{np+n@Hygd%(DAgSJVVfQ{YZ7shwbe> z95$AM!XL^%o4Qb#7vosb$}EMH6K%2IR()0)P6&a-7}Q?o(SwIai8hmCh(r#4qxV7Q z8pScjnwd$#axpis7&xMX4~&7}0(7=vx=}VX(eAS-_n6x z1ZxiZa9_k|U+5+i6m3^a__;6$1+H+I@Gb?VX(GTBaYxH;O{HJ0kH9JH^8?KD4IImo zwt8S#1p5?FEPmc5?{=(OZ4;TUf%YXen}ffMXtHl2Ztov zdfpuTiOVh2IKz<%XDq&_#B6r>r)APtjtz}$Kn_7T6O1G*9+}a5ooY;e|0@3DF?7r8 zXam;(uJl5gQGi?+xYgZ*i8}#CE9o~AR~lerySs<3#G}N+-Qt$6wvkNPIfF$0juil= zM*leNs(VU_;Fu>jRPC@4+J|n zT*DVJ`VRvLT(%Q@#QPKQ1&Ss>xpcRNKj1$Q@d8o&G+vx@KIVs&!Mk?($+`0jz~I3P zc(_;~A48xQ3olWYuT$KRgx?DSM=$M>ON`p>0%0W1&jJEN!$(|7UEyF0Xuf%qzctjp zEnu|Xtd?u@G$R263%5x>AX12Yt+n*IaLt*@20)}q*Xqr3{RUV5<8>5o0JzdUeLk&b zb}&oihhvp!Lnz)pQ2n1LZ|wJffiJ<&iyvf@wm*k-5&IS?JVlNgIM&aZ)?vbu#xf?r zZ0Sqfp4i@inrOpu1x2q`wBybMrZcP_pWwf$H(stp3IA=YK-GCX6Ijvtc3x3sIwswE z4it+!G=ZK_<>T%Vl~78^k+Q3Bt&s)`U^EKmVH@|LOLpx`lO?lvVA-mz&V|Qb5ga?x6sAHZfJN|`2G!NEWc6) zzY4Vf@XI|ppT<(RlJd7m{zf0v$_AWMoek%!-Ref z<1t0VVEPQQ;Y`(}QZJUB`H;ox%M`oQrR&H-!z+h=j3VO~I%pbQFcg1GQljca%Q(O0 zm&bzblAZ?$3E9#={fMmkS+`jj`Etz~n~M~xAMW9vZc+#3&`~3>9?a5Va_D?IXmNTV zZ5i%K{8bcb0~p@EYTKSXkWc1oGviW@IvZd{wfWXVR@vKooPPGEo3W)DZ+1Y!9t>yK zAQzuUxCP+4p%k2sY)r{|Z~iOx+jlR=(qq9Bzj37!ak0=ePYWMu|4f0pVC2X;iW+%A z%Q$6dTS=azvX7Q{I{`d_kvh0Xa)T*fu%B$dE%OR%)soJjV_R(sqF}#^9ZWQ6mxdtcKm~ejI^kS&75Q_B*)zW=V4>0)Kp6pM(?-WBtw+H~xP};+)et+r z1|b!j3XJqkh|@7fnu6HYaq@Xaq>i*O?93G%>5~OVVkx#ab@j#e=}obHVktHaJ@hU! zlTcss1WHr*MEXSec}IlvH(N!6HC;=C(o{qOcT>bf1yyQaaFtXFw83x^(L+ytp<|4u z;Bj&(d~As83m;=Ng^!a<;fS*H@%)qx3=`Y z%#Rot2T-EAK?9#F`X zkGFkw&xb#B)W)U?qW-?5Fc!k5;&4z1tsbBJ^Xua72=CfhH(vgx|3#BA6VYE0a zx5h@L)84kPYb@d!%Ycg12e<0YGpG?~QlL_tNpXeiL}?J#r}I2VD`!%0W|&DqG~*qk zljLLW6$7x8xX+nXle`4n$*H{`aybTILsbg!Xaoog%O=5ZKvk;?00eb zQ(<5u+p6Bx3Y96yLh12jO?&dSfCEJciZlg^niBZ8Zt zs7e(D<=GUs*zE=Q`#5EH8n_XTJ&2Pf0oVUznY10|nqpjTHuw z)+4?HT~~7G^ml!|qn(5*O^2HP!r_pWaCLV_+X>q)`%YK6^+_h&rcs2O$7a7g&@!x0 z1|eJH`T;3sKgM+~d`7y&#+-bWeH6usEli~{Z-b7g&-wk(Gi)2FVkX7>rO6cLto#in zPX30EZ2orNXJ6u`)xnNMeTl<3D$isg*J_WobYY7W8|oX_L}8TBXyX$zXuG8fqZ4rl zK@*K+G!M|dRbQ|e@(xT=C$f4oETS0KWUW;qy*L8b#&E!e<$x|t%b7^jk9as|z`zW3 zAwSfM#1Ja4+f+k|c{oT*K|K%K*K z`3-aXKji9aaPGWfbLESZiA$YuRF-v{+81H;Fyh$}c4Y$~MhP_<^-!QN`k}N;bet9n zX+J$fc441sy0Npm2dpFSnEJ)R>?D%D7xL-8yh zkz0Be*?w`BF`X-_InPJ2?Z zjqOPVC-BsXVbmCvUd>@VUPkHNTp6W@a~KyOJMm(~-sf!)Q6<(O96}BUi3EzEP{5>A zeBso=KZ^bUgBh1O<)|DJ1t)rKafy@s~+Yn5sF#3kBG#Nf)nOxIZ z0ZcuQjMU4HfY(?o(%9(*0{1Xm+vaq$)ksBA8~t*r>WY~C+^oB$OUCV^_FLB59Ru(cKI@D_8f^_tmO`> zZ?CV&Tcl2msEJjQs!B7WPEqN^?)A)d4bc?gxt5`UxqhL6foVL}HkS7s9N|fL|8D1p zW`=ApXhEb5@G)BFAos(hhBMEFo*|+%GM&Oah)nJxkxH#TnR*I06;rn>wfbag7^tdD ztZV{IZ2CKKv-*9vG>vKls4?m)11Vs{3zPfq!xjW>XN(?pzWt}qGZ(SBwyc(yviH$z z?zOz|=n@uqM)9UOJc3A%Wdm8F3}F@|_VX4b2hT`)lacOpkaYfr%>(LS+;KO(D=hx{grr;`>_~Leg*k%! zCtD1P<4rs%Czw=!Gu@TZkb2!26?13W3u4cwLc%?hM#i%ZG|4Ge^=KF?YJktcNLH5e zuro4M$8hK8X38NUn0J@XgqLm)vi+|ZhcJ-oWQid+?N$0$;XD;XZ2P#Jy(Ga6V59cs z!Z1`W7>Cmf(Lk{gR0aDehRW(FK*=)>6`~|Gv3f;@S>^^Yj3+zW#-L;pqzSQ z3((;gg%(;}{Blx&zn%ZbFC-n#OGStL?C4M!>07Q0z^Lhv7(wJXXF$Dq^Uq7X!xI4+ zNf~J;f+w6RG3$nQXnvb&tn9Qx>g{SSIeS^Ws*htCm%Og+<2RggmuuKhgQz1-PaUS+ z%GZk}o?^+#&;q6nwQXHyCP6EBy2n)zaWB$=NH$9^U*maMG`aN(ie$w=Dj6Va{b@d4kwC zo|)PG@gnaE@9zBF5MGPrf(oyw(wb~Z>pu|Q&D0;*K7`%DNcPI?KWyj`sK2MgAn|B!>rZWslw6h%!rB;Nm zZ?r71^=&o{N|8D$4A|A04{mOqm+~tuV0bW%B5;Mftbb5i&%+4JCs%rNg_{9U;sRw+`WPtjcHB{gEdY76% zn}C>3Aot?S%`xBS=Z#Zn)*ip>zPUL#nvPGVG7IFjBFHv?cAM6XdH3;dQ)0OY1KYF- zn6X4m==6>r$Zk^g+IG5z)O!$Z9LKpr!8|v7Y|P6bed4Gm=Gm0O1n~Un1Q7gm0{J`n z@Te&HVb`KwX;CqZa!9f22dF$(8(#k@13Lo-jw~J%s-tt>H&UW&*SyEtG)f|jd!6Vb zu?bk%Fycg+Ja}*k(aTDBXMM5Ud^J1Ht#(TcDtC8evBOs8mrXf6ofC|1nM~Q&hXje9 zJ=|nZ3Z;`NEDQtB(XIJOyKVLV0e!1$kCurM2^`yzD;XwF}ewn09z)296$_^Hms3 zcU0xt@UbsAnCI41!Z+4`EO&Y`)OPWYhu6;x#wdb3 zDgcJ71zd^m`AxTIh_?%yKiTYT;;55Ypt5vT=5TDC#psImm5=AD*{d4B2zTF*KW+~2 zQ#x`i3n(<)omW0W_~dnJC79Px%YMtz635{XWQHy>NQG2*%{)#+V}TY-u{ce!!UClN zJioXC{Shx&jH2V5H!$qBk0Lnc$eDmHhW;#if1q%o_vMi;vBBy)#Tpsbw5p9qu~;GJ z9YO#~kqONxkVM97bD%P%iGtFOk^+|GE)UfgeAbPVGf=wa7@<+(pqv`{qP!MMQ(&tu-9dB%2aD=-hV z3a&{O!wja{q1EWO370W;1-o;W!Ec-xn{V*w<{N|6e1rEj-{f%(KulCR#L_1XJ4!Y= z+UaoAW{tG1WK)+eFKv{~DW`{V*kWYv244;uU-wpPfTb~-7rj0XM?oJqt6>EEbFl*t z1*gZ}=&e4&QR!Pc#6fM*^a&zfX;PUCUJ%t28{jkWtb4Rjjs(W_2}NtnxNS|0I9A9c z9BdTyU)sqsJVI?Jd2k)1j|ay)g%7P`l;WeJ%X$!h%zk)s^&T4w%$4SI<~W&`JeW%5 z0$_XUlDz+w!;Y?-d1paxOc~u6V>0@@FBxoMU-G}BX?}SjBy5}&Xei~z(W?mo(ko4_ zcVe549wHxlggK|$Tg-%vk~eYkL-%^~3$gL+HwcJ7sFLL?3Z3-HQnD49ndb^B+FYdd zYKdE9IUn7a^b4N)fxyYtvGWi!h=+A=amO7R2m7U4c&G)k?$JY0l<7vG{%KE9>XFG0 zmGxTC@(n5S94bfdQP~l1n@{@w5g-!8!{!8ymGW3$ne7>M0#(-pgU`SSMkPUy2R1n# z36+2q&6(HlEAr=xYU|6BbW2UQJ(08IdICq&#FK;;bj}yzaN`YVbkpT8efp*|bW~zi zHG8cS+HJj`VY98dER&HWvV*&98m6qDu^f*W0o)H=gLSo?uew(n1PEr+ol+_WaT`SbLOUM#P+EEQgoltkb^2ZzeLgWpP>Cf|)9P$_gckXAYf z=DUNK`<`ATTCd1&;hPl?ijSe!*u~KF)g9mTt4JGGM%Y385!dimrn3&Z>j-wZN_G;&RvRyxTwfu5_%^sL@ zxe1vq?0j2ULJXhM5o8!}U*{iHK0A(RGN+-G_eUj0t}QS0SNS?#Bz+0T+}bgbn%)~J z4%8i;%1~{c`%8AJ)=JplM7;M`Ud4&X@{eTsz{51cJK$oZE?_^>Y;2%cD~o;}Gk$?L zTjL4SkQ=N=^>qtyH4xjo)z8ev&0BaLts;)fbX>jnr2AW(2cYGwGu1S%dm*r|ey_~? zD%293pbys&cQ4`SPuHd@8KMl%gt%jJDR&i`i%TlqEsDTgCAd0*^HH!JSg#=Hv*X-J!QkL>jtb$aT1RibNIjU_ zHdqwN`7>?ny6~W3;j)Sj#bx!SGm`j@m^Ku(um(|$`rnQ|G$dAqro`#N%`HrTGV-&Y zFGP*#X70(EW_%W@=i7)4a}lP#7h1 zC*}J#YGj@mHG={>1fPR5l0A8A%X*B7$RpEbx#^dDA4Y;;t-M|jnE=dM-OKV$5MA`A zS@n5Thx1X_G+&=1s|!cCl;SLO%8_6SF{!0%j1`lq$y54@j2p>(cT1P?rVeal?YhX^ z+~BtNc;%zP&+3N#LtpUx%-a_uKZuw&_xYd334_)?B)24*#)r|+!-c{V!kC!7Gv@Zh zc{iEjiOBG5v+bvTx!c>G%0L~VIk0d{UB;UvJ$JC7_r>VZ={DE)D0)3Pj^R6&H#2&w zF?9?~Su7sgPbT0-@}2-f_H6~!faA9wRB4*Us}9SZ-5F(nA~aX_6rAr$+pNdJsLA08 z$bn+r@aSQE;dZ{*S3P@9rN<*UXScCHoMHQz@?GqtXcdxQTy`N9RX4P7i|%?J(W$e& zAhkb>&x6Xe=oziE5d^HaTNvcn!?A7|SBapwQKH;cF&^(dE-Ew7(UR}UA1x12Htqnd zF6#Q?5%>0#E+e0cjCA_ZRRW|vywoNllwXr&bb6mk4QEk*yxxh^F~4mf)H)~x@wV~8 zXnjoBX{uGRVQD>$#nw7Cuk+T~qDtHszg=D);XSDP0ntwxPyy~3YFw0&8*AAg;WM2a(`?u7<>2tPlunJAf1k+E8BW@kISC4W(}FE*|{S#0ZX zGe`uD!yFYB3i_wnjX<0+7x(Ml-ML=D(-`S=8Cyj5&bJ(NpK+8{j5MTsQ5uiBbumUY z3?oTZ)JMJQgCqFe9W>eSACdAq#t&nRrA&~$dKYkQ>mhm|STI7wt-Wx^Z-J$pPHh3f z+0)eM>>4YOb_FY33#CmqrHfjDwtd**K3EAlEbmp{L18}Ffp&`?nR@7Yd_a?K)|7@vrlbui8*GHNz=5ziVDK{dA`Oyg@iz~lzj(Wc(5e>H#hs-K@@NS9ln>@UU zmpY<{q2+r+;B~mx>^JcZZhA}b7`ndXt!RXfyuXi3q{L5?^^ zwff>X)^!lWOf;ol!Ju%5e1j6DHWdhS-h)hx1uFx0@c6EFGeS&lX$MOEMtpO+re~`K z{Ov!!e*UaY_I5#qcX_SrnH6CE>`%>PR_rhLtM#R6!JnG(g=)LNJ*M+50{GqD-#s37 zN|?KH*VHgUd{q|xw%RZOJBAl!>YkagQ5JG+3e2&3G^05xefN!oc~A(hn4dZ2Y%naU z{B;9f1fg3~*~sj--&9jdjBf6`CJbJNUxM%a!VRFuJ~D|PO7$8C%9Z*Phs^qO&j1-U zZSTIq>x&vg#gt)p-!1;%t!eFXCiGv|yY2lg_cbDzWDr5WRf2?9jW6suLb{5L()uUQ zsodLykzdCwT%ffD%fKrxEUQvkOHigTsW|&g{Cw+Po{-yMSLMy;ydq}qLtr)M+a5^e z=R>+4b*X;O5aJ4dVZ!ebA4(!iLG%m%YT!8{J3zl>kzi#-{jk1AU|O7XEU+KLrh)pt zFpA%U4t@W?VZh>c(-y{#(5nD#k~~%arWSzm>)?WvR~E`^vch?9rv>}~8(6j?>_h+7%KcDVC zSK0+lWRAmU4cq5RKW{T#5X!GTNZkPLE3eG#KCF74_u)D5>_h3NZ9i^LF=OmF5!*oV z(N6FW91r|CWz9&;b{RY$p+XQrY%}=p7V9e*O_osl`kEt|=>>wN)r^6}tp?niK=lSW zq&JLti1*0RXdLTx9S5pm_`sU{M227mKxCJN>2P@1T+sRQ)J>;I&;t%Y`!d0jLR3t*(pFZ%PhjmWqDr*k?P~Sv(x0^8dkU%2 ztX_D(gPk=(p`0fk!_5+UxDmL_tBFe9@DL>V;uZ9)674kjeAgkRc*HMyv#=+&3k)uM ze6Oo|1^w^k6)$p$AB)|kd3^l^w@N^orX4eyj+2%P1ta~^@ai5RIeve*MZ=6lJ=j=p zJTiiGDz~*=QFj5@$sXavNZUSpMpXI16u)z=mCkbNZ>EzKV5q^}sV>lx=|IGKzooRH zRR2jRAEU{$q1D}ih$a0t);M=0tSu-u&}cSgGNGS-yXdDUyL2d#Ht-BA5gy$%W*wU> z?R<~*5eyjyn}LSLKTX>c=M#n(#a;#PHaxv?dc}|wuP@fnx5H$j_@ZH#=gNvYY$I=G zhV^l1!I}rV(5IetAYNFy5UVKMSO-aOb&D?j|8Ot`#2ao1pTSB!SO7Y46><3@R7=zq zgaIc)9+tR>m=iw>Ms{r0YPFS%>*fO-xn3PhxsOfc`)c{4!SD~S z#&Yq%>RmT0f7KZkgyx>{g(S#iju~M8j6vWqLVWhaq74=_f;ZbubvuLM6~KYuxNOpl zgoz=Y(lN^>EqXf*)AuB`jF6p zI5jaMVAu4u2s-G&J!+wt8WYeOksI1XnE~!m$7K@J%ap=V>S%F4>u{3oOe$+op4k0# zjo!y>oY13x6j#ei>I{)I&O$@OOcqP(vvgglYl$RgDP8o=;51)XHJh%?I_eKDpk(u$ z$0pdAO+BNK2IDg+G*@5UR6nv{xzUkEQ%wj;idC)b^0#-z|xUC20a}23Gz3x## z9N8fP#=Ppd#Mal9Ye6;uV)z1#oxcLbLN?AcPN>-uQ#%Sa!AZ;Wc&V0Y zj9V934PG?H#y58KNl~GRmm=#nLTBJn0UXulldL(c&;$bpY8WUQ#i%gJn~t~}ykXN& zX1z5`B#(1<#ODD_ZFIeY5eiFbPcjx4#5%_&%%?K1DHy#M*2)yWN&O{6W7|{R81l1; z*U-$OCmFYktVv@5IQMG;CvjcGFeWo&LzQ}o)&OYDAypf4s&132yHomHX)o8X39c@( zQQnOt@^st1cu;ST8T1(hfYqO*cd|)|{J}1s0V_C|@j$fOMnQ)o94#{k#~0|MIgJK) z4{Wt#(+{7-l$g@ZIV@zDWJSr z%{-eEEQm47h>;Wi%0To5bymL1e_$x^V?R&Uho3-_6$8CRrHb87Y z%85@-Q}xNzQU&)1L$v+6pb@8 zjD(2}H89pp5-?R#s3_R=G1OWq!$ghfq!l1yoeah#QJb}|`c}Y?lPjM=8Ko9%6t$T# zz9v>u|zf?_0&?(cqIU4MmaWIZ_Y*f*FG*BT?#(-JXEy}}v}#q`g` zMe|%cWbIej;FrrHYvanNh+-zmDDr>!bc1_W2%Ado3S@dV=g)T#<0QdDe*|h|7?eM^ zyKe`EG+G?VC169}<{e66!Rs07^I6QY*LTBl4GUNGoPz*`A1iVD3lsv)rS zT61xhn~)-id4|Iu4}c-G#EHz>SKR|PLdmL4+*IX{AYnzzc63X}bmK3h!o`*G+EY9Z zNU5_gdje;!=w86HYEb~qE_PZ~@q6*KR6vbv$9}|n#NN*Nr}tI?R<-)4Ed$n(Oj8F zp%a(phvw+xxdbd-bLe-4D=?EeSAf7Br8<)hM=D5OzQ4amNq(|w z2fnVqBGDYR)6Md9_YP|-8qpAl1Dl(4CmR!n)yQdtD668=8N}*dzGGm|xIi=wm@3k@ zp{@y+1$b}>!j?zX49 z_j>#jra^Vvm+2O)4k~ziqV>x%L-5*MhR^Qmukw zkFX&}Cy-bM%z?NB1c!o@nY0rt>1=hqf%R}l7c_34a*T*B7>1wuLe=_Ux0mUAsyYT3 zIK41c#JRl~^Htkb>cWitMz*o1lnfducQRLLd_t`7+gtcyr8NEq-k)?z;hGz#W`W^> zT~B6l%a2saH+10xO{M18+_nOxr%WF{FjL$iv-#9{EMyV53#44~>K>|ITKg+jRb@<% zlx#JwlbziyZ?%6OL&XN-OeyLh+{Mqgu9uSYxXQ+51m0Vv*SHyr>JNqCp%(*6jMf_q zq}!Js%#CPBxPnZ_S$diJ{$U>shC#a*JWBER)BBhB7uGJr32*|sMUx95gJcovoEPk#P4=f(;>1%qRx6fE!Xw3+>e@8)-Z|4abhDR_a~1{}C{4-`3;(cu`o=76{^PWjyk zLgK_tx5YRZzFgccWE=}X_8o>pNC@WlVQ0&G&aU8>$6ThWG+@W$&~ZG1)l7qGYX7QYJy|K zbo+2ApH?|a6E>}EaG1=}3zNVgYonr2s!N0Ils!PpN$M$u;TVpY$znKQRtunDFVI4A zH-gw5Ctg_-~uFe(FUAU z=RcZ6kueIAr556loqW8@*<`XkAcdnb=8aK&O14Ot$>)U!R#WV|#kVDOK|0EzME2hf zdax`=w`G^}3z7ae)*D}K<*OZ#G)NX5=(+*PdX@2_Tl6*!(~)iCbs^dmDgb9IE)JXd zGC%QLA;d|HUhEp^iLMa&t=)H6ZQ*3GUYeQ|YsBZ@`WD1sUlld<0EMWPH;HeRRm@t{ z`}*n%W>y~_h|oTa99T}7G<`l%7Qx4cTcSITLXZ@IMUHl-h~6AzYXSR2A6%ByKwD&^ zj;2rfFpWX4F+cw~=q)T*s5FbFFbe+DsV}qoc4w!2xnV>VaK&h2KWUZ~(&gcYoGkW_ z)yH4MA&(-|zN|f&#el}uqLG;>zCC}L-GmYk zlHeR70oSUXg}ho0iYUv|i5ldXWf7erP=QidG1>Ro0P~e0rVcaTSIrMdHN4S#>eA8DrI(pw+hhc-%bktHp;uE9F54$MfMfl5} z{-rf`*}{V64i0zN63w3p1JisZ4v(Jwz>|Z|z%!pe$=Ni6a`p3Py2e}AEHLG5MNFTl zObEZ?Iu+MCT?i0J8=mEgxeUt7wIdE>s|#Q=eBcI75o~E!YLT3uwLn}hYXK>Wwcvh& zF)mqqnLIIQZFyNt0cG3wrKwh~vC<`eWpDTqZRYPRD>F{CRDSVy#bpjxj46XZM|3n+C1zvC(kp6lb)@^D+k+LEr2P4>KA0{|vi z_~z}L1TqJ%J>wuL723lnA}Q`ec2C1qf3Ml11hK8xV zIH;=i-(PPKKu%!QUe70|KrF)NSKm}WYp3EM6QVBh1B(`Ov7o@FbDuHQM8U`zeR{y{M0+U_e#;qYqa#)>+2NNVK2>ZrK;0w&S|92sI6&|yQ+Cc|QUlSB7 z$#ErXjlgB~xy2>a%$5N0T`SnWq6l*7YwSCCCZ2x8=yOdd_;i_=Bw#2oaM+@27{U;H zeA82p%h{F+*|Ev0p<{RfZQw;88D{hcANgyHnh%8Rla|k3@5~t|Qax*MJ%SOfD#7Qu z&WufShs77VPgkZwXLu=9s@h#`e1iK%=qa=k+kuJ zPs%l(>3H<-kpXjdV$AJHReAr6 zSf7+*+&EwGqs3TrPp41qeSQtvCCQ-s9S%%)y9W^qC==WpbOH7SW3S?Siea7JUW679 zAtsFHe_h?$&WS`H0P9ZV@IE>%vsJ-k>N4D}^bQ(>-9xo^GyoFy*q7V){QfeC*q)yR zn30UwxH%L9IQ|h?p0$msZ+T*RB&J8`OiF*HN^*r1T|bNZcX=NdPU**fgR}V6q%CY zbD)Tj3cG6L48u2aA5Ckzw_7?9>D&x#N*U8mVAnZc-rQ340PT-lul(Y(%er152VBNQ zE5h2DKo}lqg6nZL)oyUk!7QRwX17}yXu@(w0Rhv4msy7;%p@9ViU}6!7mML7uE5?e zu9qS804anRuU-R*EJhFdI?;}KvPsIo8A8N|AXI0o%jMAn|FuCtPA{m~N2%Yf;ORf zcbTL`>X^bc9_=)2b5({+XW(pbMfQ~uq__HBgQ6g#Oo5?S9Junr5yKROdGcWN+5Es3 zhu~B^f+Wbco}OrE3G|jZ%hQW^6QI*wk80}A2G#b+f4;Urvf317rLeLs)nk7BGlO(D zR{#~hOWGsF;KA1F6-bT-S7NYp2W8(}BX07n1-{Ups%httVmw=h=FTbw_>vY_x!oY% z-PLwrEKLwrL1@_^#vn#&TG(imevVDlC31VbjTHUl$g>cN*nn81TTG$q18Wz~zJI6min%&A2ADnKwQ=lyGsX z+6+4sQ~;wYr!apv{-`n$BV~2RfR^NZC-8_dAF8Ah51wQVcp@@*_!mU4PZt=-ZE`jQ zU22Vff+&bLB%q)TGqjVp4v7{Wp<}#&R`OoZ&%Zo$<4W}Ac^*l<5`XL4 zk^JfsN1H*95Pa4wpribr>@9jE8Jj^lWER{;78?y&EP0CA*!OO3t8TyFCbSesTQLIs z(E#AB^W&2G?I27Ir^)w#IwfbX<`qIP-$7m9@lv>_asSUj3-`ieO|8=g#BHc@8Xr{A zP>%iEU)iSc6SQo+n6-Yu0!juJVZ-Lqwd;3C(i>uiKmRcwjcUnf&N@||7CuV*yq*uxI1unyL<+ejWZwnO>1AJUcmHk*e#YrWi~#7?(d_x2HP>?LGBTrpCYkjp%g>Qt&n zIW9djtM6nu0<^gs#8taPi0`|F2u3Ir)(1pDc7XDpX{Vzd2uIuh5KMu z90$`lL}c_rvn#V0I=-pMyy((o@}x(GSK1X{E0*mcpW1Yfzg;Xpqb67QX{apdeDwZ;8jyYY zPHN^fe&mS;?roR7q*y9_0nZuHO;77KCj5r7Gp)}I)> z*ynyGlc#p%{l^Q}y5Q8a{23fk<1k7Zl|P43>hShy4;r!6IuSyQyhPP_Pe`VP6`c24 zsEK6v9Y=@$HgmV5jE>Cxu>+^1ULaI7TbOcAujJ9lYx?HS#G2>%=BTQ9R8?BZ-RjdL z=((G9$bE-K&B%e|76}h31n>LKd!D%!slddtr^I=9JY6BIVLWP@=@jE|3&jf|UcLs0 z3~gESZSm};zh8g2-_Zf%Xa&b84surJ^PF~Nue?RdU4z-usl*A+V40yL+u7vgRGM|F z;P&8nYp1dDRKP$}irg7p$jT}dQL_n*O>@1c-sUi)u zDW;+{4&dSwo#=3( zk|-LqxdJhM|BpL`&Y`f|8U(PP738#5$Hi2qovJ$TMkwDKI-(v#Ps1PKRlp6%{R0(< zSv!+UyK^bII^l?6N?_#hTT-aJ#jmoWYwaRyG5e8jQ~!8&y8GjKQ~P68|MC3kmH+Yl zk?NnL^$G;io;S-5z;W-I{Jgrl)^Lw{Y>h~&mOhnkQQ0UjYI%!$8h6kIcza&|S zkfUm4l2$UhapoxsMO*<!iIiI<8wzMI`WBnBY9Jd1&QT3C-1EGZqIyR>}&MH?`2~ znVU*~ynmU?7mk;x8*&hz{5;!ZL0Y<4hK$v=4sLmR)G0+f=PE5$j&{|;SF3BNaoXX} ziVL+RTfDrK%%Oq<2x)IvwoZB+@<=9ccOFcttW+Le%F^TZJ5n~Y7EeTaj*nBq7Q_#Z zNyn?Yk%(UeEA?fTV0&KG-NR5@RItQ`Gy?h6k2R{RZUL7J-5JCDUZ>@V;a>R^+JhPeY!#Mu~U^@{iihfEn*L9!xL z1&&ttQ#gZ_JIXrG80IrfO-W0bGkJRsQKR|Ns`#BWv7>G$5uh;6ruMpdELGvw3=Wg; z!gb9_6II>+>WF3;d!&TPI%nmC8SL-e+DF~Z>iur;$;~Z1YjA^W2)!s5^!yxU{yD-o zx~b4wVPvwgIa5_LrnuPt%$na`mYK{_Rg+aeq03g5h;6*!+~)*q+nlB~@*$DiJop!i zNr%UZKc+xcN`h8hmyldNd{hEAc^po2J<+u!3Bkj`%fdJ{ z#2-V6OV?VkdUl;FLYO_Q!+V9f>+u9r-Mfny&#gZFs@uao5-*O`jd2jMK5Z_%-4)&l zNX(cgJMO5w4n!;s@SD|geOcj+fKeVshXCKPphN_orZ0M?9%%w%MBqA!PQ&JgQ+kuIl z7hRLJ5>aiPNn$Q*N|s%HG8H@*k3F35nI8nR>aCjpYq$Xh6vwU+yma+An`$ zjQIc@e$p@T)9rblF(M%P`PUU~5Z_ew2*}C{5DrblH^wG8|H|j*cAy#j>McfHNl(nA z!JvWv^om0d){N(Ou(E1f)aj;atu}_Ip+BW`8~jI_AZq1go?i8>Q~sGG)^AgMdgUWG znf);wo(8c8Y%a&uf5bs!cX^<@OpU7mghZ(p^1P)!7%gF!r{m8o|Ye z4N!g@Ldp2I_wjJkLMkDC8}BIJ%qn|tWFSHVF#lQeb$heEk6R(C>8H#Cja{=} zj*fSLkM41ecn1ePURUBFL@zIBl-$9&B!PVx_HVyjAi$FD{{0=71IbI^CaY$_D`&Ni zvLc1Mew#pu@gim>Q$SoQ(Y9U&r5K{PF1UANZ!IaCni#R1~ z=WG+0BQ`$uz#8Qn8aIpiROSr-_Yqyhej`!R;aG0 z7sDd^8R%!0L}_rga%3mfSo^A|Ou8o6!(wsD&K^b7n zUD5-jGAm_|zTuUu?Q^lNQj9N}B8qcizt|(JtnXmH3>(6P>QhPtwyah9E9)r~!`{Yz zX*zg*ln-)%`>fsg&lll{coFA^tJ_SIs(jS<=NVd9-bl~@ay8oCdW_2~kW>bf$4;&+@}Ag9qhQ9Y*_~*=gPzmb=y$Q^iyG^I zo}73so&O#V7ps+71q@)7qV|4(vf!qDGHB;HlVdPHa^n)_alxfViOkOn2v^$!yrM>a z33_V?jo;?uI{;UFm^eA<=XlWdmLeW*uI)u+M_%q8Ztt>d7)~)ETgNGZ;T?%P95%k- zjKn;@e*_+0F@N#AsPMzyv0#7!&GJc@&cVTTi+e@{5Y9Q1u7*^mtX6w~9o(#Xp_1S@{-IeYXui)1Pc5!LH zzuaH*N71^sO2o#XZ{?~56gVp%dQ%Sm*FE&Lb~FB({G-Exe4=pglSmp)ML?#;)rv?#>TX6)oFNWlFXcRdppsqKpsoRbROA1UBiVgSZr?CMO!AJ>qsid{a4cn$)Iw zjq#nX3mZlyx5*~*S|^fA+w2cLN(YCLUik`_#UD6w_^vvj!S0rlIGCl_}IpZP3kRO zWPM(9;T%vDl6-@g?Uz3f@IOMX2Q0~HG@^&sbdD?SAot^PKR-L?G)PBHNk*I;4X%6E zt*KA9knP^CFxMIb*_Fzt?>abCNy4$&9uT1s0ul$%h)zF!tlgw35OQ;<^gEiMj~q;v zVf4>m=fgtD6&a%OGUS^Nf=8|qbK-iYa6#S&EYh~!YxfX=6nO1glZ!OnrkW6f@Gr0cTXa`b_&A?xEUX>-?ldCjtW$ZfPY{+1V7o(mz;% zzH8e@#gYBzKtiqxz)b)1*T4L|Vq?z{!xJQcW!WbbR^6hoHwiJ#lJ>-8ZRLs*-ia%{ z$^A+^o;Q(2rBDHZ-aZYB>m?Ss(jX1Fljs{5AOWpP5d!!HB4n+$P#(cF@#bEU((&dP zHV#~UUR|26L}GFF$mzVJyl}q0l8C8I0u#tn18OZ@W)?Ax@DmaTMc^mQoo*fyp`254 zJrUeXYE|n+Bgy=#1e@xn%>L|CVGkU=QhSfdMod-GQz#V0yJamFE3henHJ=|0Y76TX zM*tflc1N;H=;On7*$xe}A*iP5tmeyKn-u36Vf*FUZ7SV)i#EF0AimAI_7 zpO#|fQn~u=)%c(BsX!&*VeT9oT$BaE;I|zQ#H~W^Z5{S+# zpid)`+#z%_&1Qld3-k_cPPVjdpp835+oV1h`OlWu>;fGRscm62hb5i~?7%U?q9iQZ zE*ZY~&KtV~N@bTw)gIWTonG95N7~-eB`FxQHHdq1mpTEWvfGOSHyA*M7mzi*XTfkF z681@A9Y60PfDC9FC+aa8nDzveZreC9KrlkGZ$04MxHo~weA69!B>Cj<*Xewp?k=M8y+<5GP99c17A2E!FciR)6e15{%(l!=*h}{HZUqSYGr`}~_TO`~b)e6#< zMy_#bX(7hexf3rQ);L6HZGsnYr$lgY7^oN5tH zl1Tl)LSWDvapGEBR&>SBvcJo}0Zk_~>B~MM zjAcs*Oe@pNFUm_CQm@D+md@f_4T3~3t`GOZ2^_;-^;S(%+I17ELFSSJ&9bB@aXx61V*W(IS`|6xw z#=u4Gw_EtvFLvDH)i!TVUc8=26Zib83)w7a3L~GFO=rP}8jUB;iw7`isy~_pyQYad zba6wpLwL91>*w3u=ji;t0ZL@w?y;(TK0T-$M|uLuqx{%g7aEmBs-G&!_tzYS{^A$3 zyT&sabeNr0;x0eB45~)f7*XFnIMO((9#Uj1UacYAZ*^Q zW2os6jQjEzaJMolR}N1W69#F0yvK%wO^SrzN&emj;@F#&Afr|p4OE#_J9mQh+DH5C za&a|G_o10~Nsbw(2`5R#(&Fw&)c_haLdf-)&7q)F7tuF&CcR8-kC0XP(sJ!ZT}Olq ziB*KvKWga&PG6IcR(er%J&6*E;Mf-s7~W@UMrPYl;i#BAO7R3wF^pO~i8s&#dVq?P zD|mfrFA<0E2WlchD;I9+fr>S24i!Nbw@@yWAQjR2C=c8&?j#|`*d=$+euSE`!|172 zb4R14>>f{=fxn3+Q(l4XVf$^#>oX?r96^lv26C71qoi~$kBjjFK&J@R8~JPz-Fx5$o!S%_noaLbLGV%2l7r;k#(}n$Rp{s|FK7CAXdokt1SXd5 zi6^hT%+q0o(B6n0=m#k1FEZRf;F{QlkcYz+_Dz2d&J$Bdg%UBORQE=mrDemx>4pym zaLFr}1y6Zvk1{|)(?IdL9CaW@34rab$jeOHLuvoPZ^(uEzP^McN5{H^X!D#7YfNM(SV?N$R-y7lkCdNRDLr zNQBiz2DX}=jMjq$Hy0NE+<&ruQvnLme-f%_&Nf1LR?hV#D;YT$n4Bo6jl)CnT)FFS zK7ycVhYD!NUmmg$Ncp9-BbX^#i1I;UY|%1w=8mO6_Gkp&Faqm+1Jc17;~qN{8?2>= z*Ee<#rV{3nLifqf)|h(r2_tZe8ubd@m?>Ily#>I747J7y+p**7+*O;E#u3S6iKn<`j02^bpd&W{LoUh zNsQ@R?9hp}!11(ARr`Lq!0Fgt2=dv~=qW-y2-B8^Ki{eoP(H-NRaZXbR)dqBa1`!d z5dB(QUVc8^$QAftCl}iM6!7GVF>+bnt79GzNVT#Pu_;jEf$6jY#>xU*|T;iuymgIBRE3;xcIV`XE@mkiBDsWKo)%vCk+ z0*SPf`g#(Pi&%5wCff4$m`KaT02Rz$as%g0k-qJ*LKHZY*p86H1E%EnaN@ot4!`#| z8)shn$*yrsxvta=W|CzJqm{!Xkwql`VT&`d)$CRX=8l^R7wh{=y>MX|*q#bN%c=&L zGmV$hpsH%k$62Ln_ji_p+s8Pc-r9=CZlzmeIlhz%&7r}L-Toc|MAy^{C+>qQ2dD8V zfBmP01VPg*Y|QqWi@B6p;fN!2D{45Hf@;T#hUXfshCR~aIlyeNi@f0bP+w_!c)Ic7 z;?QJi{NPeM-Mv|?an+4tn3GtR=IQlJm}cW-1Qfqjr!Aq_kW|eW1j#PUT%ziF?NpYevK%=YKG(?Wb`3H!u;Fj)V6=g- zef_dLrFOO%r(L;j`U7y)r+W0E0;oSVfEe}m;ES~E7p3KT^h$GXjV%M3oih-h%Az(# za~>T`zkhYtk~BX~1d&xd+J}lX<39y1!|p}LI7H#`Jt)0yrq|MfSibL{RgKlzO~uSl zhb33DtX4jP&)KH%WNpqbw#7)Fze-OFjJ(pmR894>q%iN<@@4^<;Gz~XagM0sUXu!1 z2+0605WhaNJCgpB(Ds=*kw*t27Rb_eAr_qbImIk|PYY!vd>zgmIjkbN7f^|B8T(s)JV!$0cD9-2>tC`?=QMRTa zF?MJKWL}Vhih&)H8B=?Y^*s*0U(B{?I4Igws}I~Ccx ze$3!P*I5Vx%+$jwyp#?qyo5mz+$^KD$CPg*WJG$|G9d*<)b6t3@r3QuLtb1GXc;pJ z=_h`Ht}=<_K3UeuCUG8QSObB$cGDO~3z;!R2_iMprYFVnOdEICyLbD&1#ZiM9l3t~!$z6Si=;`SEwXv;J|n3Ao` z(*me!yM`hv=9T64Fy?e`#vaifC?hxbzZ@rfveRa+0vhG$d|E;MrI+A*uQyaS>k!q_ z2M;U^;b7!)hKrnQBRVcqJNT7Sp=X4@vOF0+JpV9;v$NGwsLx?~$5qVI7k>fd5m%=t z4eO$gYHZ^Uv3vamL>8hf@gUmZky%nJvl?CxBVJs<8c{tPJe#N*;N%O-ED!&&)4gm# z#4__%bgXPHEPgJH`Dod##52vL#%MOdNB7UwW!Pb=Wym6(t8qfB6sJNS^Sc!0IizI= zS2{HQL>-g0FAZ+JVA8~CAuPt%Q-#w)hyz1A??6>JEu`u~45!rNPM@NvF#%V(*gbeu z<4|c^yV-0DsoGrDXQ%{>#MQH z9QLdV#beptcuR7jDBVNwQcOzj<1l9*r`7gYXUSydRYI_5O{^b=*+k8guNq%}n*1p^ z@wqh!K|ctYs5X_$ESZ?H^2wlX20vAQB&z}axV(la2)h)N(P};CY6Rps)<7$y%Br%wU(RlYGXZk028X7d-Bqg* z2RBJLomo3+A`fy)0ya;RNAg{`EascWUt8esTIeGvk(p54^`NLuuoRKPRM(bRptRk6dGxipqkz-ejdU-nFYUJ{@~87-%u?s7dI-< zf6}P}SJq(3WaLBL0*c)jn8(?gl105&+!^3_-NKwXvd zsyOU8&I~!8JbIdXP`Opq-GHIOiX;4lA!U3F5i_hn<>&`hX!Ym!C{K!2ni}6cfpz-!- zBej~hc$+)!_UCtY)@kGn7pdseL56N~#$N_Hqd;#zPcA_!uwhWj8Ei)+a1{xJ0!q=yUGk^|&7R0h2N=ZA>5=;9Dz04-l2 zS>`NDs|ai9b|K3*cvY1&0c=Vh*u>l=uR$`-Ez>WT@*}v?4&Ef9Fz`Ut*j(}+0a0pb zLA>$mvHhlw$$qiI;EI~u!-1w*f^C+~zReJSuW{MX&Gmf3g7+hZM=#z_yHzc?!9(@L zw}U90f)rgT>TffS<83z?r#Tt%OGZqNjNEDe3lGUX{PCQ7zqjAKIqU;i)`Ybg)ogay zBI$B_Au|(@&q-qfG$ibNd+X)mkQ+s=VS}~b9=gRouzsklez7LVfdc!e? zoR4?8Kn@g%FLfAUs}F~BEKW7Mj1FWsu5mdLm{gQeQb$QKEGAQq8>6}zS-S}OHgIE5 z+d^Wc*IGm$RYONm@2#4;H?iSLFLBCUZ?CzGSy%t59n5-F@it5dGBBQpGJ$nOR09(Y z!WE4r$~|}|yyCCMgmh(7y>OC(m>y1KZ2)pqr{_`qgdCroIT582-R5V1%0WqKjd>)W zu64@+i;-*I2vtYnzt~YY3>&Dt+W37B3ON_sPle~o196!S5v3H)lJ~l}Q=8S#c3->g zrzPbVi`ya%8)rYrWdrUk)Ty;(joMy;HTXlDe&A}Kg|#K(<={dZk1h0CV?Y`(NlP}H zA5?8?g+ucRTUqIr<^|_fQ032xFF266=RzADt z_65?8?Zu}%x%Rpz11R-;iBl8>YoL?TwT~vZwLL=HD$>2~*X4RQXKHtM_ywB}>##6{ z6Vn%hlutud0@Yc*JQbr4lD!V+sIEeOVAIB+;o#lk8`QYlZ}+!a`G<0leEoBIaSwU$ z^G9p}Sb#@R`c%oJB}jcczP=d&zAfV;NM%Oy#@;nnD@fZ%25K*}*ZH@rG_BZz<*D_9 zMy``9IQ0<*6}X^e4?N=J3qJsei?}cwA=%p$9;~rBvUN>@eMEC-(lKJ*!U`dJ7>6QD za{98-^Q9j5ern=VLne{mBlhBt4KITQgVTW`CCWH^vjm<(Ypn!m1^<0nVR-C`(Fp{d zN7UGfL_FP~XObC6q2M?}!fL8vcJyw5b=!QZos@kGXQQbUBOj$iBxYd2r0Yjprct2A zeM@VL8Zk!k5H;u@)#6OYyz$~XdA3!2jXldqL>0$CXR1azpvavG7JiChF>PvUa=-}a zl};$pwr`er2F;}EQQj(zBR@(%rr+BDW3V&bXmZpoMnqP^J%IefM8mpz3PlslW>gjN z>J4B5fvUVb2#(S*t1wwp;at=reR~Z@2k&MI_yM2g?QVB}YjPC3;N+qWjNH)F5mkz2 zEKWR8m&9*V4PYIb1Q=;jWnmKgZ6!Ms2+eE+aNjTn&S(hVZx3%**nZ3NL$XJevPgfM zEu{kNFo)OIJKTL^6#iQaQzC3HHTJmA{*C^XkPqym280%Y7&~=r1bzKea6Ddnx_bwe z05*=n-pMh?gK!rQ9;^)mBEZFNCAZH%aeom9FzgNGK3TOncs4e2xkFgYTY9fjUTYis zVSRtS+R!MwvA{&}mKYTi&TB?Epc42Mx2drL>l)5hSk!U(8VRl^SHHbbSQ~M59D0u$ zXe-NZV;~8vvtHMpGnStY%m&LGXy0PxLC7lf``uF5W#4cvXq#GMQe(}T3;-4eT3?9A z;oL@suh`eEDRq`iYGuapM(RbKAnu+sX`_K-Q6j=LE;gq-g`ha!g16AgUx;V0TGbVL z95Gj(>%E$2L-5O}DwOoH(2Sez3tk>t*v(V0ZE7$?0w2Sy}2nFI(nAUk$OXYAP9a*{dw z;v%3WO6CqlYDmep2KUF`=Y6ZXS1)ocigxw^5}B*NySlonx~jT*0ox|nLpe`0*ifca zN7tD*rLEj*XzquS<1~`v7%Y+C=;tN=?-@iijHrf%xU$!VFqOeJ2Zmlxgq=^BJo|b> zO4)=>COcgxy5bMXQe4xw)#P>Dgg{#2->E`UU7Pf?p$0bRbV1?+8YBju4bNjFuzDv1^q zL*)MLal0@dk7H5|fA^3+J|0d_L+~GG_t)=Ni(vA=7)aVq35e0sL3xwmKoFKViYrWi zZXspL14Xw`xuAjJeJSA+GaP~{|LRXYomWHj&qWkg_H12G3Q>}6rfZ;S5g#23yY$pBhXd%SCQl=nQG$z-H z+#|KS{N6+~wfyRRIYMQ#Rzj$5u`JZpFuNrq z5lrc2xmCVNJ}s(D)<$9ixn)+5_2H$o21f)v&x)j*{FR*3|8?<6tl+N&_sd#oh#TS;q8F9~$A$!T{u+t$DI34BLd6{3U2=@eINKNk* zoP9ua=c>S-GOg1~pQuu86+uvl%_-|Zq30^Vviu0H12)I$+gixg})5!!WreRo~hcOjT4 z5VN-e0=1CBV1azxEaVEJ^0IgeU?K)FkMRAzjBRG^65Clb z(gajF;bbMoWt!V`mNdAnu2qW5;XZGbMQoE)BoAY`#LdyH>6`JLL;cq=v7e=k&%>^V zAHM^HIZZLJ>Z#Yu!J&c%f=-{{Y{14kj{*s{mEzyOnGrWchs*LHZDeY|x@c)GlS8iG z%4Ewn2%BG8M4X%RtFkLq?6+Sc<0C)K$m!w3%={v991z!P!R%xksm^k3_;pyZsg%>LZ`Fy2%&> zn)&IRv0DJYtX^Jpa=Ltv&sVBn6DXNx#Oo&|YKq)tq5ihn&B!j5!KuZ4WtEZ>Fzz?k z&l{;22svCa@C*|)Vmz3cA z&ci~Bp2n5@T^3o_8J*DfMAP*o4AK%<@zYNrS!!?lZZ0}ghYZ}D;n)E&%A|y;wrE0> zrKpu;k1cpU!vxgD7Hv{ATZ(_am!ION70+tb6ddcgbxq)1Mqlt)f#gvAqglao|#TLjf=SLbcXJ1G}w*xHBo z({0OaAIAl(?KG>+Q&1}gb#D|LRPvqB(r)a2FP7=6FZP|=1Myd`g5ZJ~7S*6Uftx=g z*txJig@mofOKD#GVkwWJg=JWsY}@a?e0u0-Tf*umIrKie^4%)0Mq&Tb4mHV4LRsaj zuCTM^y{=emwfx^#*R!nST}3+p>?8ONR_bJ)3aCwSCh`0Oc!3g<@Y2^S_f6!!UM8$k zJ}KW7KO)=6Rr0cdEF#)&w8}k!7%Qy-idc;=IlzWDl+ZH|q-(X}EOm-4+ICPg7aiSK zOLXrkOT{8SUw)jgS4+9;{>D{v$%}D3^O}w4YWu9E?~duj+#wLsgrJocz08UBG=@=5 zLo`6Q5IJ%?N8o})$fulOTqDmh_f@#ilJ?huo!X^6LxSZ%?=9Cg+zy(u!r{1N#MR!QeOaGZ+~m(#f4KG<>0U2kLtK2t$engJ=yw&HCS3gCJOaYB0rd3-)b7aWd@YCsK(k+CV;;*p7q8K=HOW)}VfgVHa z@-Psa%B;ga_F1rqa4wL#Ri_m@xB(*5X)ffy;yTJgmM*7rOIzL zJg~cQ-v5a2sX`FFxZ}VJH7w!&NvfBu$~ozxnVBZR)y?$`EupxLZ)~djzVf7!Xv4J) z++0LE)kzK>oTa-z95~wZeXMW;%S4Wvn=jE&x)r5&Q}Hqim2jb{HIEY^xCN>Vp>=da`Z-`nfqr+=G8$skAjuTaxT4`h47{K;pr*0j)Kca5!X<$(* zPOtGDl^>@$2+*0V66Ld0X1VG(V# zkER|Ci#>ywB%<4Lz<83VO-3iHcvVMXHUE0mg&2*Flk z<3O@XpK*F;BfwYGPWPp}kncoLY*Z_QQi4kl?G)9EB?%){0HyW6@29JLVJyX1QuJr9 zS;8nZ_nXala&YMklfEX_?`p%Gyxe429j(Qmn5`QzD#nJz6y4pDu1lkI|- z+|)s6^ZTwTul%&S#=%Yy(faVR7}A`+p04HoUnz_va+)LFYE9yCH-+=2_AM;Vnt3FQ zo`#ppX;|ggZ`OCjrP|HZ8iu{DMAOo4M}{bqF3Aal)IA%f@}ULmDhm!tvQnK=Bg(eh zc*6l~Hj_3OovUWrd~z#zMZ~+ju=0KbTG~;*n{(Jtsl~ZB6-O!I3 zX=lB;eKud86L^cqR0=2Ydox|q(|AH{Zd-$@DuB^7tsgt9G*r6#8C-MyB+lt5hJ9*7D8%8u z^u@n6T`;^1It#}ST4!hyexL`1pT(w{N#qIjWX-X&Nw(99_FIA2>-B%l)+_H2`O;SV zRSZ)l>kF?=}Mpe@UOBIS(9gs+J&f}+~q2qlMA#45mbN& zrAnliO0b%=G5~l=Qy)-qHpgimu8cM-K-&3>kRrOXekz5mhe(Rw&g!MO42-eM5xI+2 zs~=FQ-mSjdP{}UIg>t9`X#i(CWIpRlM*L0?HFKi1TizPNG|1I&jr+$gT8o!=qf^>9 z!^w}i#?zF4eOVqF=xR!c`4J}Fwdzx!lL6!N^jdQ-k5zUzkv?`4kIl_>QEKwze_a28 zQkNK#i~FNVUTy3iLfCMt5f(c?e{`Dh4DrVPF^dKA{Kq4qR`UCy7MhxmAZRTAKsRln z2in^n5p;HYM66af+@bEa+t92o?$PPa&mWx6c3t&f>-KwAFk~cdh!C7KaiJ*clC~>V zF_M4G?;z`0Hq$_OQy`9CUACUdRQo6s#DJ}~$!aMPX6Pa%8asxP!4C$6V=f)sd5D2@ z+i8?jUywjF^3kvfRQ5QUx3Au@+Z17#E-$)2|$sdmsP7!(4GIeZEufT8f*{8qH7B?EzkpaDx!Z@~4 z=(eOl-y2XI{U8Jw&1+hs9-gIH)uTDqg`c+WZhqF6mhS@R@iOXTqn&yk$e~W^M}Ip8 zb#;;VMCQ|6gwecx^_c%?y~p1P-YEd5&{jKq@{I`WNG7u%$qA&aq_s9jTOj#Bx|V^Q z8j;@{se3>W?#{(!R7LqcF(6yv5J5ksS4Ob zRlc3`{da=TNHsDA_|ckxU3{s?_1W3?3Ii~b(e+h9QHTG7kt%x|%ns-eWO-Ww(zt;j z<&&rVgEt35tbH7PbFBGpCo#)`i3E6eyW46YCaW%ZRa9{i%OYOE5n}@&dg-)Pju$Fx z$)jqO;QM*>aNdTRV|i*-TK<751Ia4gKgbvs?j2BzXH?dd7q8U$NLDu^;;?Wcl9@df zO5W#PqWf)ll1pW)F@hiCTGuQoQr%)~X8=5&3AmFKST(atW1c+zgt2P`+7B*=V79jf2D`riWM+Q>CUMAg zW1}{6ErhENzXQP>#P7k@z7?sLV$mfhKZarqPFr@R08N*J>4BbOSpaSrxqVAi^FvHAw84(hPT~03#z1nQc55VNq7!G0Ou0(#}f*R2N+XIbmgJyQWg$ko*8x zx70yE4T4zyen$rs%ofj#JUN6{tb)vF`aJg9ZlzFZD+TPXE)~W5*KCjIAwVr{OP$Fn zdb&yh13M4KHd^30fVEtl2yZFt5bDw>C_$kLye}yxG3Nt*iMWkYQ|;7jTz+JjPEAv< zW?L-c|kgJHIGqBt>z<#YPFvdB*o{%Nkn*!-~O9+0G8;Ivw50E z^TSVzg$>Qf!ob2k5ugDXvwPl3B9vUy+gy)Cqe0M6hud4&VuAnx@V!F$)~ro>n4g{KPjLd zZqH%k))4gI)%;&~v+mAS^R4#6X1SB7)^aQ=mApj)5Hy~c zyzZp2Bs*#Bc@t_>BkY}kxS9y&K9+S9=ol_c^`>H2eEgX#{uzOO;0PRMk!)j~ST&S+ zqTHRbO*zMAGK2)vO%@E$?@NUrm3B6e7MAy&qS;^c=E~T1FdXg_+)f5O>MDd2bF5DP zri>`xv`ZqsOVwAa$-!(hoWa~q6*iSZ7 zVM7fmbgI@JJLi=&`FEjgkc(;dP!w`D;$j*OiSL2zI=&a4zT9&($()B-B9Y2Hh1%Xg z>bE-SUui2M!N@eOnROIoJJBWWS>tG;UBRt1m2+=12R5nYw$sF2opySCeP_BY1)P?z zj3*~_P`TqLTDGC0omHT17Xjgm%GV{an~aXFilIS^8$YvM;T(vwuTWh*Y-}G^c-7aH zi@N>fS{k%Xcz{sDXmjM_3E7UHwm}`9d?EEt$5jS3vC(#s3|0{OaS?j`fwFAHhi$!ZyEZIUMRI(kqRYs_3erFN`D)f2~ z@l>1%&xM+M^XWS9YPrGMoUU)PVVA)477lE#^Gy25~93~@X3eTWVQU z7V;~wf7Tu-VSj~wu>rGKXKVTwKAiZDAOqh_v1%8KSF-s>VKkill_tPoY_5K|vw$*M z|8*nm6g6C}rOd2EzyPRlKcsMM#Q?{F<=H!ZDN-YQV6cOD)u7SH>xdR_kZSV-X~wC0*#>;8(PG$eaO)J>sJ{=sRd{{ znGkIHfN~rosHdX=9@swz#9KI#&PIx9@Q5SdzaB+2_WsDEob^GW5k^E(4(w-7F{0wQ zO?ftzO&4d`|Fv&){o(CtzcGCO`0k5q$@WaGKDZC;EsGTja@Cz(4gp(ZbuTUSM^O~{ z=&$m`AT(4j@1|F8eLvnOy6!7>h!#3`kV-Y5is)0cP@K8y-aw_VOHd|DO7!MHEqqcn zWgD1T>eN)gTZ94yX-+T0<5GDxVWkbijk$0?fz8}cfJQY5k3E2XLcaNl?xbe)gRi)8+{B4QPobQ`JP)=>+OlVaTs8LH`W z8;swJd~_5M8!@e#qHeY%j>Ym+bOQ`f7$YA^qf^}V$h2@Wb`23YwE zpmwijRD+|+g!Y)-5qeflJfYdMlwvtq-Q7HpLofD@LZ$ki8$D|Nu`y#Hb+{~8egXr& z|983epf<4c87aFbWZBphX+69!BRQ~eAYZRr z9rO~-z{|m`K;*$J8e~6RPtiDSHq)!v*=Bn~$uu;STt?;VOF>R3Rv(Nq{Im^T%~&ID zIQzvi8@!ceJ;zuUO^;=<<;Jo=_OUF6ftXP-!|2*i2xz5`dFRU-E1TSJEH@xAmdl7) zqNjJ)+=wD@PqAI6^24-{d>FTUI@;{v28&MM3o69iYSZ~7wwJ>5`(f|=fF0u-8O+|f zMZX6RG{87M%B>_2$(8o^lYx}@V`n9=D8Zfq?!{lm-2cFmjQ7BjhxZP|4r~)3?k$ zdZA+22Z!NW56yJA)v{A;%SxqOL@kZ}{sM!m?!OJd&kQ zVZ&I49L`Y-SzoVrx{aK1>Pr0}h}CEtm{WIGOODJStev?yR!w|D%hE2Jilyal@z30D{a4ec{8 z=s>+)%mxTOW|G6gNuYQK$S6>`&f(=45nX&_0Sjg9H|cII#9&!U?C5thm`<~U>1_VN zbO_vFIy-<))9=+c!MXfw&1%TNec$ZPI|`q9C`LcG4m%F6ZujG?TqbTQf39l3de=C} zGnU2t#8-|Wf+;pvH}IMQs_zF*y8Tu zIUX)tR{NM2M;^%e!NO$lWp$Z{Xg$KvVPQ|bRiRHRV@M59n+og(Qi07tDqtB%WyuFp zVFKu=<`<~x%eKL4{O!@P2L9M1-*Z@%X85z}}xzOolP@Pz*1fMSe> zr`)3qQN!=wAm^Xdl_YOZuTH#Pl|eI zAS3!2$cXO9P~;=KM42dtwTm^o)&qbW-fWWMrY;*38O@`s(L8!X8`w6(rg z9JSEU2_C$}G3VmEq8Y5iVb>J-u!8Q9+nRtU%_K#?7k9;qRdY9UZ%rfRShKMmvc)Zp zarmkNgwgDP(P(yThtcfVc%#`ch<$x?9N3pF6UyHF2gWXg%ij8+`QH2ohJWJI&3XG}OYbz$U#S<&cVR*ZKrD?mM%1rDX4{0^6O z-`cFZ*>R7wRUezG*?#}eS1)t(jso{+ALL|p8?E_xA50(b1L)&@*!Fl|pU3<9+=rw4 z@P|Y5=p>K!WghQ?-SNIj9`BvxzKI;~-D!Kb*}kbf-q+{jeSJRO*XQHCeePS7Cu5su zY%6{@InZKiCiX~D0vEYAkCGy%@TY@~xA#{WL=SBmbZZ?>B*j$gZL?t&IfMgJJ*9^y zhXusYhjg1??m$Qv5y_{Yyulq@ar4x0(FFHPWS4Q0a#${WDvA9gH+l|f*`f4N39Jz7 zvf5BGi$`)Dcwe}+Vu0*Ah_nraN@zHP2Kx%wu&<=>tdYc7{NX}@SK;!?VoSrCq%_>4 z!k2*++FL|eD3dZeY=Vh6B2GOM*E>YeYCWMjY%B^%G_AQ^-Oz)E8Z1LGAQ!O|1==H7 zg1E8}F4e6ZiKE6B?TUn_;tDPvl@pBy_ZPOqo;J7k!G55dN{=rJwAelZt$R$mtP{s( zy_F2Owm~0=QIYb#=-n;TMo_P`3gwKvFT7r0LB?_N5R8t+MTJw!Ey3riC9vLi8_3Wi zUKCS6f71k7wwvSr^peJKJijKs_|b-=j>fIwRM;>RCPZ=(#AlnBLdZ`!XUN zHph5N8l78C9P3AC-~|*iJ-u(d7vlh5lp?Q}gz~Z@swJ!OOh7fB2`&_E$L@N1YWuR) zbn0sQP%-r!%i2?`8IEr671)RVqG*Lev8}w^hA?D+_u>G@+uK6CxBE z-_MaD30}gh9pMw23WWy>F)23X1BF5gdZ17UJP#BK-Yyu``QArBDtX8kxcOksuv9MC zK%S_B;GwKl06xN<>Y;Z9itV3cH6e$(Oq`3$chbb_rQd9WLO^|6#!lcD*zYS5kQ-=# zg{WBNeXEs7jbREU^;B-s6KGxo-gf+|7V|bdq?mBj8nykc311H=#%hHNdXC<8h__cP z1g!WV>XNgYasQ;&7_FOMc|byCyFbMZOuam-3);%uD5ZT|}f5jncxw-u#E^ z1E-z(9%6{?qj|K;86s!a8(q})J_X@=SsHRt0}ke>)oFb>w*lB=w>O%%uU=wRzNKBP zQymOLv9fnE`{zmrauVO6c|)_0!R!D9>2}iaif}WiT9~gx7-Y*--sneQ?9n7x>RbwbfX)ZUFQH; zd80r@D8H4`15IH@p>EGzCB7qxRMy_st>9!uN3~}TZ4JzB# zsa~ZvRei>_xbq;q#+9nYjWi8T&i>8|dcwFOPp=0=x`nO-#v5d_9TotiUYJfe4T+Ce z+)aR{M0JJ@i{~Mhy5yQUr<*F_7_u!2ST5~WJI;3Y5mn_vXf-|F-PD6Wdb~w5%CZ|5*SLb528w>W3b^OYOJaCXZP;x?tMg7A>FW4<;++2w~LdXSX+Ev7e|2SQ6gsRP$ zJjremC5Dzdio7@BF8PZYFL7PiL&e!?qHu`eAOngaphaOnH)BDG-+a+#^L5nEJle`7 z1aVvV(sbe%ly$fnwU=2#78=L^D!^bMBeV?z8DZua$lw^%j3tdwSq=>^l;w8CAI<*> z=8zI#I+MPo-R&goBHKw}4arv3)EDmKq{^q84bgA^%1-S!)Z2><6o6vUcEx6FLGbs>%E@xMBC4+(+8-r| z_>B&U>w;goFkjnQJ1-wE%-@TIEdv}+DiJ0Az3y8WeTA2=rWl3TW+lRWAfr!s0y^*sp{^SG_l@1|`{4zkir5PnPB2|87;{E@%o+3S`khRyAQc6H^GU%J=}@ zens6Xc-AUoZmIfO&jj4f)JiYmrMKA-6(HO_uyV5R?N0aLwJE9}e9q zJF>uO?K|bM%~x)sgIaF)SEW3QMV!YDl$}n?a{k{N`JNuy8Q5yJyBI7W)S$GHTTJVR z1hezFxP_kZNT^TR)rp0`6wQU-O(4h@f$lW9?q6Ehu8|%9ey$3%R&YYV8qQ#UHLsrW zZUj*i!{!794W)BA0m|@Fb3y#xgJ_p`Jk=ae!U`Z$H2!m3eoN$kgYA63QcT+ridD{l z0o~@5c*J52tMiW7^TRoE=NisA>L^R94|1OUps+xJc`3?YcOZqxgH7TfvGxKjrxsnS zpD&ZMYE2w%(Ih;6DGdWLVgfJ1Y}$`v_R0378%*yD*EIAFmJr|3!B=8dRMj}^VUxLG zt790jz(9VcU-exqY5fh3)!$kxxv~~S*H)u!7q3DKlYs3ke44&a8^am&t56y$B#*Ps z^dK3%=?ryanl@t-htkSpe=J0~C0=JQWZo0X1(bo%C1C^+`s_YaUy$2L zZ1z|vi>vY|Idx2MRCQqZO0*0{w%b?_QJdK&{Hj?K+!1U%4~wd{cI2d?=@Kfe3{rxiDm?!HixFSj|my9j#Vq(pd#N=?vs)z&bgn63{Eq zTUJoOGJQWrUCc3?UBzqH-IvCfA(%LNdA7|bQf*zWqG@c>fR;lr-sJ+K@2}vlgrRg z_$D6^{8eEEOfsH-zR{z7#oW>CD#%sPq$bTef`2TzV)EPLP_n2SdASz$(v2JVP?5lLu_|zgIEBSUha-qiZ=$tjqYCIV zo@>UvL#gGRUhX6`z}sauBOC!J=SF~E%Ofu+DNz1dY%bp~-V*lUJLC!#@bELeC`(hL zXB0d+{+731FDi4%7a+TD+R0%D519P;dKqsHXY%jUg^l~nXe0-L1d1Gh(>Tzi^}a#4 z#F*M`Op=0K_jM(l$2t)u5MgbBi$%GX<~8c3uFrskJ?{(-r3GiJm1{cVXjMiM2wXAP zHk)G|Z?~!qE%taOGv4Ngk~7QO)o8LQYmr-=90)hYIq->`UjpxvKaK@ z@$pxZ(|!|IEe%ZxIhLdywo_nS-9(~HopnVaDYv&u0qd<4lLP{8#tX>Nl!>WC*82!7 zssN_KGYL5aFz3A+IA8EgB}>Rzp>UZ^uk+dIEZo}84O8@aw24Flk7s`suFq;Z zetGLE1O?tnqp9#es8&uxQ)A@dcSnqv%PD$OWbt$b(|L1VNBdwI$Vek96{3=o-`1yn zE@|Amc-3mQ)hRqgRipfR2q zJZ7ZXI1-FxKC$R?a{YdOb+@_;DY^UlQS7Y+F;5)KiC-|gkh5SxN|D&~<21KLy67Hb zYch>(+j)h(&fl+ru#4<%_%WJ&J z*NzgV)A5w#HPAJoj)QN%)#KKVDDVkSJ!6HvJ_gglhrx7aJDAQ02W;%1+(0UAw`8er zd8Y~!c_%`xk=xD06m7sTozy~@-EcgHLJN&8&0a@f_pl`y4-p1Lja>M~fzPfzR3x)V z53OB1K@yOk2#=F;!Jmz=b}0EBRV0cNic2e=sW$ zc`yqU8}90F3OzWM71I1*gK+(^TB)5HtR}&Kv>r%0n%CRbeTd|3)ZzZ(kTRUN2Pk=> z9j>r!!x=y%GJd_Ol-YD|Ahnj&vA33v_>57!oH+0`1o?K-df?yk zUkHcYZUuI;dkX|;5c@#tPER{xX|QV3vr`)z;QIM2S>Hl1G*B_b||)W{oz{5 zsPZzn%e(KNX6FkWZJb~~&?X_KBKw}_dv_{Nlphm3f zKnBY-+(*y=AcusG3@?WV?&aZkySdBbxyCoFeQF&c3?RDL(-PBpxSfLvF@KbG8$6$cw+ndfeMaQ$rdyIZ=R4%butM(Fi z8nTgc=`rjB0Ccx_k};3?;o{|T;ZX59gmAVW9zt6P7(ZMH7(ZMHe0ztp?d>fd@_C-f zN?m`3^FyT=QZ1r5X?-nOZLC@30BxJoGfx+p(^9FIn-lL+NS)){DPS~ts0ibHs0e_2 zs0d(ts0bUD)80p(;qFHmYbTrqdYh`dY06Od7x*@xfOans+XROjF- z9SR*@r9;hw>vVMG;3^&B9$clvDJ;@_;fq*;3|81*_KQ~%h9LHJyymRW%7Fp1Y6!y zfC=nxA=Y*G1A8@R9Za^iOdo?{16}dJ z%=G-jEFJmQ{~sX#BGN{0hnFwC+0*26@gDs0PGNF~cPb?fOBlz<@Y_1&j>JlVWLg56|b?Y5Lmn=n-gH90BFc5;ccvQyq~>csobdP?qSbdsZJgq^{Wrm%X ztEpdB0myz?1rRtZ5KCpYE~d-{t_&BLg)?ke@qIY7s7ck`5|tCaUATU3X5kHhAzf1y zWDY3et+~Gy!}QJip+d|u$!(562GMY;35%dmdACGMn_piRM0mGK+NWt)BKXRhfZ!`@ z4$kMG(Kayky1<=rXU25eK;vs+VfmY-(8hJJZo`(=(q&_@Nc7V2_EfQU(m|vx(xFVR zaBilZmR{lm_U^;9KJw;xP2Hv{ZDe(03L6bwHn#o#l${59JiWX9`~2zyvSq(g$V=?a z^wQ7CUE#7+D3o0nPOp|ITVry%)E9~9I?d0jJ`fRtxByK!DX8>Tie#aFnG6TGlL#j5 zmIO(56GfJHs*7gsB!-aFYP^f=+D}tASi2j|_!DuEoixpFyWCJ8Av^WkLoVsp1!MXN znpdy&6f>H3LbPlWd|l)gU;f`$Yg}#glxD%$07vfl%3J8QdpW)=dZ$@@Ae%+nS3BUC zU6#FE17_Nb=d{ouu2(;uRT-%UbO=jLHi6;1Yo(&2aN7Ej)6L&+Z*RIQDLyxCCo-d+ z<8BLy&U?8{MQY+{-yTd@mw#R_+Cn3;<73Bo8qWkmp-)v>hw~n_{S>b`QtmZ&!UAcZE z-^!<`Tcd7=`Z>Yjj6M$4QGP4$vf&ziTxx0rrMEwKE^Kv$4W_m;6Yy*&jpldKXnANX zR3A!)2@EB}bh>2WZyh@AV7{L(kr%Yg-o-u1MQ(PDYb)jZuB{~4rWR&nn>TyktnH4Q zYdrk9BHU~15c`eb?#9DS(`aWz+D#m2)dT4_$pR0#(^3*d&vlaFx5Jhi(zrZNog&HX zRtXhq5sycAdwR(<+{F|McN6$=M8yhM633d)+ch=LtxcsPIZU+8(M6>7+!xjb9_!=x z=I_)mPtoFZrWULt@3*j1qO{SookBh?^Ma=r-EEAQIGP+l(c~)^6s@{}@B;H1PmoWD zFbOt2*!~x2Wo>S(`S_yu1aS`_Na(wf6N-&Dne3eS7)WvL;uxi_YXJCtXnqCXVpUAilI&w@10`*7RF!5BtkR z|NG0tIQEx0WIS;YtKL@M?)YDk@`aSATckR?5(n>pZvDra?yvt>;;qF?D^&?~rAv9w z9sTuxOuQUux<78=5x#{eUX~X)*1zO}%5pwEI7cfVXMN%z9i+9R0*Bj1Ytiu@$ zVmO1*4`)DPhBG*C3}+y*E2Azvtln#HKG@6AEQ}BXsX2v8DY_fPs+Dep!p@YAw(}pV zpODyEg%DiKU^J$ho9E!-LOqz1Jr2~_?yGAp!9GwY;XaU)fH!F?!Mj4F@9)c$aJ!T3 z^mFPWQcvduJ;FT1iif*$VUVyCAjRO*N;Rr};4!@R0df!ZD`!H<%#BL%RBi7V67B!Tz zUaWJIc9i<}>N%V2$I1D-JdozJdW!O!Xu*_k?=_b!-Ao;X-`49Jmh~K5_f{=d$FWtg z+$npgU~J?v@YU+iyPFy#W?vqVjfepu?8MSBehO(E_ zgLGHy@cCsiWDb>K*&ZyzI36qm^?R@kbTFaW-G8jZrIYbYfHj`UH21q=KQJ&F5A%XN z7KoRyk?@Cxyskvi6#sI!LHD9$t&evh3&w?Dhg;UFd)>;ZBm5W21@pj#ewuo2PE4H! zZY2G&Db-*VvZA$-m>2QjEmg zo2@15QTp6D+JBwaoTf6}%ip4>23I0#3}t4$5SxP#^TyJ7apd2+A)iv>-V)5gitliq zP&jxup0B4!CBB;z^wnhE@XR8W>-jCo`jr!}lBq}p!C%%qPWN%vN*q;w`*pb)D-*(2 z*kv)k5TKH^`a4n?C#ZMCR9&29=;x4{VHX@_zIy_Vn=QF*BnNNeLo2qW$(}=Ywv6Pk z(!uOa_hL2)0qyRq5>!6arlD)?$l@|$v=L$R@^TaEM)Wa(N!dyYEsQ-jdrHH4m1u7} zZ2zj`ah*6?h!6@&Ez20mVHmZA(0VV=^ML>_Ur;$yAb&THHrP-cH#wducGaaX=j;Ad z^Odd-KwoVz-Y=_Qv7i})5C$0j@kU!fVI?N-R(AG~1Z z2nJ4Xxg*nM0G)ta_(a9Nc>Rg@p}b{^o~6GDnWWS{->kJ8TOCuZnE%Sse!z*iXg3HRcqNxoaRFXp02ar(Z(yQ?G=Cr6$2-?2D8_ z$kuunDrgDQ#%nF{;V&2n_B9Gtw))w2^s!-^=>!#N5(4H;Lhs-qp!N7^Bml*CtA92I zxDdH_(uT?b@ZEAl1zCulf`}J8X^0d%X^0U!X;>k5(l9~pq~Z6nla^QI%al#ghjko0 z56NU$lEcb|yw#R1>VoFvETG4Q>aBM3T-fsP(D`kZD{Ur!D-^Yw5!?PPcgZLu{sPQt{RP3<;afqx{iD>xyXL{}Y<$2m1hM?bhtfQKoI}XMj#3 zN3li~4%tuJEmwq>K`huxr1lj|rlgj~hmH%SZV<%K4{?7xwLXD5kP+h>$OufYYNe`z z>E!&1;tv#xE*>Z})}5#2P4Y!VJtibSnUQ|~0&PE_?JYd&8F;NH*h&FJvXyEJckngW z9ZLQ}V^nU@cugTcYEORw4iB{3hrt)E-dM~37Y4f>Bo29A&liY<$5T{QzPG!YeKX%%cMdlQbGGx0!AAEYG@q)N6}mp}V=56@}j))^r*oL3bJm6%jrn zH^g4e6Rv_nL_#5qtygK0DEnzijN(#Jr8o|!XP#Pw`n+~BSOP+VD`Fg&f?>9w^w54% z*t4sSI+nM<+$4xH+SYS9@vei&mI*yj=%LvppsYewN19?V+Olf1+vx?bpLp&?gEgEN zeGTUYY1Lrk&4^f)mWA}wX)D4n#bG2FVnz_2>&}-J<)j}mm!Zf>*xl1a-~^K8GN8+B-~C^ z2>oF1o1S~$0E$D$#%WsXaH!o*3J|x_#S`O%Qi@w`fYl59#0#v<+%4MLb+C=u_K7!f zv{Ft!(L%xVXthJn47%4PL;<^^xn^K-q(?x%ZS52ly7^~;-S zH2bZP_;x2fE}W%J41Hu*GlMI18V!4n_3{B=?;CvTeS<^2?;fR~eQ>3h4}SE%!HM2C zc+mUS+V>NH3GA$$*53-#-bx%S$%J;RGppT1rnZ~N{B{$=X0X*eGu4O_Z5@QG(BLGQitfDA5D*6B~SlPbq(;^v$j6-Dd;-F=t5n$h=*6aEM@f&AA|a+|>$~Vi!Cry;I37ivpE~dYR%Z(k9ov6!}I4wOfOM zBenCCCqM|wznkZZ!3K;Sfnjw(7FXjKe7Y%oW*a@ z{NtG*4IDA$|85SLjh0t&A+j72-WSd&b{kcG-W5?ec&3fO|?tO2U z2W*vFfA7h(pVV$YdzE>uyVp41sj zJG3aRzQRuZ%j|O(wSh_Pq_LDcX~;}FX^2caX$s`Kn+RI%CJt6d;@T}~%os`@n$gh{ zJ!ddf8ki3y4-W2$lcIRW8)cG8DJ34u}v?Ey2%Ku zmo49FLz?t3f_?%+?I#o`xVN(_WEvj-L)lg0@G)O_{!os$)|=^ObJVeW`S}0#Sl=E9 zHQSVEt4(1jTV;UDRtl)LmBLbPrNjZCZ8@a>HYG&=HYMc#HYJ7rUefNUwb)w&(0Xns z?G9WIqimPj9ePHz?hRsh@QPo()m8>0Zz~EvwoC1f{&;uv`FMD*t=-Y<@w)BicOieg zJNo0@(d#L@t+pQTj$V)KY?s;{eZKzPj{foP=pXNn{_*bU^#JpBkGrFPvOD@GyQ6=y zJNhTPqkpoCIZqCHtXmfq^Dy;t`tSQZvcYL@SLdrmT=16Zck}4f4m5?wP~zhp|E>S{ zE62C5?Gf0vr?xY^#2SCRD@+*-ljplPyq84TCa?Dyz^=Ifnpr6*0y<|N2&x&ePX9aH z9SFIy>jLYU$uSxb2lcgnGYHHK~#IRtnX;K*=BM7NbCHZ4AW`?Pig4vpr83$P!_ z`tA6|OmFz*D?8u%c*b-k`u)20vzejQ-bsti(k~aJsh<|Ksh4KtAJ)y;!VszH$-!IA;rhTkLh4+bh@=Eo(0Jq||`&c51B0c4{28>2Dx|iS~$? zCA6+6#ztw0?6aG-cLD)^LFvGuZ0NzZ9b@AQT7S}qN`dbF2^7z(9MqwedhkE2QgD$k z>L=E7m;9AgDjgdmQsLXCMVD<#w75guVqtcYDZNFxXmKlrs#_@mo3_{86kqITZQD|^ zcka{cb}WBeN@?-a?C>x6X%)$5w@rrI4Iu=JE>Kz}sf9HaEYINNz17zk3p;NRyrlNG ziBik9-)*Jj^tQg+FFDa#$7z&VM_VoF?LtJ2*K3r?aZRG5LtBYWNm0uj}fTN|4A63D_H5OwdwdFmuW%;wQJ?|%eekiWzX zH^bHSFLyV8UNsec@zRNQGTk~&_mA1j3sG$y0u7tCl`Pl=KnxMO7GatX6=Bv{WVj3Z zE*77y#;FO$|kOC{+&a9d`v)7Ou*LZU`rwdaPKVVKQ^`s(rg zKmYIl(1_Gfe@r@iCnQbYj6>cl@iEuaTwidc_*2v<*C)pH^r}@Xdwbx}H>{v)Vk1KbFOm#5X*6bjAu1BF6U z|3IP8-BVQ7cgNNl`fmrEsIgy<+xSC+f^5zZ?<+vJ*yn?bgWJ>@#k^|p1P@3(D8Fel0&qcEsD}TmISL@ly&HNv;;+qV1?UFswTTjcU9EAk%mw8h2MWf9Z z_|tqblYA^7#X_~fFgwcQ_Z8YtlV&i(eSM=C;%zQlQD*9=fb$bE(_F~J3!GE<870s{ z^F;&A7xkMj_H6Tosjd04^1Anp-L3b{#CzXNz4wh#uJIuRr*2{s_xylgtwUs~Vgki4-(| zi#-L-W_CZ#r-bw0+|JL-z3D(Rw#{Hx@Pfgtu+$D_r5S%XkLir{R?IAe70bgWBbh*M zyjfOzJd=eV&kU>eNT0C^Bbn*(FqRuTW-NE_fYaH*H~gDrJWBn{ZhA~&H$BGxk|#`f zVHXFLDWaGNaX8Xxn^;H(I{V9LGp%k`i`5l@F&2etTHGE?r{BSJVfs)ztdN-Di}{*R z<@5FV9nUj&d{g5Lji?HZb zEnH9A&)6Cn(@{^6HhqLADo8h)eeiwoJ$nji*S^t;h+DjpLa*|_vQ}zY`di9e?tVGF z7W2P;&+}(!HLWu;aqN6|z2a}4y|mN@;EP=v&8as{bx!Qjw^#I;mHR+Z2l zyaeXO$#(|i*Q&6Xx!L2?6`g}`xoUlyE*H4%GmtucJoV~EoGdpX7qM*OHLGE*iZhPR zF{XUR@$x(tR=C~!23>pKY@FUVi{1MMWqaSus`m}1_P&`ye%C{HA7i|a9xBp}aq%skWc*<`|hR};%duaSx_r8V`GbWmN-r_0-Cq;O7l z;MJkbH^Maoc9w&AU&lmnIaa zXRk|e0?*DBSxGQO)SJ*W_X244#;Ez~Sq40BH+f(hPE9U)NKBkn+in+MI`hLzNZjf{+3J3RyTvLj+ua|KPYa@g1D9yOfG0j);N0`odM@1$eA9={wpqVPNccxEgQe zZ8l&w3}(U=!dp*xyA|zQn++y!rWY?~m$wr@f@`bna}(JJHB^ZN?hkz5U=Up0fGl8v zKHjW0xKFZKH6D6(#SOnhog~M5ULUzA)wNw9Oq#zc30vuG1lX%@TS zcf_vMr%i7sBns4BDwy+M?u3|=@j|@{xiYrnv{>&|;a6NN){A>EQY4jKv1I?Cmb-no znsmS1f?~(rIpUw^ak}|=hEisJ?usRh}Mr8OQ+Di!OW^zVHi`f>L*@!-6o9r;V zsZL%_@0RBuCa>m8cRRWGq(=z3jbk$ipWuJ}5^2}2k%i>f+w;Or^!5!4E5fbiK}I|K z0AI+rU!C@Lc40&WA^h~f>$_WoxWO!+x{ShLl(FmG`fi3vK<&Ta377JFb@K=M;h26P zjVWeDctBDjYKdm*Dp42~CfX?81zMWI?^1&B zkr>%Iz({(=B`FT?@sSLBB89+wiSw~wzcoaAeZ69(MYl9-SS&lqL$_3EzN-k+w;$fF zR*VqpMw#;kn+sB?CTqFPL?Xqzm04IN(U~X@*)C0>5y_$n2`C~LNQm6#gLx}2NvTcs z1+=p@-m+%xid@nk;;(N{odDsvoPYMP@~2vJ_{-`b?e<~N?-amo0*2HHxQQ?__$NNv z;vnNe{oomt5Kfne)f_>W+|Pu zSmBs+!kI&Xr@tUO;c@o*k_b)wWCt5F9Hs*9`}F>H7QaPr1n2LbXL$JYg7uO)UFaC%r55TyuoFUV>g|jj zz3iZa3I)~~MD^Kpu@JLOmJy5LTBg;m^mFh7_(iFwRVfl)o9r=a;eAj#ed&dq>KG7G zp{!<}bqI?ur|)Nr$xEKyxtcr|XLyMZ=aRqYf1EyJk58tXr!O3c5( z3y&BoO)TH}d44?Ali4O73&$Y#Tz;BC#@D!Fq3g|Zu!qUhHRqMtA7>f?S^N+j)%DXg zA7{6uMjn5coY72fRmLrCB9F7=)79r}_9aI+ger0_p08h?!ONyfzw;!^)rfb=h64;ENPS?av53x0OY>qnS^$mx| z_kcsE9rI3r!1`ZvaqBX&6pb$1HYd#Azs=XTJWEH##o{BsA+xty$7p@SXrW_*4A_b; zXmS6al-65K=4vXL!#P=B-6{HX(23_C`I&yg1+PtW>uyogge1ygWFs{fQu!;wdhAqi zPj0hC4y}cvE75jVUvg)^G4#-p@7?pgYe5&J?nlPN>>nGl&_i92ZR!kU!D zlD`(wQoIeF#QcUs8oz&UU)5D14@J zvDQe=Vygat5;$O?o16r>bV3x}3@hOTp`wMlH!lt*n=2E_r91>TD!&u%804Wm4z?V@ zuBSmI5QctXrQUs*Em1t6+FPPS@F3D5b(NDrauDsPnD2v!3;rFj7Oj`xRANtuEfUCR z`O7EM^rzH$nmB?6_`O)=NZ!5R5_AX{e#hh-rXDjp`}@f;%5F`cpO3$PQhstp!+b2s zLhSF<5fo*B)_y3zuS4()51pJ64?b9e$=#qAAYKu@ga=l-fw@?bX_&1@VLk9^SsM)1 zIIQD9UcoO`plcSd{&YL!)v_o7An(tY@cGqJy!tn( zePL|SBvu)<0p$E9e_eqC-MwWE{Wm}Lq1_OOd@Gxl1ec5t1HxoMXs;Ay{cDr{(qe0qR|ks z0xC;jpQ5_0Br(&4=8+~K_;4IEFe$bWYAEE4tSZ877bINF*j2AMa)e--HVFYwEp-@? zSn|v4R?io==p|W)LyHXtLY8a-wu#TbTjA8Rmb9o%sy*?^iliv;^zITU7TwTzuIk2U z^a}(YKy>B&V>Y`vS#Sz-&)pYwg)ZED=Fp##OTnQT?;BpDr>W`8B6l|0uqoF}L~XWG zIIQTb*GW0QpIOp4dMqGFSTaViGcQ5|=mj7vraBCjmnf}tUIM7!(F_x) zzlGYcdCM2{RCuj02GIJ7d6vGSw)PU5-aiXMp-@{Ff$iz)BB)Ix9@An7CUoyG3ZQ6f z3$Hd73fBF{WiCtlBFgHtFlkvWG=*pEzB%$lUS$hBL?(qz&xxyi4sjHIN=$RJ;f0?F ztOigbp%&WFXN4RQ8zo#v!7?T&v%EX$o{9A*bCUKw^0boh2_H-l4m9p+U`9WQ=IGuS z6G!8CNen&bhxv^(6wucB1cOD~0NQoMoxr>5pwY=b$K9 zW8-*>;ykplVMaK+y=OxR5= zX2EE?P4)GQC@pEuwh5=XXibWnc94Tu`I?#ET>}QK;#r&vxW>WqAs}*8T%Jz=Pe^pS z+|>U{JO(irXtFMB&5Md=s(}+B$20pi>>-(Xfkg~OYg7n#k?p;>5m~pv)@V_ZO>y={ zRw{JHvq8e=U~)E0g4n?@XZETUo;oe+3>a^1TDjPL`Y0c1g6ppwg^^6l&!Z4TPtKm5 zhWfhGyrDSrKy*n~qB57F1I#*<&(_+^3{AfB-*O<>5d)6=z6blJ5Z&!Q<>cAz z(L@%`==1Eu?EKGe?y8AMF<-t}p%n_OusoF(T0d0oMl4>?LzusW?C2R#lWCJH7tx}@46b;MSIX8IJYy`NcyN;ilW11Ob z>Y7`T@SM_^pL5O{;dA-eAf&UX{iz86PHLfIy|1dh`+(m3h5=hQ_4e$fvNiVwE3mTJ zJuF%5^VtPyziy^1aE$uy{eL5{khsM~@*T2-OU)$!&B*ngG9+3T z#?8?-&eVBy_*}j~Rq;sgC)|9nwyf_NSTG%Um|$^bLj|eGW6}?3r`WWx4zHF0)~6yd zWC8oRjVf=asHc`!mSSUJ-=vK>=r;mx+HRkHxDzP6k_}a59y*kyYN-@Vac z>b#Ts3Swww>;dc*0^qj02aquTEIoU+g&$i~nEW5B%ZoT+p*vhlk`9CIsO%RK&SVR@ zE#6iokz&YrEOr*+^w+s$^%HBE;Os?dS(CS2wMy+RkFJu;+@yq+s5;Z%6$K-LuR+)Mjtb}>E|qKCQ^V5`uw zs=g9ii3~&F=E!OerOow|b}*{p5D~%!FXqtgIXmuk=G%IBFNclJ45OMDCvfjTee~nf8NcI!ZewaHV&POC*|q!ylUE2$2#@hMAPxJIOfRe zx0yklku(?H)T|88Z_lrgIlSB4`L@@IJ%^nGDh~*nk#9eVl+brmI$<>Fp#pG(cI??o zvMm-P$v@`qH3cOYRBeXDR*GnTfo6sU&Q5laTHGhQ?r&}#6}7nUIFT5d?^ZCx*mP_rlA}p^7;h2M@Q@H!;H(#KxYqKS9Z@Taq9uJ` zJp!dMWt*1DoJDc1Ff0j4&_mrp>HMxEAxZOxHtLsbY36Dr-`)mXnzoW~DKqnio2cvem36Db*@ zUgqxJekFP4GAdu(+6Sc7UWa{#kj%W6Uaf;Qhn2NcluJjzCp@@ohh*E|YL&E|p^5C0HKk{g-gt8Ez zTv=tR!X4fm`T}<_2PcU;$#h<<7OD^bj!)Y3x?OMH-{JUE`7D#0N=KLTWD$78sJj`n zvV&O36%+ssqX>;n3<@-LEK_7-_3UnPDnTPPArBAEi+A5$II0$xe*)Q+_KU*sdiweI z`2}FjQB{#?GF_Om*ijp`EAEV8H<~OJL4}Su%ue3VK1}gjT0<2Ju%%R*rb*jF(F|fL z0dyu&luwuCszL=4?rw{_3*@xx_bagWJw(rqY|y{r9%hK1Uj9O8NMyX6dNwG*Rsk*Z z#4(L%Fd?8zrq*KCRTZFwLoEp31gIw*q(UX2LYsncvC4^kWR%`3$a6IMZd>*57C9>F zvio&jGb7qe7VImb-vvwI({5RMq)oBEfMra>ug0=yvJ3{mj*L z4-c8i64Zrs)S?CHDC^wKXU=h89U{+M#`DEO+JecNhHi0-oqRN?FNy)q1G(NfD1z1i zUV=-O2;)0I95y`~!Wp*t_6TsF_0(^(w?u@d7?eH_xeZk*qtZdo!?dvZDn_6NPnY_O z?WwN#L{<A%+-l6AMEh_;#yIHZUXi_^(&fU!g3)(8%Lf7JU*+R3)r^zQ> zS_xm!xh6Hg;IGIJwZh7bq^m4LUKj#r}b}#3y#u>E-k5 z8}zF=SU81wyY@@S1a-`lOFvxBhWAcmTgfHlboqLXmm^NL6Kx0sT7NuB`LbT1hsUEn z7%ymOo0>+yOczlEN{~n2?B>~x^6hD*j}tnXTyuS6E(J+fVzFt%XDzL7Ub28u5^b--IE(=y6gIfv{sYtP4Q4fE#8sq?ak_62oAieqm*RG5z zQ%S6~M_3g%jJye9ctxX)+=V+$H;|x)H?jFqnS+P_(to#97CcsX ze30tV-MYiBz@^nOC|Ehsuv$VglUP4pQ1CKBhePlxq$4ug>N2h5B#K6ld6Bu+RLP;?w17 zd4G+jSnH9-niVaHwB{$`X(_MN=Ru(ieKQJc+Pc}-!icLDe7(}fXeJb}uj6rE{i^T+ zO1>po1{#9i=KVz29y&f)kyQGevBFq$Vc8Pi5>~m`@o{8<$3-GL*S^-pIi!3S-Ea@` zZ6*WTbZb&_@C}~IkUEgXDLRDRQdV8WGO`V{uv!P}0@f3+Cm27yh)uCRpKR9Wufs9< zLZUi8a`6SaGaFMyMC^av!IE@OEXC6T3onZiWPuUSy;L|k4;6kX3$ndlzmoPCiByi% z=VcT%ZoL+LtY*U(+ zFHboKjL^)QPW&Z+L2N*0U&K4vC?T?*6-Dr^#`kmvI5kHM1(RE_*zyQC6>&iWR?tu4 z7d+RA2!j_5upzZs?9H>2d{M+_J1ST;R`ee=3uhiYlzMsYBin>!R+Ui#ut`d^FD111 zGpUpf-^hWmsFXFPv<+TXYxR=g^)| z^ZqwSue0;@{N^^kORe5MyfE8!Yu;;XH{RPzxAt^jd5c(#4ep*h?Lrp?AgvdZdtH^| zE7Z25cma(|1fSrl zuoC{-g&ZcaiG{d`i{PA0;!J1$E8l3!&(>=WRd<9A3ISDLlXSl7j(Vw=M-J&KBNJ5%?-yu1z;Srz_khS)V2@}eJFiy z_3nCdxk385QOMLHSx??G9rk4y;ZR|C97bdR3P9VehtF(#jSkHMxD%AHLKIeYf!OI; z(OBItZm@p6x2w(@L}ZCIHecB#_MFiLLyX}B5rw%2%iK(s6B;_wpX15J(c}UlwyZuZ z>_mL*X|V8+IWU8A6VQ}zdNi3HL&2dd)q^|M!jmiVZJ_Jq5`NvedkYe75FQ-Xw?LQq z`Kb)Xa=%84<(&v7%w=NG*#NK_hDNRVxz%D#xlXU+Qs-0Hy`^dUzUR7#U3UUbJ>{WrVLJ(nvM9ce3Ldf$lH0y8BvCu3>qp@xDPAs4=Ko*wv-{XY0m(W$OFWt2Vp=aMma}R z@z~gUf%U)@)lOd2<~x08WrM5aM4HIQyr$HQm?Dd z@$-2;hMEHlsW3jkB`X2zXs&cHTs~{dB)O512Qa77xR9FjI!++d^$Iyaqf}dE#0kuf z2$u~~g+O$ToHCKAvWpm@EVB~f8bp*B_y(yh@a3b3Ozo+YDQZ%58Lh?dnsuAxrJMpp z{5lL-zl{TSMVYl^C^*m>8Q-gomkAWT;JBDZ#Z;|Ztl)`Ml}qB9EU#d>R=>r}}G-#@1))=Ixcwb(=rucwg(3@l5zGWtTGtS{oEx%OyVP z77;#A?hCqEaf2cTHOxo&+3_RMl73nx>p!2o7Wjw;Nk?nn+QOcOCIvZ{A1KQFqAxxWQmUkO zBsif27%61rnpe3HrW0;pfn5UZ#p>KTP0s)@#qCx0`mQI4PUwj#K2wzCa97#wn>g~< zzLe+T1LML4v&{Q?=>zeAW0K^Qkb|=T>;>HE?uHbdwwR*!wT{nj`(zWMdJ%Ya&$4V& zExq=z2r}H7_$8j{d%$K5dGgiNSRl1+{^Y_1bp8rG6#UEjHAxHD++-70s{CDk;?l_+ z%{reaGTW*Gf3GLED`6S{6jieYtonIYX}6z@PFG@T>C39kuFkab1?^fXx;rK0b=+IW z@&IAE)lNg8;N~q(J$LV!(g!P3?Vbv*pQ`+l`2{;oS7A{ zm3($_uNCb+HeC3xDn=4xxQQOlW>?x&S0H4 z{M*$6WZ!DyPSEsGg}WIr?%%9v6ass;)$Ip{2=tFX*$!C1A}io|h;>AflDD z0SRqG&WMx@rJ_?M)8BF*8{MYa*h@e3V1+OEoHRx#%$oS>;33fr`A^M|aT+aw zLT)!P_Jm2-N5Snyb z$}JjeJ*a{?5}p-0;bjiCLcq98qkhGQ@q?uLQ*D60h?le}7B{-O54zbY;M>cNuV4ck8YYCyBe?1&3oR>n!Mg&dV>S zXN%$2j~-Pw-0UN(DRvl)To-0OhJ%E&L7$XA&}*uVv`AKxl=M?8Yu{GxVY@$T}n47%W~bNj7cW5bf`YV-tYl**yO*SyQE%(%+;dX zPWno+X(EPi7?^3YM9`u<>Ve+j`;c7Xt(}T<(POQPGB(hn?#T`>)lnrm##!vs+<&^% zB>@K7d;ld5uxCQv5YWKc#_!W^U5db>%N@9mCYwV&hii`|mU48}ky-9iA^A<~U5Ffc zFr0$Wxn`)vaBWH?FODY#*v2-vp?$B|^A0l@k@qEkTGY$WEhOY}${hc+qT1!eB8lw09-Sa;?^dU>oTvOp0;grvVw(`e!=f(} zvW5c=zZb8(e`&uNPPuY}h!4po+3LpUvJ{+YSl!CiQxy94!;;-+MMVm!B3hmmvW=xw ze?VZ00ekXcMU9Kq?UH*w`uPc^#5qqM;GdAc%K6Bi3T20iq_Y`#>zF-&Dm6SXG=w7$ zM0#8l9Js9q$xfP%Y>1nPM&*Tw&sdYYOMHl=zsAGlCwWoSFROX*Yvb6W^F6}nyHYT+ z02rPPn!5I^@0*(z^Pu8-?$$$OHwov&Gih<-Pf^x?5bF zq1{qcU}nv-l#2i{g25Xe;_c`Yw_ZL;mIwX7?=1_ z62NVt2US^cQnW3p0~`RXBrQiyDD=omHiUB-U4=Ywp8WBw=pUtXwVuIj==1Zip@2#l zj=dIe9Z@fuT5?4khFvO1Zpm>MloplYN+3GZo(2Oow7hCqln7I6q%?M%MdzxovBgN7 z9lH6l;lkxA(n@F9cP-K%X0}{sTw0H=P<;XjL<~ouOT&A7z^mwo^)A){)7PT(%Vsgv zq7H>}Mf^O@VRCbTv#J?%&b#a14+N}8V zMwjx-Dp=4a)X2hU)S|GQ_!X2BhKcbWaRxOUI#b#JyNaq4eo_SAnu^;L#5{RzY-~`Ft#&UB#1I^T`xh|?BGvz7C^-}t5_E}`yFG$br4l0f3yj3 zz(Y(n#p4);san%Z=OWaHrMo)sUEtxEz;(k;ohW>j&!mOVt_4ktgV8i%WOOPBAYM~n zW$Yelj*^(jx4J4tHig1;C(@mil5neQw#7-X&ZFek^99_vq}BaU5`O%T4}WlR5x?co zKy(N};PTNtLWz&&5zTxwk1*z=dBiUt%{wA&{AlL!zR?_CdRv<`)HSH54=oJp$`l5{ z*x36ZeDVR4HAthmLxEX9s(eW^V&09@xg7{>!>+_JG*mFh&z&MOe1xmcP<}1jNs_ZI z=V(6{;T4g45#A+~7LE!l2S}(7Y{_soR~H3wFqnh)9q+31%sF)GDRBowiIk zvgQj>I&+Rp=r~>73og@bP<^&yMOqVqX6K%ql9W!ph_L6O#wdFlQY9Z$sjqYQdjPOY zG|HiokDH>hoMDB+sO953Ya!_!Wo9?XoV2IZj~xFzam{{p98Y{><0`Jf7?6f1|#_cFSL1un^0L zaBw>EBN7*`P_{#odhr#+EliB@1E(ZQp~h#Vq4f#QDVVP~RY?_u0xRT74~A~9X4qqKX&?KwH35z_twVpJaAW(p^uEBE9ZsA?SKVYEy1h^~SO7{hquh z=WIR=Nt;4Ec)S@0Rq)<1rg0!02$H39YNz)s$n?y9I)Vpc!B2yjE!`{Dg}2z?IIQJ! z6@}<%yS?xRM<@-NdU~l4Gg3kCo4TG(8{LJ3P1=)rxhM1Z8;;>7=qAB>Z9^tLjM(6p z)W{MFQp6irn<jvFibY!XT^<&*n7X-0H$?L?`W%DhnYGlZ}p0_0*b( zQ?Y{(SU*)`&}-Qj!nFMcH+NV8j5xuxk>?R_bTpV}HdQdPXqzzI2^S%*t501c$Q082 zS;JL+u2DE9j=m8)hjeh82%$8bhxxjWW_T0@3M_pK1aN$Tj-Eit2Bgn(-k@$z@(P4Oc);Y^-48*eFuJL-mnXz-$x5Vu=tgG zCoP!;Y^tMc#}GZ&wuGeKQAsV!t^B` zSk>5fO(G%#^t+Y4Z#^FrVT2BY+!#|Dr`4EaT1 zlZdz$(rytbM5tV>1hII2l)g~=8-JlRJ(v43)W(}w{@5S5p^b1&+0)o5532|(xnK=JRg_b`hdSmr;@QoQ)?d6c6 zt)wH8EJTfqhW$hf73%yiI08@CWqmKZpSLUAAMdz|q7L{VfJdUI1qdl)L<-mzEIUkC z=3SX>R+XkJ3K*lcuuT~6^xCZiNf8jFhqtnrREhT!f&X436+^Q@EIw`#T8tnTs1YFl zu>^EDBK^$0+&BZruJGW>v8I{`8L8Cuu~zc8sanz^IkXa|a2u>i)51k<<)(Ngl-u9w zu#m04(9!ZaB0CbC-Sg6sPG<%^^@ww2h$A4Wj~yM)EGLA_&;Zt}MPgjd>=y57Uehm2 z0wp&ym~gETSO1VwE^&U=^soxc!SW{dU0ohK<7+hpnd&*Rd4;Q&Nbbc735&BAJNe{_ zZCNzQ1ywk?)t}8OC=PpUn$lgg|6JCdZVVwy+DjmEh+7ca)z_EeHe8|N!smvEE)wW! z6P&NZE8`ANgQO{SZgpCj0XA9w1Tkh91=iT4SuGp!0yMX<_gM7hozBQf9uj7o<}?%f z^VdsoNtC8gh3egvJ$77M=Umy*<8Uc~X^vEo_~xTfafvFcdg^0y4*4{WLX%6~x297& zdGyESGeVJJ1T|?EAj@#jQjpmp)YIFqHkX|c{-k8d+{sj$6y4G&*=C|>g<17G$2%3~)VlMj$E|plK zfEZpD`}$PZQSV%5oAs78dR&*vKD?jG%b?Vp z;V#h4R!0uXk{_-yQCQG`U>)t^LH;Ix@+ph-;)=%HJKlx_d!m=oa2ZkK9@uJ{vXo}} zKi2yznJ(s&PCwat)K%wz7C9euW8F3CSgyot^O8=-r=XW-)v3AVZ1tT+q;X(P|o&ti=f}JD~Oky2$d`FWJpW!-#1R z={S@-c&01}=g`Zsf7gZT$dOOn_{7?E?y_>Dl?F076-5aRnv_7GRx2AQdLXFH zc@=C~liI9hq$c13U$ffE3fa0?R^3IZhtk*Lr^SWwa@!OFRphZWv+A=8P+En02Rw;4 z39APySk1E^pH~mE(fuQyjMqM+u~i4ysTtgE_d4*~?b2eetu}riAA=ke8=BYVojtkB ziB}IWnuws|i$*BQ@#g3B`2iJ~_D>AuLRY%mew4s8%w(M;x-x{S9X zRYysu2Q7BVH(1m`tF`I1d+xsSnP{xEmVj~qz9=V+Xf)1PY#E!E4kg;c3(Ut!nT?uN z{wSq|W{Hw(Iru2Ar{=>g;sE0w>KMlBX)zR4KQvTYhBt`ID`jN)kqoAz64YW%FG4IX z7vV}2gZOW?3NFL{>wA==5-v@e)cp+Q5;NTV3h4ADHG(9Z!b;Idz zdapt=17rFk1P1i+jZW(5Aw#2U%lel$F3^vPl1m{eZ`I#CQj32>^ZjTE)A`V7|0b;r z_6KeghC;JU-p7519^*6~Qo;&nS0(6IXBOYZQR{GGk zo%GJ^&103{-smTOLd&A4%OhajG+&gKVbzmp3xD(Rbb!BxSL-A9GGr)U7S*j@bN^f( zlS?t_6_{ht3*Zuo8K`?zyy^Pm$Fe4wQG5GxlM|`rozOxOipfO$VDg@MXBv$O#&q%W z=*=V9=VptqHejUW35!<|8}<>}9K|hk`*jH#zc&xZJn#EtZ9Gj;{QhsZ&DugLtTh|n z(97mjg<*ZGRs>pbp&WH-^)66r%8cm|-CMqan)2{G(shv`h>eCmKRfP-){gR;B1xvD zqq3Y5WrHlc6OW>ki#9K2HNTWPVNat&$X_}h@Iam$4ssPPWT^YIKGMJk6)9Ngp{uH< zVF#>PK(lhy6k8}I%e_yFPH1Q8r!V{)pF4d&;yaEw4iCXa)#`_K90}o+{rAGbRq(oyV< zWD*|V@fJTT%y1zc{KjVWTYilg_!=^Yf$a zM1OymAGF5eOZk&}-FOs=YpK0(JLrj{SVmz(n;DEUAhFofj&G&UhEE*HjH;&aE1jD3 zNE=RJ6?a`AJNdm?c8f~BthzPwuj*^{aJIz|ZhAY2Hcvs7%75{zM+amOTFSS=NF%O% zT4O!3Ou1`mBK<>DyI@HXkM$^MkhHs4OTzz@GgEgGpL)|xJPn8dA*)Oon?o;BvpnCa z*`Yob+o9vX<}Lx~ z4bM#vE3J4qFQTY>-cva>V1XtU@X$WyQzn%?Ww7G`79XL^p;fS2lylB5vnVvfhY|_h zLKO$k#VJx;bOpe!Y{3517OD`YmJIYZDPE=Jyi{sZYH3{KvscupSe8ZF)AAY25^Obh z@L@;#m^g|Ck+!*hvxJdS(L#|js{AVtTyKScO2)mTcglGHiP|U>FnV=)@da;xpi%S_ z*$56^{22d1VO;&7q3-nn_mtA>PR1muw4xB=f$4Q+WyU+!+T zVB`CEFu$KLgvWa-VLJyo0IyP_)YZ~Mvr7^08VxKOi$(UL`=KF5beyptKbk?XJ|5sW zVhme6t9R1tUQi(M_#tmo!F^a` zAW75W(nc>HI;~lf@0l$s2f_@^FTOtiN5m+vKVqy4w=2VCbc@Y<0>ifx)jlg4+M~ua zJ@V9yE^Fk_wc=S*rpUsQrcz*j8M2edCEFpG&2vyrL9>$MhG7(X?@|jtMNo2&BOP$P z`r|Lt-}SsxXh->gl)Hq4g4UlercOKOR$eK@*Te^K-_py#I#tAE%Br|EY^cDi%CD#p zV}M6(=`TK{6dKzdzc<#Ub)hRABW>kK15kf75kWOt-o>Dm&Y!WR4cTs5YF-CckyYAq zxioruYm_mnzSyO5lb>bem!Gh?U!5uz|I@>kKDpfZH4oDfDkVOjP(Q-R`xC4VE zJcTam3hi~?G;5pt_2?#m!rHpjqy}ugd-IUSOQEd#U((~geiZ!X0tfjSgaVj}RkYppvlI*Yfd(aNCjyzY2o4j#5x z%j@B^ScN*JJn*1}J1Sf1tnz%|o@XVv?~WlIZBx|pNBZ>A)}i7DZB5#7j7uU+pkU$z zE0TF|aVPV`D95pQ6*iPYA2U2uM~dwVg}y3{*z0EGzw1bi4vDn&j~Gb-ypk(SG(^|q z&A7h8v%VwSGWCuS#e=S+Zgy9j#(gSB50u}uuS_{rW$^U=6))3(#isd6 z+INViDfzX)(!%5n|B>n+tP8**SS*Em;64OR3h`XYF1P`eVcN0BYBLf#=SN(z>C z(BIS}Mq5;705vaS?B-G5CFb@HH5#RJgIT;4f8R=f>YIEK_^_e4_i^@^Lg6tAm&07BA2~=_VJ{;wpVn+zl-G>+<2| zP=~bZg@S=pc_cJ!seI^0lxTQ{=Y4re0RQ!O$!*#|EVFPZ{CM*+o-@X%xz;vy4gzzY z`rf!UhLE}Gl@PpQ&UXC3?(p54xi-C{Pz|)xXDC%@A8rm9RGgym zi6O&PUE;z;54{jZ)9B;^9#?D=KJ&+}H9Nqc&B0t7P?GL(%ndni3+sRsCed^YtukB2 zhQwQRw6Z`;AIwBznX9a7!qD)rc*1AWct&*^?t9>cLS-sn>#Xt<(qb0-X8YHF`8O0r z%@XEPxhb)%7@8IfQ11#wymyqpwg1(Kg;Rpc4aUZEghM+ zq6NFKJFHRPrRDmX!a-k)d`Q7y?d(@+BDo%=}@TB%R{J#NQv zT<8O~vRDZtamdHgMQ6X>P$vanH$>K>rF|NPZnl9ou0(r5bJZsqBxb%QiSz5hawS}l zC89e)O9nLn&2XYTmNn7Xd3j5lp{SMM!+JcvC4fCjDaiV~v|y`<0;8k1gs#Quu%`J3 z{<{&Oo<)zXN~|j)_h)`$%0s{GP#WHp9jIxAYQB_@O+%A<6+BxBRcLWgR8;fS1jC!Dysn)#T1JuGy)*a(El?))Bq4i*Y{+a5J%5Zss)= z$Y}pyuR|I>rY0l0Q`a-Ni}5N$m*9+TG5Qu#rPr1{$TGCDM_#jD(yj0+)q8KG%sEbcr&FbtR7czWhw z@Q!~|@%d#y9)?q}su9EZc;9!AosM{eSso-|j}7#j{ zZJpLCflPOUtS0+ha8<_z?hZD`P}z6+N7rHB%56&X^coQI?HYYQc?V82xUA8?qHVKs zm|A0;3$$p$3M|aY;027~9qo*z9b7#lWpBQ+7BOFG;|zJN75+zgO5ZD@@bF8~p9X0T zuMa%Qw^}dJcSk;bxZ!COs;24vaKwJ`cgR9BJeNO@=ZvCN>}b-r9!z8v~tRy1)Ng&GQ15su0tD6)5jq( z!N;$6@}gCA#$~{1q%wm1in16FGQ(6M@8+9zlf=US@PFPtfpP%pK>zX!@%yPuN#mxAM|=dJ@tZN5#@$Co3 z4&{S@i|u~#h)ygK61q&3FdNF$ffI!)9K)V8BnZeA9TKWgP#nh;+(J0gEzBYOEN z6RIg{d7zl)w#s6l)7&Bx;TiU*4lq`8#LV2Se*LCwP$Lc`6m%|3`7e#zxcFcCHu5u? zYpet6D=zXmN54V#pe^R59^-T-UCC#z7AWiZ?G3z{&j0c!Zf|99x15L%U?4jigKvLg z=8L{}CYPU%($g*MDI`G;qetMykvziFF4Ci{P8l4*qfA-XA?a_vj{e<@2o*LmOFiO` zTTT5dy;DGaH*Po4S>H~BUuAg=x38}4_s;YTWbY;O>*#k=M5l3>1cOf@l8jJYJJF#F;ggV@` z3m#=Es)OzMuo96r>EyHEJ#JPAu|IsHVtY~EE4+idxXGqH7vz=Zb?YDcXW)VpP$>7pe)_d>R{8W$u2!hmtmB|49a zj!4lby`o*^&?Z|b^p;)>D3_*Qv|g}|oKZi4{0{#Ho20(K<)W`Vn*NlGAS;#iy5&6@ zh9p2_Ny>|5{bD%@ERO+z7x-xZf*skPjtiO0(X+BZ<#F}q@DC0D^8NF-$rc#Fls!-I z2YpOX4p2emhq|rwY}6#LcvA!N25hKb*Q33E>Nf&QvGNzaHXV-#DpsQ1feXhNej+}~+I?=CD*tYG4VK2*Mt9nsKM!uZa$?Wa>H(KmkJZ$#FT zTq-n_xuzp?qg(c?%V@rf*5ixLbae2#hqR2jvXiMMWM7O(pf81qX*7vTXS@cQ2UAP0 z1sMb((@)-&s;iXFP)ail^`pF#P^{M0ZF-2Q{WP^SLi(SrkCf56rY21u zDM^hZN>ZbLNov$CNsYE8sZp{dHM*6gMx~lGX;P9J1xivAZ`X(mLawEDef>ZqW|G^; zd8p<)2ThT*P!mxLHIcPY6JZNAk+x71aSJt(w@{;igC->`)F@)1Mj2oEXxtspMm_>- z>;rI$z6YE5d$3W!gN+6rY*g@Iqk{(`DPl~Nh%pf&#zco06B!bWRfsVWA;z2rKKhj6SD^NE}ER`;>f8kf|6=u$EhxAoa*AGV=-gzyprnU)*vQ!6Jq06 z5f{UXxHwkC#j+wUo)vL1t%!?jMOQt}TpSkr+@#Qt*iDWgsh(fTBnO ziXsUpiX@;Yl7ON}0*WFDD2iktE0TbsNCG-V%FuVJOhE7o#U!ItNV1AWB&S?NatcNy zr({HOibf=-Y(#PjM=xoWPFZ^mPm;uVXlM z9m9$17*1QqaMF5)Rn{?_u#OSyTAJ;B&gFv(lqlN3k(?78YFXh}$O^|QRydZh!m)l8 zj>W5RtXzd-*(NyDtirKi6^_*^^CUh8wypj-G?J?>I5nEEv^1&N-kD;!n_<63)CoBBu2p^ zEeaM%QLspff<;0UEYhK1kqi|JR47;^LcvIb>n*Js;?*)u#nC3)%;d15sRQ&nNaqzt z^)h8&)3RXB;?uBeig4bXL$O=5xoSP@{R8}vqvVJjB~Roixgtl&7dcAK$Wih}j*>fO zQ1~N9$ssu^9_jS!7o$c>IEGWhGQ1*&5vXDqfii{>sACv`LWU8jWEg=`h7qV`8D25N z2vjo+ubeKK)oj;i_g3MQ7ic}lwW`l>o&Gu3J0a(KPvl(hj-2a#l5@Rda<2DI&h;*u z;W|I%T<@%$>pk|aY!sW@t1Pyn>bx&_Cd68&BF-oU@wO<4H$g$Xn|1WxEctA7hR)f>=j-GE-<2K2f%pjWd2y@n0wm1{t+ zR|`6o8qjOgfL;;v22EcG_F$llBUp7T!6{@2UL`~DN*RJz%MiR`hTv5*1h1SSc=as7 zDQE~@MMLmP`j?-2F&!T08F8$n0mrH8IbK-L@!EQhm)CQ=!k*(r_8hOX=Xj|Dj#Jxn zyx^V_X@0_Mh)q=+YU(*&Q^yH3wVX&(%ZW6#oJdp4i8QsGNK?y+G_{;aQ^yH3wVX&( z%ki29gB)#-$iji*0mTaLDNb)k@p3zgSKCp%*pA}0b`&qQqj;qq#S85zPG?8)GCNA5 zax+}zBraH{Z-a;8PI#X8o@iR}M9_*SYF0dvvf_!36;DK*@L0i$C-PN1(JmfQ zWaZ1Crtei6I25iP9qU%$M79Dasueg9t-y(91x_R@aH3d&6TvDR>s8=Ht^#|t&W1xfhQO#Ji#d92}T%CF#33ck;xN`TApA;3kbb-o?s;O1f!(w zUbI45%<356os1suRdl!!(cwlzhZ_kUZWMI55zygAKZhInJl?D4a3h|>BkfAx!U=V37_5i)5%+phCeS5ejBBKr97 zrV-IbJZ9K@lCnC5GFGou!ZNBQETdn-GD;>aqiMo2>Lx6sbHXx;XRKcPgk@Ziu-Ff@ zxi+?);uT=8u?a1{eqC2eVHdInO@Wfhd%E*gdoH!4?nrkpFWmATf3inilfrh3Qjh*< zUfSikWFMbOcJZlX51&eQ@TnxfPbIm1D#;t9LQbDb^7&MfOH+f55EU_-Cx)qQM0-Av z8dZxRqsC`zqHB(6B5aT*$_8m7ZIC9~25BO0kS6LDnIdnHCi(_x6p$=Mo40CEYC;>` zHn9e_Nt|LjB{t!l5}Qa)iA@lv#3p`IViUS4v5DH0*aWOeoMJU4Hes3)DMGq+kCrKA z!^gJtILqJ|Uc#!|7UoyuGU5EWDD3B1q{Dd@33HxBN}Xqsbmv*5<#`r~djSh-pJ$Q$ z=UIUtPWax~j2R3ZIl)t$4IBkI&D^b}1O~8_Kz&OIq_>nncS{LGx0FC}O9|w56tA_V z1VUSiSGlRs`>^NAH%K6Qk6OJu)JffkTdL28|N!X%Z zy%zPt=~~Z*Wb%zC1ZssOD_TTy+65#pVLJP_EP2!lTQyeq3i({sqam>^>o|(DF zF;fdUruR|45qIyTloxnuj%(dC!*zbjx!zGZ*Ly1GdROIK@2i~aot1OFw{os`*9_PB zE9ZKL<=n_)yo5&$)$P&Vn+#ul=sz1b4+qw>X8^@I&QqMx9L4*~QM|1j#kQM?`ecLREv9UkZ$ajeh*$0_YOUTV+rT6>Nc+jG3yp5x{A z9IvUxl!GH(023k9(avJkYqsBYj&u(zeATU0XcTw8bMm zTRhUT#UmX%JkYSkBmG+3YZtx1V!9g0`dc|)34Iiz{9Lz>q-q zns-1*^G1ki&JQ8Y+ajc8?#RBf(&zN)11fpXHRJ?d%6QgH3D3DH=6O5CJnyHN=M5F} zyrW{Cw^YpYo{D+iR0+?yD&~1x#XRpT`SMWi6ZvY6`U^8H_$aL@l=Qng=T0#;gurM1 zEFhh~R4|?$oXOiOAbD>EByX&MEyL?-R}vXvEgUEt zP^`3`;nL7dNAUtXidWcCyu_a3GOaaH3d&y;?H6+`e#WMDc_m zUxp;BUqo^$1|+X#K=O(PB(G~g^6Ca8uW>-~N(UsbcSLfk2PCh3Knfi&;v;u?g^RNo zLhJxf3LM}_kpnC#bbuv=4zQ%q0hSaxz>-1-SW@T!O9~y}NRb09DRh7(6$j{|4la}H zA~PzGBcLjtfTDB^s=^^ylnue6Xb2W1L$D|qfpF~rf2A&z|lQRHKY;~qmc>axRE{laUa zLjCS*f_Gno%{ct!;7dU(wtty|JGRq9283RCPcZU(f-!+77%O;!F@z@=TX=#ohbI_| z1ccrwo?z_a3C1+ioprR5-$PNRE;wIZCSJDCv@;q|6Ko zZE}><$o+Pj=95>8>p9Z11(c-plr$wbWXW}>M1wy zK*|jqk#oI2Qf}aul6!Tu>0Nc|p9w1@U?o#H&{juT??3G6nHE zRK!KSAU?(g@e%#_mygs=#EiQMs19q7Z`Oz50iUX|S;*l*_Ki4auM-rn{ybhX&++1U zj+fDMyr7=rrS%*yvgdfY1CA5kbG!vS$D85P@NmL7>G#2Sq2J|l77Sd_9}R-FfF-z$ zY6xC@L-5KQf|uP8yyk}B1vdn*wjp?_Ey3w*2wr4E@Cv)_D~*^A5A=*UR?>juRP`J$ ztmk-bJ;%%IIbLDU@gjSU*V%Kt)B(q-?Kxg>&xtg*+gtkT(+&Rw*)-q;n%Zkcnp#ez zspUkPT27>?jM4CEIpsD3Vnp%$6)Q%3AY;55m7Y7uZi9N;X?I>PuNAYSq ziWl2ayw;B5rFImrw4->TJ;mwlC|+hqNmMSw4Uxp!3!Z^87C2G1z=^6AjukC%qGo|f$=l(0wO{}D@Dj|3&0wt8Jbs)zH@BYW zjwi|HaMElJC(-6`Qf&?=+2(N4Z4M{lW^pJthm&-3c%+@H`P>;CBh)csc{KwTa-qFv z&x(}wtVmzaid6QjNNdlE6!)x1ch8E{4_JW*JS%d7XGMOnMYj{j5<)iwByR>!3cTP* zkrf;%a)Kj8MsTFa2aXikz>y*sI8tN+PYOKXNRb5`DRn@tnQh+WQxr7UzF{Q7PmE0U z%19-zj8yN+NX4#xbn>!49daCw3?@ycK zdcPT(@Q1t)yV=uVta{QfIe`vlCMXSPhImZDsmr6?C= zDaz$oigF>AqFjQdC>LKT%4JuMQo)s?Txz8#7g-%DFOuadIXuuR;#kE3j?>L^yn3GF zHS`>>q~~}&J;$r+IbK`O@d^hVr?cmHwLQmc?h5eE5e5zP_6)1GV>qoX!z*nWUT4ej zDqDuv*fPArmf`ia46m+ZIBhM%D{C2E*RUngX&W#CePfo@IAl4UBbL`XVtKtIme)LD zdEFzH*FIu-{Uet5K*(}Fh*;hW5zG6*4J(*>!{LD!B93)Jz;QnC9Pa|p@gDFT?*Py7 z`g@L7-*de7p5v7dI8JxZ@v3`HpgFbvi#@KfyAZZcz8ik_{gAFq;Qqq=5s@#>Uk67y zr(ANB_s#`J1#Y_FsK8$r92Ge3f};WtUT{?4$_tJPe0srAfpaf8%6s{OqXKtdaFqA^ z9Zoc3Ztk+gJ(Cs+3~pfSCVJV9X;o-0t&pDPm7vm#wRE0Wi4tiBK@ofV&yPisrAdr$=dT zY&N?epSMRGd>mGvcDt@45+8!b%&z;EQAlg%qLR79@UW&Fj_y-DRZSD<*pQ@?3IJaUnxi#ECnftbs+7oN#E+&tRIe7 z4?N|22KV~-bAV%Hia00*&t1!j7M#av7jT?}0mmsBaGan4$LShyoV*dosvK~f*a63B ze%-Br=p^VMue61Y$Og9h_OO$-hn=cD>;&y$r)3X28GG0%*uzdd16$pC*h$vIPOa(N z!hLR;(!EX{W<+W+t5AbEc^b@V(_l`R26L)3n3JTzoE{D4#Aq?AM1wgQ8q8@hRi<{t zqsu)h%_}rVHG<7ht$sPx$(d7~x;fQ}ol~9mIn`Mqr#ffkRA-bKs`X7ybvDYW&Q-U| z$8Pyi=OqJr2YG_=jU!mMSc3D2B{*kTg7bqVI2Txg)7}!C;+Eiab_A=gB{)qj!7Djz z9^004uVfDfN;-m7(h{7KhTxSn1h1qacqI+ND`^N`Nki~T8iH5S5}cBT;FUB4uO!dd z=2}`fP&1%dJw3%K>L^}QNAapUir3Xqyt0nswRIG)uA_K;J;f>PC|+YnaVqzn*e

    HsXpVP6= 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 @G!p9Lv+UsJP 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̘$v%^=YJ{*wBr~4!Cl^S|_ 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

    - - - - - - - - -
    - - - -
    - - - -
    + + + + + + + + + + + + + + + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    @code { - private string _name = string.Empty; + private string _owner = string.Empty; + private string _module = string.Empty; private string _description = string.Empty; + private string _template = string.Empty; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; @@ -35,19 +57,19 @@ { AddModuleMessage("Please Note That Once You Select The Create Module Button The Application Must Restart In Order To Complete The Process.", MessageType.Info); } - + private async Task CreateModule() { try { - if (!string.IsNullOrEmpty(_name)) + if (!string.IsNullOrEmpty(_owner) && !string.IsNullOrEmpty(_module) && !string.IsNullOrEmpty(_template)) { - var moduleDefinition = new ModuleDefinition { Name = _name, Description = _description }; + var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template }; await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition, ModuleState.ModuleId); } else { - AddModuleMessage("You Must Provide A Name For The Module", MessageType.Warning); + AddModuleMessage("You Must Provide An Owner, Module Name, And Template", MessageType.Warning); } } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Edit.razor similarity index 82% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Edit.razor rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Edit.razor index d65cc5db..d23ab2e0 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Edit.razor @@ -1,10 +1,12 @@ -@namespace Oqtane.Modules.[Module]s -@using Oqtane.Services.[Module]s -@using Oqtane.Models.[Module]s @using Oqtane.Modules.Controls +@using [Owner].[Module]s.Services +@using [Owner].[Module]s.Models + +@namespace [Owner].[Module]s.Modules @inherits ModuleBase @inject NavigationManager NavigationManager -@inject I[Module]Service [Module]Service +@inject HttpClient http +@inject SiteState sitestate @@ -29,6 +31,7 @@ public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; public override string Actions => "Add,Edit"; + I[Module]Service [Module]Service; int _id; string _name; string _createdby; @@ -38,9 +41,10 @@ protected override async Task OnInitializedAsync() { - if (PageState.Action == "Edit") + try { - try + [Module]Service = new [Module]Service(http, sitestate, NavigationManager); + if (PageState.Action == "Edit") { _id = Int32.Parse(PageState.QueryString["id"]); [Module] [Module] = await [Module]Service.Get[Module]Async(_id); @@ -53,11 +57,11 @@ _modifiedon = [Module].ModifiedOn; } } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading [Module] {[Module]Id} {Error}", _id, ex.Message); - AddModuleMessage("Error Loading [Module]", MessageType.Error); - } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading [Module] {[Module]Id} {Error}", _id, ex.Message); + AddModuleMessage("Error Loading [Module]", MessageType.Error); } } diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor new file mode 100644 index 00000000..b4d00775 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor @@ -0,0 +1,101 @@ +@using [Owner].[Module]s.Services +@using [Owner].[Module]s.Models + +@namespace [Owner].[Module]s.Modules +@inherits ModuleBase +@inject NavigationManager NavigationManager +@inject HttpClient http +@inject SiteState sitestate + +@if (_[Module]s == null) +{ +

    Loading...

    +} +else +{ + +
    +
    + @if (@_[Module]s.Count != 0) + { + +
    +
    [Module]s
    +
    + +
    + + + @context.Name +
    +
    +
    + } + else + { +

    No [Module]s To Display

    + } +} + +
    +[Module] Module Created Successfully. Use Edit Mode To Add A [Module]. You Can Access The Files At The Following Locations:

    +[RootPath]Client\
    +- [Owner].[Module]s.Module.Client.csproj - client project
    +- _Imports.razor - global imports for module components
    +- Edit.razor - component for adding or editing content
    +- Index.razor - main component for your module **the content you are reading is in this file**
    +- ModuleInfo.cs - implements IModule interface to provide configuration settings for your module
    +- Settings.razor - component for managing module settings
    +- Services\I[Module]Service.cs - interface for defining service API methods
    +- Services\[Module]Service.cs - implements service API interface methods

    +[RootPath]Package\
    +- [Owner].[Module]s.Module.nuspec - nuget manifest for packaging module
    +- [Owner].[Module]s.Module.Package.csproj - packaging project
    +- debug.cmd - copies assemblies to Oqtane bin folder when in Debug mode
    +- release.cmd - creates nuget package and deploys to Oqtane wwwroot/modules folder when in Release mode

    +[RootPath]Server\
    +- [Owner].[Module]s.Module.Server.csproj - server project
    +- Controllers\[Module]Controller.cs - API methods implemented using a REST pattern
    +- Manager\[Module]Manager.cs - implements optional module interfaces for features such as import/export of content
    +- Repository\I[Module]Repository.cs - interface for defining repository methods
    +- Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core
    +- Repository\[Module]Context.cs - provides a DB Context for data access
    +- Scripts\01.00.00.sql - database schema definition

    +[RootPath]Shared\
    +- [Owner].[Module]s.Module.Shared.csproj - shared project
    +- Models\[Module].cs - model definition

    + +@code { + I[Module]Service [Module]Service; + List<[Module]> _[Module]s; + + protected override async Task OnInitializedAsync() + { + try + { + [Module]Service = new [Module]Service(http, sitestate, NavigationManager); + _[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading [Module] {Error}", ex.Message); + AddModuleMessage("Error Loading [Module]", MessageType.Error); + } + } + + private async Task Delete([Module] [Module]) + { + try + { + await [Module]Service.Delete[Module]Async([Module].[Module]Id); + await logger.LogInformation("[Module] Deleted {[Module]}", [Module]); + _[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Deleting [Module] {[Module]} {Error}", [Module], ex.Message); + AddModuleMessage("Error Deleting [Module]", MessageType.Error); + } + } +} \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/ModuleInfo.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs similarity index 65% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/ModuleInfo.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs index 5252730e..6f6bc85c 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/ModuleInfo.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs @@ -1,7 +1,7 @@ using Oqtane.Models; using Oqtane.Modules; -namespace Oqtane.Modules.[Module]s +namespace [Owner].[Module]s.Modules { public class ModuleInfo : IModule { @@ -10,7 +10,8 @@ namespace Oqtane.Modules.[Module]s Name = "[Module]", Description = "[Module]", Version = "1.0.0", - ServerAssemblyName = "Oqtane.Server" + Dependencies = "[Owner].[Module]s.Module.Shared", + ServerAssemblyName = "[ServerAssemblyName]" }; } } diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Services/I[Module]Service.cs similarity index 85% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Services/I[Module]Service.cs index f481dc02..e98da06d 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Services/I[Module]Service.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Oqtane.Models.[Module]s; +using [Owner].[Module]s.Models; -namespace Oqtane.Services.[Module]s +namespace [Owner].[Module]s.Services { public interface I[Module]Service { diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Services/[Module]Service.cs similarity index 96% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Services/[Module]Service.cs index 7935c184..4ce98e00 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Services/[Module]Service.cs @@ -3,12 +3,12 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; -using Oqtane.Models.[Module]s; using Oqtane.Modules; using Oqtane.Services; using Oqtane.Shared; +using [Owner].[Module]s.Models; -namespace Oqtane.Services.[Module]s +namespace [Owner].[Module]s.Services { public class [Module]Service : ServiceBase, I[Module]Service, IService { diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Settings.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Settings.razor similarity index 97% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Settings.razor rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Settings.razor index 703ba2ee..a53dc57c 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Settings.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Settings.razor @@ -1,4 +1,4 @@ -@namespace Oqtane.Modules.[Module]s +@namespace [Owner].[Module]s.Modules @inherits ModuleBase @inject ISettingService SettingService diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj new file mode 100644 index 00000000..c3b58f44 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj @@ -0,0 +1,39 @@ + + + + netstandard2.1 + 3.0 + 1.0.0 + [Owner] + [Owner] + [Description] + [Owner].[Module]s.Module + [Owner] + + + + + + + + + + + + + + + ..\..\oqtane.framework\Oqtane.Client\bin\Debug\netstandard2.1\Oqtane.Client.dll + + + ..\..\oqtane.framework\Oqtane.Client\bin\Debug\netstandard2.1\Oqtane.Shared.dll + + + + + + false + false + + + diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/_Imports.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/_Imports.razor new file mode 100644 index 00000000..147a5eee --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/_Imports.razor @@ -0,0 +1,18 @@ +@using System +@using System.Linq +@using System.Collections.Generic +@using System.Net.Http + +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.JSInterop + +@using Oqtane.Models +@using Oqtane.Modules +@using Oqtane.Modules.Controls +@using Oqtane.Providers +@using Oqtane.Security +@using Oqtane.Services +@using Oqtane.Shared +@using Oqtane.Themes +@using Oqtane.Themes.Controls \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.Package.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.Package.csproj new file mode 100644 index 00000000..0689d66f --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.Package.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp3.1 + false + + + + + + + + + + + + + + diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.nuspec b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.nuspec new file mode 100644 index 00000000..8598cfee --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.nuspec @@ -0,0 +1,28 @@ + + + + [Owner].[Module]s.Module + 1.0.0 + [Owner] + [Owner] + [Module]s + [Module]s + [Owner] + false + MIT + https://github.com/oqtane/oqtane.framework + https://www.oqtane.org/Portals/0/icon.jpg + oqtane module + + + + + + + + + + + + + \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/debug.cmd b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/debug.cmd new file mode 100644 index 00000000..8b72b03b --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/debug.cmd @@ -0,0 +1,6 @@ +XCOPY "..\Client\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Client.dll" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y +XCOPY "..\Client\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Client.pdb" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y +XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Module.Server.dll" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y +XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Module.Server.pdb" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y +XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Shared.dll" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y +XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Module.Shared.pdb" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/release.cmd b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/release.cmd new file mode 100644 index 00000000..16c8ad03 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/release.cmd @@ -0,0 +1,2 @@ +"..\..\oqtane.framework\oqtane.package\nuget.exe" pack [Owner].[Module]s.Module.nuspec +XCOPY "*.nupkg" "..\..\oqtane.framework\Oqtane.Server\wwwroot\Modules\" /Y diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Controllers/[Module]Controller.cs similarity index 95% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Controllers/[Module]Controller.cs index 9fcb319e..1ff6a0ab 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Controllers/[Module]Controller.cs @@ -1,13 +1,13 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using Oqtane.Models.[Module]s; -using Oqtane.Repository.[Module]s; -using Oqtane.Shared; using System.Collections.Generic; +using Oqtane.Shared; using Oqtane.Enums; using Oqtane.Infrastructure; +using [Owner].[Module]s.Models; +using [Owner].[Module]s.Repository; -namespace Oqtane.Controllers.[Module]s +namespace [Owner].[Module]s.Controllers { [Route("{site}/api/[controller]")] public class [Module]Controller : Controller diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Manager/[Module]Manager.cs similarity index 81% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Manager/[Module]Manager.cs index 8380c97d..9414847d 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Manager/[Module]Manager.cs @@ -1,10 +1,12 @@ -using Oqtane.Models.[Module]s; -using Oqtane.Repository.[Module]s; using System.Collections.Generic; using System.Linq; using System.Text.Json; +using Oqtane.Modules; +using Oqtane.Models; +using [Owner].[Module]s.Models; +using [Owner].[Module]s.Repository; -namespace Oqtane.Modules.[Module]s +namespace [Owner].[Module]s.Modules { public class [Module]Manager : IPortable { @@ -15,7 +17,7 @@ namespace Oqtane.Modules.[Module]s _[Module]s = [Module]s; } - public string ExportModule(Models.Module module) + public string ExportModule(Module module) { string content = ""; List<[Module]> [Module]s = _[Module]s.Get[Module]s(module.ModuleId).ToList(); @@ -26,7 +28,7 @@ namespace Oqtane.Modules.[Module]s return content; } - public void ImportModule(Models.Module module, string content, string version) + public void ImportModule(Module module, string content, string version) { List<[Module]> [Module]s = null; if (!string.IsNullOrEmpty(content)) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Repository/I[Module]Repository.cs similarity index 82% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Repository/I[Module]Repository.cs index 7a607ad2..f38a60d5 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Repository/I[Module]Repository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using Oqtane.Models.[Module]s; +using [Owner].[Module]s.Models; -namespace Oqtane.Repository.[Module]s +namespace [Owner].[Module]s.Repository { public interface I[Module]Repository { diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Repository/[Module]Context.cs similarity index 82% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Repository/[Module]Context.cs index 9a438a88..2a14bbf4 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Repository/[Module]Context.cs @@ -1,9 +1,10 @@ using Microsoft.EntityFrameworkCore; using Microsoft.AspNetCore.Http; -using Oqtane.Models.[Module]s; using Oqtane.Modules; +using Oqtane.Repository; +using [Owner].[Module]s.Models; -namespace Oqtane.Repository.[Module]s +namespace [Owner].[Module]s.Repository { public class [Module]Context : DBContextBase, IService { diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Repository/[Module]Repository.cs similarity index 94% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Repository/[Module]Repository.cs index c6383fce..9b83b239 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Repository/[Module]Repository.cs @@ -1,10 +1,10 @@ using Microsoft.EntityFrameworkCore; using System.Linq; using System.Collections.Generic; -using Oqtane.Models.[Module]s; using Oqtane.Modules; +using [Owner].[Module]s.Models; -namespace Oqtane.Repository.[Module]s +namespace [Owner].[Module]s.Repository { public class [Module]Repository : I[Module]Repository, IService { diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/01.00.00.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/01.00.00.sql new file mode 100644 index 00000000..0e7266e6 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/01.00.00.sql @@ -0,0 +1,26 @@ +/* +Create [Module] table +*/ + +CREATE TABLE [dbo].[[Owner][Module]]( + [[Module]Id] [int] IDENTITY(1,1) NOT NULL, + [ModuleId] [int] NOT NULL, + [Name] [nvarchar](256) NOT NULL, + [CreatedBy] [nvarchar](256) NOT NULL, + [CreatedOn] [datetime] NOT NULL, + [ModifiedBy] [nvarchar](256) NOT NULL, + [ModifiedOn] [datetime] NOT NULL, + CONSTRAINT [PK_[Owner][Module]] PRIMARY KEY CLUSTERED + ( + [[Module]Id] ASC + ) +) +GO + +/* +Create foreign key relationships +*/ +ALTER TABLE [dbo].[[Owner][Module]] WITH CHECK ADD CONSTRAINT [FK_[Owner][Module]_Module] FOREIGN KEY([ModuleId]) +REFERENCES [dbo].Module ([ModuleId]) +ON DELETE CASCADE +GO \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj new file mode 100644 index 00000000..d92e4f75 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj @@ -0,0 +1,51 @@ + + + + netcoreapp3.1 + 7.3 + true + 1.0.0 + [Owner].[Module]s.Module + [Owner] + [Owner] + [Description] + [Owner] + + + + + + Library + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\oqtane.framework\Oqtane.Server\bin\Debug\netcoreapp3.1\Oqtane.Server.dll + + + ..\..\oqtane.framework\Oqtane.Server\bin\Debug\netcoreapp3.1\Oqtane.Shared.dll + + + + diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Shared/Modules/Models/[Module]/[Module].cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/Models/[Module].cs similarity index 73% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Shared/Modules/Models/[Module]/[Module].cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/Models/[Module].cs index 6ebba399..5a6d7896 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Shared/Modules/Models/[Module]/[Module].cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/Models/[Module].cs @@ -1,11 +1,12 @@ using System; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Oqtane.Models; -namespace Oqtane.Models.[Module]s +namespace [Owner].[Module]s.Models { + [Table("[Owner][Module]")] public class [Module] : IAuditable { - [Key] public int [Module]Id { get; set; } public int ModuleId { get; set; } public string Name { get; set; } diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/[Owner].[Module]s.Module.Shared.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/[Owner].[Module]s.Module.Shared.csproj new file mode 100644 index 00000000..b16ad2bd --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/[Owner].[Module]s.Module.Shared.csproj @@ -0,0 +1,28 @@ + + + + netstandard2.1 + 7.3 + 1.0.0 + [Owner].[Module].Module + [Owner] + [Owner] + [Description] + [Owner] + + + + + + + + + + + + + ..\..\oqtane.framework\Oqtane.Shared\bin\Debug\netstandard2.1\Oqtane.Shared.dll + + + + diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/[Owner].[Module]s.Module.sln b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/[Owner].[Module]s.Module.sln new file mode 100644 index 00000000..9879eac6 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/[Owner].[Module]s.Module.sln @@ -0,0 +1,52 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28621.142 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Module.Client", "Client\[Owner].[Module]s.Module.Client.csproj", "{AA8E58A1-CD09-4208-BF66-A8BB341FD669}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Module.Server", "Server\[Owner].[Module]s.Module.Server.csproj", "{04B05448-788F-433D-92C0-FED35122D45A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Module.Shared", "Shared\[Owner].[Module]s.Module.Shared.csproj", "{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "[Owner].[Module]s.Module.Package", "Package\[Owner].[Module]s.Module.Package.csproj", "{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + Wasm|Any CPU = Wasm|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.Build.0 = Release|Any CPU + {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Wasm|Any CPU.ActiveCfg = Release|Any CPU + {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Wasm|Any CPU.Build.0 = Release|Any CPU + {04B05448-788F-433D-92C0-FED35122D45A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04B05448-788F-433D-92C0-FED35122D45A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04B05448-788F-433D-92C0-FED35122D45A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04B05448-788F-433D-92C0-FED35122D45A}.Release|Any CPU.Build.0 = Release|Any CPU + {04B05448-788F-433D-92C0-FED35122D45A}.Wasm|Any CPU.ActiveCfg = Release|Any CPU + {04B05448-788F-433D-92C0-FED35122D45A}.Wasm|Any CPU.Build.0 = Release|Any CPU + {18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Release|Any CPU.Build.0 = Release|Any CPU + {18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Wasm|Any CPU.ActiveCfg = Release|Any CPU + {18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Wasm|Any CPU.Build.0 = Release|Any CPU + {C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Release|Any CPU.Build.0 = Release|Any CPU + {C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Wasm|Any CPU.ActiveCfg = Debug|Any CPU + {C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Wasm|Any CPU.Build.0 = Debug|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1D016F15-46FE-4726-8DFD-2E4FD4DC7668} + EndGlobalSection +EndGlobal diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/wwwroot/resources.txt b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/wwwroot/resources.txt new file mode 100644 index 00000000..2542de03 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/wwwroot/resources.txt @@ -0,0 +1 @@ +This is the location where static resources such as images or style sheets should be located \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor new file mode 100644 index 00000000..d23ab2e0 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor @@ -0,0 +1,95 @@ +@using Oqtane.Modules.Controls +@using [Owner].[Module]s.Services +@using [Owner].[Module]s.Models + +@namespace [Owner].[Module]s.Modules +@inherits ModuleBase +@inject NavigationManager NavigationManager +@inject HttpClient http +@inject SiteState sitestate + +
    + + + + +
    + + + +
    + +Cancel +
    +
    +@if (PageState.Action == "Edit") +{ + +} + +@code { + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; + public override string Actions => "Add,Edit"; + + I[Module]Service [Module]Service; + int _id; + string _name; + string _createdby; + DateTime _createdon; + string _modifiedby; + DateTime _modifiedon; + + protected override async Task OnInitializedAsync() + { + try + { + [Module]Service = new [Module]Service(http, sitestate, NavigationManager); + if (PageState.Action == "Edit") + { + _id = Int32.Parse(PageState.QueryString["id"]); + [Module] [Module] = await [Module]Service.Get[Module]Async(_id); + if ([Module] != null) + { + _name = [Module].Name; + _createdby = [Module].CreatedBy; + _createdon = [Module].CreatedOn; + _modifiedby = [Module].ModifiedBy; + _modifiedon = [Module].ModifiedOn; + } + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading [Module] {[Module]Id} {Error}", _id, ex.Message); + AddModuleMessage("Error Loading [Module]", MessageType.Error); + } + } + + private async Task Save() + { + try + { + if (PageState.Action == "Add") + { + [Module] [Module] = new [Module](); + [Module].ModuleId = ModuleState.ModuleId; + [Module].Name = _name; + [Module] = await [Module]Service.Add[Module]Async([Module]); + await logger.LogInformation("[Module] Added {[Module]}", [Module]); + } + else + { + [Module] [Module] = await [Module]Service.Get[Module]Async(_id); + [Module].Name = _name; + await [Module]Service.Update[Module]Async([Module]); + await logger.LogInformation("[Module] Updated {[Module]}", [Module]); + } + NavigationManager.NavigateTo(NavigateUrl()); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Saving [Module] {Error}", ex.Message); + AddModuleMessage("Error Saving [Module]", MessageType.Error); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor similarity index 54% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Index.razor rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor index 02c457f0..b6080579 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Client/Modules/[Module]/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor @@ -1,9 +1,11 @@ -@namespace Oqtane.Modules.[Module]s -@using Oqtane.Services.[Module]s -@using Oqtane.Models.[Module]s +@using [Owner].[Module]s.Services +@using [Owner].[Module]s.Models + +@namespace [Owner].[Module]s.Modules @inherits ModuleBase @inject NavigationManager NavigationManager -@inject I[Module]Service [Module]Service +@inject HttpClient http +@inject SiteState sitestate @if (_[Module]s == null) { @@ -12,44 +14,57 @@ else { -

    - - -
    - - - @context.Name -
    -
    -
    +
    +
    + @if (@_[Module]s.Count != 0) + { + +
    +
    [Module]s
    +
    + +
    + + + @context.Name +
    +
    +
    + } + else + { +

    No [Module]s To Display

    + } }
    -[Module] Module Created Successfully. You Can Access The Files At The Following Locations:

    -C:\Users\Shaun.Walker\Source\Repos\sbwalker\oqtane.framework\Oqtane.Client\Modules\[Module]\
    -- Index.razor - main component for your module
    +[Module] Module Created Successfully. Use Edit Mode To Add A [Module]. You Can Access The Files At The Following Locations:

    +[RootPath]Oqtane.Client\Modules\[Module]\
    - Edit.razor - component for adding or editing content
    -- Settings.razor - component for managing module settings
    +- Index.razor - main component for your module **the content you are reading is in this file**
    - ModuleInfo.cs - implements IModule interface to provide configuration settings for your module
    +- Settings.razor - component for managing module settings
    - Services\I[Module]Service.cs - interface for defining service API methods
    - Services\[Module]Service.cs - implements service API interface methods

    -C:\Users\Shaun.Walker\Source\Repos\sbwalker\oqtane.framework\Oqtane.Server\Modules\[Module]\
    +[RootPath]Oqtane.Server\Modules\[Module]\
    - Controllers\[Module]Controller.cs - API methods implemented using a REST pattern
    - Manager\[Module]Manager.cs - implements optional module interfaces for features such as import/export of content
    - Repository\I[Module]Repository.cs - interface for defining repository methods
    - Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core
    - Repository\[Module]Context.cs - provides a DB Context for data access
    - Scripts\01.00.00.sql - database schema definition

    -C:\Users\Shaun.Walker\Source\Repos\sbwalker\oqtane.framework\Oqtane.Shared\Modules\Models\[Module]\
    -- [Module].cs - model definition

    +[RootPath]Oqtane.Shared\Modules\[Module]\
    +- Models\[Module].cs - model definition

    @code { + I[Module]Service [Module]Service; List<[Module]> _[Module]s; - protected override async Task OnParametersSetAsync() + protected override async Task OnInitializedAsync() { try { + [Module]Service = new [Module]Service(http, sitestate, NavigationManager); _[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId); } catch (Exception ex) @@ -65,7 +80,8 @@ C:\Users\Shaun.Walker\Source\Repos\sbwalker\oqtane.framework\Oqtane.Shared\Modul { await [Module]Service.Delete[Module]Async([Module].[Module]Id); await logger.LogInformation("[Module] Deleted {[Module]}", [Module]); - NavigationManager.NavigateTo(NavigateUrl()); + _[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId); + StateHasChanged(); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs new file mode 100644 index 00000000..6f6bc85c --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs @@ -0,0 +1,17 @@ +using Oqtane.Models; +using Oqtane.Modules; + +namespace [Owner].[Module]s.Modules +{ + public class ModuleInfo : IModule + { + public ModuleDefinition ModuleDefinition => new ModuleDefinition + { + Name = "[Module]", + Description = "[Module]", + Version = "1.0.0", + Dependencies = "[Owner].[Module]s.Module.Shared", + ServerAssemblyName = "[ServerAssemblyName]" + }; + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs new file mode 100644 index 00000000..e98da06d --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using [Owner].[Module]s.Models; + +namespace [Owner].[Module]s.Services +{ + public interface I[Module]Service + { + Task> Get[Module]sAsync(int ModuleId); + + Task<[Module]> Get[Module]Async(int [Module]Id); + + Task<[Module]> Add[Module]Async([Module] [Module]); + + Task<[Module]> Update[Module]Async([Module] [Module]); + + Task Delete[Module]Async(int [Module]Id); + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs new file mode 100644 index 00000000..4ce98e00 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using Oqtane.Modules; +using Oqtane.Services; +using Oqtane.Shared; +using [Owner].[Module]s.Models; + +namespace [Owner].[Module]s.Services +{ + public class [Module]Service : ServiceBase, I[Module]Service, IService + { + private readonly HttpClient _http; + private readonly NavigationManager _navigationManager; + private readonly SiteState _siteState; + + public [Module]Service(HttpClient http, SiteState siteState, NavigationManager navigationManager) + { + _http = http; + _siteState = siteState; + _navigationManager = navigationManager; + } + + private string Apiurl + { + get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "[Module]"); } + } + + public async Task> Get[Module]sAsync(int ModuleId) + { + List<[Module]> [Module]s = await _http.GetJsonAsync>(Apiurl + "?moduleid=" + ModuleId.ToString()); + return [Module]s.OrderBy(item => item.Name).ToList(); + } + + public async Task<[Module]> Get[Module]Async(int [Module]Id) + { + return await _http.GetJsonAsync<[Module]>(Apiurl + "/" + [Module]Id.ToString()); + } + + public async Task<[Module]> Add[Module]Async([Module] [Module]) + { + return await _http.PostJsonAsync<[Module]>(Apiurl + "?entityid=" + [Module].ModuleId, [Module]); + } + + public async Task<[Module]> Update[Module]Async([Module] [Module]) + { + return await _http.PutJsonAsync<[Module]>(Apiurl + "/" + [Module].[Module]Id + "?entityid=" + [Module].ModuleId, [Module]); + } + + public async Task Delete[Module]Async(int [Module]Id) + { + await _http.DeleteAsync(Apiurl + "/" + [Module]Id.ToString()); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Settings.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Settings.razor new file mode 100644 index 00000000..a53dc57c --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Settings.razor @@ -0,0 +1,47 @@ +@namespace [Owner].[Module]s.Modules +@inherits ModuleBase +@inject ISettingService SettingService + + + + + + +
    + + + +
    + +@code { + public override string Title => "[Module] Settings"; + + string _value; + + protected override async Task OnInitializedAsync() + { + try + { + Dictionary settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId); + _value = SettingService.GetSetting(settings, "SettingName", ""); + } + catch (Exception ex) + { + ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error); + } + } + + public async Task UpdateSettings() + { + try + { + Dictionary settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId); + SettingService.SetSetting(settings, "SettingName", _value); + await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId); + } + catch (Exception ex) + { + ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs new file mode 100644 index 00000000..1ff6a0ab --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using System.Collections.Generic; +using Oqtane.Shared; +using Oqtane.Enums; +using Oqtane.Infrastructure; +using [Owner].[Module]s.Models; +using [Owner].[Module]s.Repository; + +namespace [Owner].[Module]s.Controllers +{ + [Route("{site}/api/[controller]")] + public class [Module]Controller : Controller + { + private readonly I[Module]Repository _[Module]s; + private readonly ILogManager _logger; + + public [Module]Controller(I[Module]Repository [Module]s, ILogManager logger) + { + _[Module]s = [Module]s; + _logger = logger; + } + + // GET: api/?moduleid=x + [HttpGet] + [Authorize(Roles = Constants.RegisteredRole)] + public IEnumerable<[Module]> Get(string moduleid) + { + return _[Module]s.Get[Module]s(int.Parse(moduleid)); + } + + // GET api//5 + [HttpGet("{id}")] + [Authorize(Roles = Constants.RegisteredRole)] + public [Module] Get(int id) + { + return _[Module]s.Get[Module](id); + } + + // POST api/ + [HttpPost] + [Authorize(Roles = Constants.AdminRole)] + public [Module] Post([FromBody] [Module] [Module]) + { + if (ModelState.IsValid) + { + [Module] = _[Module]s.Add[Module]([Module]); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]); + } + return [Module]; + } + + // PUT api//5 + [HttpPut("{id}")] + [Authorize(Roles = Constants.AdminRole)] + public [Module] Put(int id, [FromBody] [Module] [Module]) + { + if (ModelState.IsValid) + { + [Module] = _[Module]s.Update[Module]([Module]); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]); + } + return [Module]; + } + + // DELETE api//5 + [HttpDelete("{id}")] + [Authorize(Roles = Constants.AdminRole)] + public void Delete(int id) + { + _[Module]s.Delete[Module](id); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs new file mode 100644 index 00000000..9414847d --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using Oqtane.Modules; +using Oqtane.Models; +using [Owner].[Module]s.Models; +using [Owner].[Module]s.Repository; + +namespace [Owner].[Module]s.Modules +{ + public class [Module]Manager : IPortable + { + private I[Module]Repository _[Module]s; + + public [Module]Manager(I[Module]Repository [Module]s) + { + _[Module]s = [Module]s; + } + + public string ExportModule(Module module) + { + string content = ""; + List<[Module]> [Module]s = _[Module]s.Get[Module]s(module.ModuleId).ToList(); + if ([Module]s != null) + { + content = JsonSerializer.Serialize([Module]s); + } + return content; + } + + public void ImportModule(Module module, string content, string version) + { + List<[Module]> [Module]s = null; + if (!string.IsNullOrEmpty(content)) + { + [Module]s = JsonSerializer.Deserialize>(content); + } + if ([Module]s != null) + { + foreach([Module] [Module] in [Module]s) + { + [Module] _[Module] = new [Module](); + _[Module].ModuleId = module.ModuleId; + _[Module].Name = [Module].Name; + _[Module]s.Add[Module](_[Module]); + } + } + } + } +} \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs new file mode 100644 index 00000000..f38a60d5 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using [Owner].[Module]s.Models; + +namespace [Owner].[Module]s.Repository +{ + public interface I[Module]Repository + { + IEnumerable<[Module]> Get[Module]s(int ModuleId); + [Module] Get[Module](int [Module]Id); + [Module] Add[Module]([Module] [Module]); + [Module] Update[Module]([Module] [Module]); + void Delete[Module](int [Module]Id); + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs new file mode 100644 index 00000000..2a14bbf4 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.AspNetCore.Http; +using Oqtane.Modules; +using Oqtane.Repository; +using [Owner].[Module]s.Models; + +namespace [Owner].[Module]s.Repository +{ + public class [Module]Context : DBContextBase, IService + { + public virtual DbSet<[Module]> [Module] { get; set; } + + public [Module]Context(ITenantResolver tenantResolver, IHttpContextAccessor accessor) : base(tenantResolver, accessor) + { + // ContextBase handles multi-tenant database connections + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs new file mode 100644 index 00000000..9b83b239 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore; +using System.Linq; +using System.Collections.Generic; +using Oqtane.Modules; +using [Owner].[Module]s.Models; + +namespace [Owner].[Module]s.Repository +{ + public class [Module]Repository : I[Module]Repository, IService + { + private readonly [Module]Context _db; + + public [Module]Repository([Module]Context context) + { + _db = context; + } + + public IEnumerable<[Module]> Get[Module]s(int ModuleId) + { + return _db.[Module].Where(item => item.ModuleId == ModuleId); + } + + public [Module] Get[Module](int [Module]Id) + { + return _db.[Module].Find([Module]Id); + } + + public [Module] Add[Module]([Module] [Module]) + { + _db.[Module].Add([Module]); + _db.SaveChanges(); + return [Module]; + } + + public [Module] Update[Module]([Module] [Module]) + { + _db.Entry([Module]).State = EntityState.Modified; + _db.SaveChanges(); + return [Module]; + } + + public void Delete[Module](int [Module]Id) + { + [Module] [Module] = _db.[Module].Find([Module]Id); + _db.[Module].Remove([Module]); + _db.SaveChanges(); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/01.00.00.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/01.00.00.sql new file mode 100644 index 00000000..0e7266e6 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/01.00.00.sql @@ -0,0 +1,26 @@ +/* +Create [Module] table +*/ + +CREATE TABLE [dbo].[[Owner][Module]]( + [[Module]Id] [int] IDENTITY(1,1) NOT NULL, + [ModuleId] [int] NOT NULL, + [Name] [nvarchar](256) NOT NULL, + [CreatedBy] [nvarchar](256) NOT NULL, + [CreatedOn] [datetime] NOT NULL, + [ModifiedBy] [nvarchar](256) NOT NULL, + [ModifiedOn] [datetime] NOT NULL, + CONSTRAINT [PK_[Owner][Module]] PRIMARY KEY CLUSTERED + ( + [[Module]Id] ASC + ) +) +GO + +/* +Create foreign key relationships +*/ +ALTER TABLE [dbo].[[Owner][Module]] WITH CHECK ADD CONSTRAINT [FK_[Owner][Module]_Module] FOREIGN KEY([ModuleId]) +REFERENCES [dbo].Module ([ModuleId]) +ON DELETE CASCADE +GO \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Scripts/Tenant.01.00.00.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/Tenant.01.00.00.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Oqtane.Server/Modules/[Module]/Scripts/Tenant.01.00.00.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/Tenant.01.00.00.sql diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Shared/Modules/[Module]/Models/[Module].cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Shared/Modules/[Module]/Models/[Module].cs new file mode 100644 index 00000000..5a6d7896 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Shared/Modules/[Module]/Models/[Module].cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using Oqtane.Models; + +namespace [Owner].[Module]s.Models +{ + [Table("[Owner][Module]")] + public class [Module] : IAuditable + { + public int [Module]Id { get; set; } + public int ModuleId { get; set; } + public string Name { get; set; } + + public string CreatedBy { get; set; } + public DateTime CreatedOn { get; set; } + public string ModifiedBy { get; set; } + public DateTime ModifiedOn { get; set; } + } +} diff --git a/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec similarity index 69% rename from Oqtane.Framework.nuspec rename to Oqtane.Package/Oqtane.Framework.nuspec index 3b82d16e..20827508 100644 --- a/Oqtane.Framework.nuspec +++ b/Oqtane.Package/Oqtane.Framework.nuspec @@ -17,9 +17,9 @@ A modular application framework for Blazor - - - - + + + + \ No newline at end of file diff --git a/nuget.exe b/Oqtane.Package/nuget.exe similarity index 100% rename from nuget.exe rename to Oqtane.Package/nuget.exe diff --git a/Oqtane.Package/pack.cmd b/Oqtane.Package/pack.cmd new file mode 100644 index 00000000..aea8d441 --- /dev/null +++ b/Oqtane.Package/pack.cmd @@ -0,0 +1,3 @@ +DEL "*.nupkg" +nuget.exe pack Oqtane.Framework.nuspec + \ No newline at end of file diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 6ed8b830..348673e1 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -12,6 +12,7 @@ using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; using System; +using System.Runtime.InteropServices.ComTypes; // ReSharper disable StringIndexOfIsCultureSpecific.1 namespace Oqtane.Controllers @@ -147,22 +148,38 @@ namespace Oqtane.Controllers { if (ModelState.IsValid) { - string rootPath = Directory.GetParent(_environment.ContentRootPath).FullName; - string templatePath = Path.Combine(rootPath, "Oqtane.Client\\Modules\\Admin\\ModuleCreator\\Templates\\"); - ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, moduleDefinition); - moduleDefinition.ModuleDefinitionName = "Oqtane.Modules." + moduleDefinition.Name + "s, Oqtane.Client"; + string templatePath = Path.Combine(Directory.GetParent(_environment.ContentRootPath).FullName, "Oqtane.Client\\Modules\\Admin\\ModuleCreator\\Templates\\" + moduleDefinition.Template + "\\"); + string rootPath; + + if (moduleDefinition.Template == "internal") + { + rootPath = Directory.GetParent(_environment.ContentRootPath).FullName + "\\"; + moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, Oqtane.Client"; + moduleDefinition.ServerAssemblyName = "Oqtane.Server"; + } + else + { + rootPath = Directory.GetParent(_environment.ContentRootPath).Parent.FullName + "\\" + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module\\"; + moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Client"; + moduleDefinition.ServerAssemblyName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Server"; + } + + ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, templatePath, moduleDefinition); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Definition Created {ModuleDefinition}", moduleDefinition); + Models.Module module = _modules.GetModule(int.Parse(moduleid)); module.ModuleDefinitionName = moduleDefinition.ModuleDefinitionName; _modules.UpdateModule(module); + _installationManager.RestartApplication(); } } - private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, ModuleDefinition moduleDefinition) + private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string templatePath, ModuleDefinition moduleDefinition) { // process folder - string folderPath = current.FullName.Replace("Oqtane.Client\\Modules\\Admin\\ModuleCreator\\Templates\\", ""); + string folderPath = rootPath + current.FullName.Replace(templatePath, ""); + folderPath = folderPath.Replace("[Owner]", moduleDefinition.Owner); folderPath = folderPath.Replace("[Module]", moduleDefinition.Name); if (!Directory.Exists(folderPath)) { @@ -176,19 +193,22 @@ namespace Oqtane.Controllers { // process file string filePath = Path.Combine(folderPath, file.Name); + filePath = filePath.Replace("[Owner]", moduleDefinition.Owner); filePath = filePath.Replace("[Module]", moduleDefinition.Name); string text = System.IO.File.ReadAllText(file.FullName); + text = text.Replace("[Owner]", moduleDefinition.Owner); text = text.Replace("[Module]", moduleDefinition.Name); text = text.Replace("[Description]", moduleDefinition.Description); text = text.Replace("[RootPath]", rootPath); + text = text.Replace("[ServerAssemblyName]", moduleDefinition.ServerAssemblyName); text = text.Replace("[Folder]", folderPath); text = text.Replace("[File]", Path.GetFileName(filePath)); System.IO.File.WriteAllText(filePath, text); if (Path.GetExtension(filePath) == ".sql") { - // execute script + // execute script in curent tenant foreach (string query in text.Split("GO", StringSplitOptions.RemoveEmptyEntries)) { _sql.ExecuteNonQuery(_resolver.GetTenant(), query); @@ -200,7 +220,7 @@ namespace Oqtane.Controllers foreach (DirectoryInfo folder in folders.Reverse()) { - ProcessTemplatesRecursively(folder, rootPath, moduleDefinition); + ProcessTemplatesRecursively(folder, rootPath, templatePath, moduleDefinition); } } } diff --git a/Oqtane.Server/Repository/SqlRepository.cs b/Oqtane.Server/Repository/SqlRepository.cs index c9bdf734..2952752e 100644 --- a/Oqtane.Server/Repository/SqlRepository.cs +++ b/Oqtane.Server/Repository/SqlRepository.cs @@ -15,7 +15,15 @@ namespace Oqtane.Repository using (conn) { PrepareCommand(conn, cmd, query); - int val = cmd.ExecuteNonQuery(); + int val = -1; + try + { + val = cmd.ExecuteNonQuery(); + } + catch + { + // an error occurred executing the query + } return val; } } diff --git a/Oqtane.Shared/Models/ModuleDefinition.cs b/Oqtane.Shared/Models/ModuleDefinition.cs index 33880e1f..fe39b8e6 100644 --- a/Oqtane.Shared/Models/ModuleDefinition.cs +++ b/Oqtane.Shared/Models/ModuleDefinition.cs @@ -19,6 +19,7 @@ namespace Oqtane.Models PermissionNames = ""; ServerAssemblyName = ""; ControlTypeRoutes = ""; + Template = ""; } public int ModuleDefinitionId { get; set; } @@ -54,10 +55,12 @@ namespace Oqtane.Models [NotMapped] public string ServerAssemblyName { get; set; } [NotMapped] - public string ControlTypeTemplate { get; set; } - [NotMapped] public string ControlTypeRoutes { get; set; } [NotMapped] + public string Template { get; set; } + [NotMapped] + public string ControlTypeTemplate { get; set; } + [NotMapped] public string AssemblyName { get; set; } [NotMapped] public string Permissions { get; set; } diff --git a/Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs b/Oqtane.Shared/Modules/HtmlText/Models/HtmlTextInfo.cs similarity index 100% rename from Oqtane.Shared/Modules/Models/HtmlText/HtmlTextInfo.cs rename to Oqtane.Shared/Modules/HtmlText/Models/HtmlTextInfo.cs diff --git a/pack.cmd b/pack.cmd deleted file mode 100644 index af7998a6..00000000 --- a/pack.cmd +++ /dev/null @@ -1,3 +0,0 @@ -DEL "*.nupkg" -C:\Nuget\nuget.exe pack Oqtane.Framework.nuspec - \ No newline at end of file From 8f4fd6f1358f068f3f0a318c48f57a78a2b258c9 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 10 Apr 2020 14:18:22 -0400 Subject: [PATCH 128/265] minor improvements to field level help --- Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor index 763eb5ea..d5f98e72 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor @@ -7,7 +7,7 @@ @@ -21,7 +21,7 @@ @@ -29,7 +29,7 @@ @@ -37,7 +37,7 @@ @@ -45,7 +45,7 @@ @if (_pageName != string.Empty) @@ -55,7 +55,7 @@ } @@ -66,7 +66,7 @@ } @@ -77,7 +77,7 @@ } @@ -86,7 +86,7 @@ @@ -94,7 +94,7 @@ @@ -102,7 +102,7 @@ @if (!string.IsNullOrEmpty(_exception)) @@ -112,7 +112,7 @@ } @@ -121,7 +121,7 @@ @@ -129,7 +129,7 @@
    - + @@ -64,7 +64,7 @@ { if (!string.IsNullOrEmpty(_owner) && !string.IsNullOrEmpty(_module) && !string.IsNullOrEmpty(_template)) { - var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, 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 From 89066ecfd0617d6596df8d0a35dc9224f6b0304f Mon Sep 17 00:00:00 2001 From: Grayson Walker Date: Fri, 10 Apr 2020 21:49:57 -0400 Subject: [PATCH 129/265] help text update --- Oqtane.Client/Modules/Admin/Logs/Detail.razor | 56 ++++++++-------- .../Modules/Admin/ModuleDefinitions/Add.razor | 2 +- .../Admin/ModuleDefinitions/Edit.razor | 6 +- .../Modules/Admin/Modules/Export.razor | 4 +- .../Modules/Admin/Modules/Import.razor | 4 +- .../Modules/Admin/Modules/Settings.razor | 14 ++-- .../Modules/Admin/Profiles/Edit.razor | 36 +++++------ Oqtane.Client/Modules/Admin/Site/Index.razor | 64 +++++++++---------- Oqtane.Client/Modules/Admin/Sites/Add.razor | 36 +++++------ Oqtane.Client/Modules/Admin/Sites/Edit.razor | 28 ++++---- Oqtane.Client/Modules/Admin/Sql/Index.razor | 8 +-- Oqtane.Client/Modules/Admin/Tenants/Add.razor | 28 ++++---- .../Modules/Admin/Tenants/Edit.razor | 10 +-- Oqtane.Client/Modules/Admin/Themes/Add.razor | 4 +- .../Modules/Admin/Upgrade/Index.razor | 2 +- .../Modules/Admin/UserProfile/Add.razor | 12 ++-- Oqtane.Client/Modules/Admin/Users/Edit.razor | 4 +- Oqtane.Client/Modules/Admin/Users/Roles.razor | 12 ++-- 18 files changed, 165 insertions(+), 165 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Logs/Detail.razor b/Oqtane.Client/Modules/Admin/Logs/Detail.razor index 2f9c054d..02d4c804 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Detail.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Detail.razor @@ -10,52 +10,52 @@ @if (_pageName != string.Empty) { } @@ -63,10 +63,10 @@ { } @@ -74,62 +74,62 @@ { } @if (!string.IsNullOrEmpty(_exception)) { }
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index a342297b..49e70702 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -8,7 +8,7 @@ + + + + + + + +

    +} + +@code { + private int roleid; + private string name = string.Empty; + private List users; + private int userid = -1; + private string effectivedate = string.Empty; + private string expirydate = string.Empty; + private List userroles; + + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; + + protected override async Task OnInitializedAsync() + { + try + { + roleid = Int32.Parse(PageState.QueryString["id"]); + Role role = await RoleService.GetRoleAsync(roleid); + name = role.Name; + users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId); + users = users.Where(item => item.Role.Name == Constants.RegisteredRole).ToList(); + await GetUserRoles(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Users {Error}", ex.Message); + AddModuleMessage("Error Loading Users", MessageType.Error); + } + } + + private async Task GetUserRoles() + { + try + { + userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId); + userroles = userroles.Where(item => item.RoleId == roleid).ToList(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading User Roles {RoleId} {Error}", roleid, ex.Message); + AddModuleMessage("Error Loading User Roles", MessageType.Error); + } + } + + private async Task SaveUserRole() + { + try + { + if (userid != -1) + { + var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault(); + if (userrole != null) + { + if (string.IsNullOrEmpty(effectivedate)) + { + userrole.EffectiveDate = null; + } + else + { + userrole.EffectiveDate = DateTime.Parse(effectivedate); + } + + if (string.IsNullOrEmpty(expirydate)) + { + userrole.ExpiryDate = null; + } + else + { + userrole.ExpiryDate = DateTime.Parse(expirydate); + } + await UserRoleService.UpdateUserRoleAsync(userrole); + } + else + { + userrole = new UserRole(); + userrole.UserId = userid; + userrole.RoleId = roleid; + + if (string.IsNullOrEmpty(effectivedate)) + { + userrole.EffectiveDate = null; + } + else + { + userrole.EffectiveDate = DateTime.Parse(effectivedate); + } + + if (string.IsNullOrEmpty(expirydate)) + { + userrole.ExpiryDate = null; + } + else + { + userrole.ExpiryDate = DateTime.Parse(expirydate); + } + + await UserRoleService.AddUserRoleAsync(userrole); + } + + await GetUserRoles(); + await logger.LogInformation("User Assigned To Role {UserRole}", userrole); + AddModuleMessage("User Assigned To Role", MessageType.Success); + } + else + { + AddModuleMessage("You Must Select A User", MessageType.Warning); + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Saving User Roles {RoleId} {Error}", roleid, ex.Message); + AddModuleMessage("Error Saving User Roles", MessageType.Error); + } + } + + private async Task DeleteUserRole(int UserRoleId) + { + try + { + await UserRoleService.DeleteUserRoleAsync(UserRoleId); + await GetUserRoles(); + await logger.LogInformation("User Removed From Role {UserRoleId}", UserRoleId); + AddModuleMessage("User Removed From Role", MessageType.Success); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message); + AddModuleMessage("Error Removing User From Role", MessageType.Error); + } + } +} diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index 0758e80d..6f3ab545 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -6,18 +6,18 @@ @inject ISettingService SettingService @inject INotificationService NotificationService +@if (PageState.User != null && photofileid != -1) +{ + @displayname +} +else +{ +
    +} @if (PageState.User != null) { - @if (photofileid != -1) - { - @displayname - } - else - { -
    - }
    - + diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index 732d7d5c..98ee9c93 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -6,15 +6,15 @@ diff --git a/Oqtane.Client/Modules/Admin/Modules/Export.razor b/Oqtane.Client/Modules/Admin/Modules/Export.razor index 77c0ba53..eaa0bc92 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Export.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Export.razor @@ -7,10 +7,10 @@ diff --git a/Oqtane.Client/Modules/Admin/Modules/Import.razor b/Oqtane.Client/Modules/Admin/Modules/Import.razor index 2a4d3aa0..e341ad78 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Import.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Import.razor @@ -7,10 +7,10 @@ diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index aeb4406e..44a55284 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -33,18 +33,18 @@ @@ -19,6 +20,7 @@ else + diff --git a/Oqtane.Client/Modules/Admin/Roles/Users.razor b/Oqtane.Client/Modules/Admin/Roles/Users.razor new file mode 100644 index 00000000..c292d3d5 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/Roles/Users.razor @@ -0,0 +1,201 @@ +@namespace Oqtane.Modules.Admin.Roles +@inherits ModuleBase +@inject IRoleService RoleService +@inject IUserRoleService UserRoleService + +@if (userroles == null) +{ +

    Loading...

    +} +else +{ +
    - + - +
    - +
    - + - +
    - + - +
    - + - +
    - + - @foreach (KeyValuePair container in _containers) { @@ -55,16 +55,16 @@
    - +
    - + - @foreach (Page p in PageState.Pages) { diff --git a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor index bcfd2d3f..d232a5ac 100644 --- a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor @@ -6,66 +6,66 @@ - - - - - - - - - -} + + @if (_packages.Count > 0) + { + + + +
    +
    + + + + + + + + + + + } + +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    - + - @@ -73,10 +73,10 @@
    - + - diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index b20ccd1d..a4069b67 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -12,31 +12,31 @@ - - - - - - - - - + + Cancel } - -Cancel - - @code { private List _packages; @@ -52,8 +57,8 @@ { var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); _packages = await PackageService.GetPackagesAsync("module"); - - foreach(Package package in _packages.ToArray()) + + foreach (Package package in _packages.ToArray()) { if (moduledefinitions.Exists(item => Utilities.GetTypeName(item.ModuleDefinitionName) == package.PackageId)) { @@ -81,18 +86,18 @@ } } - private async Task DownloadModule(string moduledefinitionname, string version) + private async Task DownloadModule(string packageid, string version) { try { - await PackageService.DownloadPackageAsync(moduledefinitionname, version, "Modules"); - await logger.LogInformation("Module {ModuleDefinitionName} {Version} Downloaded Successfully", moduledefinitionname, version); - AddModuleMessage("Module Downloaded Successfully. Click Install To Complete Installation.", MessageType.Success); + await PackageService.DownloadPackageAsync(packageid, version, "Modules"); + await logger.LogInformation("Module {ModuleDefinitionName} {Version} Downloaded Successfully", packageid, version); + AddModuleMessage("Modules Downloaded Successfully. Click Install To Complete Installation.", MessageType.Success); StateHasChanged(); } catch (Exception ex) { - await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version}", moduledefinitionname, version); + await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version}", packageid, version); AddModuleMessage("Error Downloading Module", MessageType.Error); } } diff --git a/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor b/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor index b37adc98..d4ac4947 100644 --- a/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor +++ b/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor @@ -1,29 +1,38 @@ @namespace Oqtane.Modules.Admin.SystemInfo @inherits ModuleBase +@inject ISystemService SystemService
    - + - +
    - + - +
    - + - +
    - + @@ -44,7 +44,7 @@
    - + @@ -52,10 +52,10 @@
    - + - @foreach (KeyValuePair item in _themes) { @@ -73,10 +73,10 @@
    - + - @foreach (KeyValuePair panelayout in _panelayouts) { @@ -87,10 +87,10 @@
    - + - @foreach (KeyValuePair container in _containers) { @@ -101,10 +101,10 @@
    - + - @@ -112,10 +112,10 @@
    - + - @@ -127,42 +127,42 @@
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    @@ -171,10 +171,10 @@ + + + + + + + + + + + } + +
    - + - @@ -182,7 +182,7 @@
    - + @@ -190,7 +190,7 @@
    - + diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 0bb51513..7c18431c 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -17,10 +17,10 @@ else } diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor index 838615ee..2eb3110d 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor @@ -11,34 +11,34 @@
    - + - @foreach (Tenant tenant in _tenants) { @@ -31,26 +31,26 @@ else
    - + - +
    - + - +
    - + - @foreach (KeyValuePair item in _themes) { @@ -61,10 +61,10 @@ else
    - + - @foreach (KeyValuePair panelayout in _panelayouts) { @@ -75,10 +75,10 @@ else
    - + - @foreach (KeyValuePair container in _containers) { @@ -89,10 +89,10 @@ else
    - + - @foreach (SiteTemplate siteTemplate in _siteTemplates) { @@ -105,18 +105,18 @@ else {
    - + - +
    - + - +
    - + - +
    - + - +
    - + - +
    diff --git a/Oqtane.Client/Modules/Admin/Tenants/Add.razor b/Oqtane.Client/Modules/Admin/Tenants/Add.razor index 962b9e4c..3fedf8fb 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Add.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Add.razor @@ -7,18 +7,18 @@
    - + - +
    - + - @@ -26,26 +26,26 @@
    - + - +
    - + - +
    - + - @@ -53,18 +53,18 @@
    - + - +
    - + - +
    diff --git a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor index 56f3fe20..f8a06db1 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor @@ -6,25 +6,25 @@ diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor index d78ab5e5..dcf822f9 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Add.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -8,10 +8,10 @@
    - + @if (name == Constants.MasterTenant) { - + } else { - + }
    - + - +
    - + - +
    diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index 5d55b6aa..e70ab017 100644 --- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor +++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor @@ -8,7 +8,7 @@ + + + + + + + + + + + + + + + + + + } + else + { + +
    +
    + + + + + + + + + + + + + + + + + + } +

    + + } + + @code { private string username = string.Empty; @@ -196,15 +187,15 @@ username = PageState.User.Username; email = PageState.User.Email; displayname = PageState.User.DisplayName; - + if (PageState.User.PhotoFileId != null) { photofileid = PageState.User.PhotoFileId.Value; } - + profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); - + await LoadNotificationsAsync(); } else @@ -243,12 +234,12 @@ user.DisplayName = (displayname == string.Empty ? username : displayname); user.PhotoFileId = null; photofileid = filemanager.GetFileId(); - + if (photofileid != -1) { user.PhotoFileId = photofileid; } - + await UserService.UpdateUserAsync(user); await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); await logger.LogInformation("User Profile Saved"); @@ -294,7 +285,7 @@ { await NotificationService.DeleteNotificationAsync(Notification.NotificationId); } - + await logger.LogInformation("Notification Deleted {Notification}", Notification); await LoadNotificationsAsync(); StateHasChanged(); @@ -309,7 +300,7 @@ private async void FilterChanged(ChangeEventArgs e) { filter = (string)e.Value; - + await LoadNotificationsAsync(); StateHasChanged(); } diff --git a/Oqtane.Client/Modules/Controls/PermissionGrid.razor b/Oqtane.Client/Modules/Controls/PermissionGrid.razor index 48a3d5b8..543403f2 100644 --- a/Oqtane.Client/Modules/Controls/PermissionGrid.razor +++ b/Oqtane.Client/Modules/Controls/PermissionGrid.razor @@ -12,7 +12,7 @@ @foreach (PermissionString permission in _permissions) { - + } @foreach (Role role in _roles) @@ -38,7 +38,7 @@ @foreach (PermissionString permission in _permissions) { - + } diff --git a/Oqtane.Client/Modules/Controls/TabStrip.razor b/Oqtane.Client/Modules/Controls/TabStrip.razor index 10527d0f..4f0b872f 100644 --- a/Oqtane.Client/Modules/Controls/TabStrip.razor +++ b/Oqtane.Client/Modules/Controls/TabStrip.razor @@ -38,7 +38,15 @@ public RenderFragment ChildContent { get; set; } // contains the TabPanels [Parameter] - public string ActiveTab { get; set; } // optional - defaults to first TabPanel if not specified + public string ActiveTab { get; set; } // optional - defaults to first TabPanel if not specified. Can also be set using a "tab=" querystring parameter. + + protected override void OnInitialized() + { + if (PageState.QueryString.ContainsKey("tab")) + { + ActiveTab = PageState.QueryString["tab"]; + } + } internal void AddTabPanel(TabPanel tabPanel) { @@ -47,6 +55,7 @@ { ActiveTab = tabPanel.Name; } + StateHasChanged(); } private string DisplayHeading(string Name, string Heading) From 70502cd8815410b310359e475e61ac77b9821396 Mon Sep 17 00:00:00 2001 From: Sean Long Date: Fri, 17 Apr 2020 16:25:00 -0400 Subject: [PATCH 153/265] OS independent file paths --- .gitignore | 1 + Oqtane.Client/Services/FileService.cs | 4 ++-- Oqtane.Client/Services/FolderService.cs | 4 ++-- Oqtane.Server/Controllers/FileController.cs | 24 ++++++++++--------- Oqtane.Server/Controllers/FolderController.cs | 5 ++-- .../Controllers/ModuleDefinitionController.cs | 10 ++++---- Oqtane.Server/Controllers/ThemeController.cs | 2 +- Oqtane.Server/Controllers/UserController.cs | 4 ++-- .../Infrastructure/DatabaseManager.cs | 4 ++-- .../Infrastructure/InstallationManager.cs | 2 +- .../SiteTemplates/DefaultSiteTemplate.cs | 11 +++++---- Oqtane.Server/Repository/SiteRepository.cs | 2 +- Oqtane.Upgrade/Program.cs | 4 ++-- 13 files changed, 41 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 33c751ad..8f8f91ba 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ Oqtane.Server/appsettings.json Oqtane.Server/Data/*.mdf Oqtane.Server/Data/*.ldf +/Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index 09389f1e..c073a460 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -42,9 +42,9 @@ namespace Oqtane.Services public async Task> GetFilesAsync(int siteId, string folderPath) { - if (!folderPath.EndsWith("\\")) + if (!(folderPath.EndsWith(System.IO.Path.DirectorySeparatorChar) || folderPath.EndsWith(System.IO.Path.AltDirectorySeparatorChar))) { - folderPath += "\\"; + folderPath = System.IO.Path.Combine(folderPath, " ").TrimEnd(' '); } var path = WebUtility.UrlEncode(folderPath); diff --git a/Oqtane.Client/Services/FolderService.cs b/Oqtane.Client/Services/FolderService.cs index 9d63ae8d..6c3fd69d 100644 --- a/Oqtane.Client/Services/FolderService.cs +++ b/Oqtane.Client/Services/FolderService.cs @@ -38,9 +38,9 @@ namespace Oqtane.Services public async Task GetFolderAsync(int siteId, [NotNull] string folderPath) { - if (!folderPath.EndsWith("\\")) + if (!(folderPath.EndsWith(System.IO.Path.DirectorySeparatorChar) || folderPath.EndsWith(System.IO.Path.AltDirectorySeparatorChar))) { - folderPath += "\\"; + folderPath = System.IO.Path.Combine(folderPath, " ").TrimEnd(' '); } var path = WebUtility.UrlEncode(folderPath); diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 71095f2d..5b22b062 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -161,7 +161,7 @@ namespace Oqtane.Controllers { _files.DeleteFile(id); - string filepath = Path.Combine(GetFolderPath(file.Folder) + file.Name); + string filepath = Path.Combine(GetFolderPath(file.Folder), file.Name); if (System.IO.File.Exists(filepath)) { System.IO.File.Delete(filepath); @@ -199,14 +199,15 @@ namespace Oqtane.Controllers try { var client = new WebClient(); + string targetPath = Path.Combine(folderPath, filename); // remove file if it already exists - if (System.IO.File.Exists(folderPath + filename)) + if (System.IO.File.Exists(targetPath)) { - System.IO.File.Delete(folderPath + filename); + System.IO.File.Delete(targetPath); } - client.DownloadFile(url, folderPath + filename); - _files.AddFile(CreateFile(filename, folder.FolderId, folderPath + filename)); + client.DownloadFile(url, targetPath); + _files.AddFile(CreateFile(filename, folder.FolderId, targetPath)); } catch { @@ -262,7 +263,7 @@ namespace Oqtane.Controllers string upload = await MergeFile(folderPath, file.FileName); if (upload != "" && folderId != -1) { - _files.AddFile(CreateFile(upload, folderId, folderPath + upload)); + _files.AddFile(CreateFile(upload, folderId, Path.Combine(folderPath, upload))); } } else @@ -400,7 +401,7 @@ namespace Oqtane.Controllers { if (_userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) { - string filepath = GetFolderPath(file.Folder) + file.Name; + string filepath = Path.Combine(GetFolderPath(file.Folder) , file.Name); if (System.IO.File.Exists(filepath)) { byte[] filebytes = System.IO.File.ReadAllBytes(filepath); @@ -430,12 +431,12 @@ namespace Oqtane.Controllers private string GetFolderPath(Folder folder) { - return _environment.ContentRootPath + "\\Content\\Tenants\\" + _tenants.GetTenant().TenantId.ToString() + "\\Sites\\" + folder.SiteId.ToString() + "\\" + folder.Path; + return Path.Combine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path, " ").TrimEnd(' '); } private string GetFolderPath(string folder) { - return Path.Combine(_environment.WebRootPath, folder); + return Path.Combine(_environment.WebRootPath, folder, " ").TrimEnd(' '); } private void CreateDirectory(string folderpath) @@ -443,10 +444,11 @@ namespace Oqtane.Controllers if (!Directory.Exists(folderpath)) { string path = ""; - string[] folders = folderpath.Split(new[] {'\\'}, StringSplitOptions.RemoveEmptyEntries); + var separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries); foreach (string folder in folders) { - path += folder + "\\"; + path = Path.Combine(path, folder); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index ee75a536..a1e48fc1 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -9,6 +9,7 @@ using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; +using System.IO; namespace Oqtane.Controllers { @@ -109,7 +110,7 @@ namespace Oqtane.Controllers if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { Folder parent = _folders.GetFolder(folder.ParentId.Value); - folder.Path = parent.Path + folder.Name + "\\"; + folder.Path = Path.Combine(parent.Path, folder.Name); } folder = _folders.AddFolder(folder); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder); @@ -134,7 +135,7 @@ namespace Oqtane.Controllers if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { Folder parent = _folders.GetFolder(folder.ParentId.Value); - folder.Path = parent.Path + folder.Name + "\\"; + folder.Path = Path.Combine(parent.Path, folder.Name); } folder = _folders.UpdateFolder(folder); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder); diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index b197e62d..03e1937f 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -138,7 +138,7 @@ namespace Oqtane.Controllers assemblyname = assemblyname.Replace(".Server", ""); // clean up module static resource folder - string folder = Path.Combine(_environment.WebRootPath, "Modules\\" + assemblyname); + string folder = Path.Combine(_environment.WebRootPath, Path.Combine("Modules",assemblyname)); if (Directory.Exists(folder)) { Directory.Delete(folder, true); @@ -189,17 +189,17 @@ namespace Oqtane.Controllers { string rootPath; DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath); - string templatePath = Path.Combine(rootFolder.FullName, "Oqtane.Client\\Modules\\Admin\\ModuleCreator\\Templates\\" + moduleDefinition.Template + "\\"); + string templatePath = Path.Combine(rootFolder.FullName, "Oqtane.Client", "Modules", "Admin", "ModuleCreator", "Templates",moduleDefinition.Template," ").TrimEnd(' '); if (moduleDefinition.Template == "internal") { - rootPath = rootFolder.FullName + "\\"; + rootPath = rootFolder.FullName; moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, Oqtane.Client"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server"; } else { - rootPath = rootFolder.Parent.FullName + "\\" + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module\\"; + rootPath = Path.Combine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module"); moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Client"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Server"; } @@ -218,7 +218,7 @@ namespace Oqtane.Controllers private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string rootFolder, string templatePath, ModuleDefinition moduleDefinition) { // process folder - string folderPath = rootPath + current.FullName.Replace(templatePath, ""); + string folderPath = Path.Combine(rootPath, current.FullName.Replace(templatePath, "")); folderPath = folderPath.Replace("[Owner]", moduleDefinition.Owner); folderPath = folderPath.Replace("[Module]", moduleDefinition.Name); if (!Directory.Exists(folderPath)) diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index af7af8e1..5f516851 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -58,7 +58,7 @@ namespace Oqtane.Controllers { themename = theme.ThemeName.Substring(0, theme.ThemeName.IndexOf(",")); - string folder = Path.Combine(_environment.WebRootPath, "Themes\\" + themename); + string folder = Path.Combine(_environment.WebRootPath, "Themes" , themename); if (Directory.Exists(folder)) { Directory.Delete(folder, true); diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index c0cec6a8..dfcb5b48 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -150,7 +150,7 @@ namespace Oqtane.Controllers } // add folder for user - Folder folder = _folders.GetFolder(user.SiteId, "Users\\"); + Folder folder = _folders.GetFolder(user.SiteId, "Users"); if (folder != null) { _folders.AddFolder(new Folder @@ -158,7 +158,7 @@ namespace Oqtane.Controllers SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", - Path = folder.Path + newUser.UserId.ToString() + "\\", + Path = System.IO.Path.Combine(folder.Path, newUser.UserId.ToString()), Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index dabe8c78..40f30092 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -387,14 +387,14 @@ namespace Oqtane.Infrastructure } // add folder for user - var folder = folderRepository.GetFolder(user.SiteId, "Users\\"); + var folder = folderRepository.GetFolder(user.SiteId, "Users"); if (folder != null) folderRepository.AddFolder(new Folder { SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", - Path = folder.Path + newUser.UserId + "\\", + Path = Path.Combine(folder.Path, newUser.UserId.ToString()), Order = 1, IsSystem = true, Permissions = new List diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index a85fdcc7..f37d719e 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -103,7 +103,7 @@ namespace Oqtane.Infrastructure case ".svg": case ".js": case ".css": - filename = sourceFolder + "\\" + entry.FullName.Replace("wwwroot", name).Replace("/", "\\"); + filename = Path.Combine(sourceFolder, entry.FullName.Replace("wwwroot", name)); if (!Directory.Exists(Path.GetDirectoryName(filename))) { Directory.CreateDirectory(Path.GetDirectoryName(filename)); diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index daab95f1..150373a2 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Oqtane.Repository; using Microsoft.AspNetCore.Hosting; using Oqtane.Shared; +using System.IO; namespace Oqtane.SiteTemplates { @@ -131,16 +132,16 @@ namespace Oqtane.SiteTemplates } }); - if (System.IO.File.Exists(_environment.WebRootPath + "\\images\\logo.png")) + if (System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "images", "logo.png"))) { - string folderpath = _environment.ContentRootPath + "\\Content\\Tenants\\" + site.TenantId.ToString() + "\\Sites\\" + site.SiteId.ToString() + "\\"; + string folderpath = Path.Combine(_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(), "Sites", site.SiteId.ToString()); System.IO.Directory.CreateDirectory(folderpath); - if (!System.IO.File.Exists(folderpath + "logo.png")) + if (!System.IO.File.Exists(Path.Combine(folderpath, "logo.png"))) { - System.IO.File.Copy(_environment.WebRootPath + "\\images\\logo.png", folderpath + "logo.png"); + System.IO.File.Copy(Path.Combine(_environment.WebRootPath, "images", "logo.png"), Path.Combine(folderpath, "logo.png")); } Folder folder = _folderRepository.GetFolder(site.SiteId, ""); - File file = _fileRepository.AddFile(new File { FolderId = folder.FolderId, Name = "logo.png", Extension = "png", Size = 8192, ImageHeight = 80, ImageWidth = 250 }); + Oqtane.Models.File file = _fileRepository.AddFile(new Oqtane.Models.File { FolderId = folder.FolderId, Name = "logo.png", Extension = "png", Size = 8192, ImageHeight = 80, ImageWidth = 250 }); site.LogoFileId = file.FileId; _siteRepository.UpdateSite(site); } diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 4cba5e20..2d5db9e7 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -583,7 +583,7 @@ namespace Oqtane.Repository }); _folderRepository.AddFolder(new Folder { - SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = "Users\\", Order = 1, IsSystem = true, + SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = "Users", Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); diff --git a/Oqtane.Upgrade/Program.cs b/Oqtane.Upgrade/Program.cs index 8aa70d3b..f1f0f9b4 100644 --- a/Oqtane.Upgrade/Program.cs +++ b/Oqtane.Upgrade/Program.cs @@ -13,11 +13,11 @@ namespace Oqtane.Upgrade string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); // assumes that the application executable must be deployed to the /bin of the Oqtane.Server project - if (binfolder.Contains("Oqtane.Server\\bin")) + if (binfolder.Contains(Path.Combine("Oqtane.Server", "bin"))) { // ie. binfolder = Oqtane.Server\bin\Debug\netcoreapp3.0\ string rootfolder = Directory.GetParent(binfolder).Parent.Parent.FullName; - string deployfolder = Path.Combine(rootfolder, "wwwroot\\Framework"); + string deployfolder = Path.Combine(rootfolder, Path.Combine("wwwroot","Framework")); if (Directory.Exists(deployfolder)) { From 209f6db0fb24f83de1fd7ef0fd30a3bc3eba8293 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Fri, 17 Apr 2020 22:22:04 +0200 Subject: [PATCH 154/265] Database creation bug HostUser change is not allowed in silent install --- Oqtane.Server/Infrastructure/DatabaseManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index dabe8c78..0e1c475b 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -419,6 +419,7 @@ namespace Oqtane.Infrastructure { using (var db = new InstallationContext(connectionString)) { + //check if DbUp was initialized return TableExists(db, "SchemaVersions"); } @@ -431,6 +432,7 @@ namespace Oqtane.Infrastructure public static bool TableExists(DbContext context, string schema, string tableName) { + if (!context.Database.CanConnect()) return false; var connection = context.Database.GetDbConnection(); if (connection.State.Equals(ConnectionState.Closed)) From 69ceb5dd426170d260e642932adca31ab45db682 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Fri, 17 Apr 2020 22:29:55 +0200 Subject: [PATCH 155/265] HostUser change is not allowed in silent install --- Oqtane.Server/Infrastructure/DatabaseManager.cs | 4 ++-- Oqtane.Server/appsettings.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 0e1c475b..3485ef63 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -348,10 +348,10 @@ namespace Oqtane.Infrastructure var user = new User { SiteId = site.SiteId, - Username = GetInstallationConfig(SettingKeys.HostUserKey, Constants.HostUser), + Username = Constants.HostUser, Password = password, Email = email, - DisplayName = GetInstallationConfig(SettingKeys.HostUserKey, Constants.HostUser), + DisplayName = Constants.HostUser, }; CreateHostUser(folders, userRoles, roles, users, identityUserManager, user); tenant.IsInitialized = true; diff --git a/Oqtane.Server/appsettings.json b/Oqtane.Server/appsettings.json index a3352489..cad2785c 100644 --- a/Oqtane.Server/appsettings.json +++ b/Oqtane.Server/appsettings.json @@ -2,9 +2,9 @@ "ConnectionStrings": { "DefaultConnection": "" }, + "Runtime" : "Server", "Installation": { "DefaultAlias": "", - "HostUser": "", "HostPassword": "", "HostEmail": "", "SiteTemplate": "", From 62987ca72f41d14d6c9e7a360a07b73e7eb80ba9 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sat, 18 Apr 2020 10:51:07 -0400 Subject: [PATCH 156/265] allow users to modify default module names, descriptions, and categories and improve control panel behavior --- .../Admin/ModuleDefinitions/Edit.razor | 20 +++++-- .../Themes/Controls/ControlPanel.razor | 53 ++++++++++++++----- .../SiteTemplates/EmptySiteTemplate.cs | 47 ++++++++++++++++ .../Repository/ModuleDefinitionRepository.cs | 21 +++++++- Oqtane.Server/Scripts/Master.00.00.00.sql | 3 ++ Oqtane.Shared/Models/ModuleDefinition.cs | 9 ++-- 6 files changed, 130 insertions(+), 23 deletions(-) create mode 100644 Oqtane.Server/Infrastructure/SiteTemplates/EmptySiteTemplate.cs diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index 10671204..743938bf 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -16,10 +16,10 @@ @@ -43,10 +43,10 @@ @@ -158,6 +158,18 @@ try { var moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId); + if (moduledefinition.Name != _name) + { + moduledefinition.Name = _name; + } + if (moduledefinition.Description != _description) + { + moduledefinition.Description = _description; + } + if (moduledefinition.Categories != _categories) + { + moduledefinition.Categories = _categories; + } moduledefinition.Permissions = _permissionGrid.GetPermissions(); await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition); await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition); diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index f2cf43e7..d6fe191d 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -87,22 +87,36 @@ @if (_moduleDefinitions != null) { - + @if (_moduleDefinitionName == "-") + { + + } + else + { + + } @foreach (var moduledefinition in _moduleDefinitions) { - if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Utilize, moduledefinition.Permissions)) + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions)) { } } + @((MarkupString)@_description) } } else @@ -207,6 +221,8 @@ private List _modules = new List(); private Dictionary _containers = new Dictionary(); private string _moduleDefinitionName = "-"; + private string _category = "Common"; + private string _description = ""; private string _pane = ""; private string _title = ""; private string _containerType = ""; @@ -267,6 +283,7 @@ _allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); + _categories = new List(); foreach (ModuleDefinition moduledefinition in _allModuleDefinitions) { if (moduledefinition.Categories != "") @@ -281,23 +298,34 @@ } } - _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories == "").ToList(); + _category = "Common"; + _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList(); + _moduleDefinitionName = "-"; + _description = ""; } } private void CategoryChanged(ChangeEventArgs e) { - var category = (string) e.Value; - if (category == "-") + _category = (string) e.Value; + _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList(); + _moduleDefinitionName = "-"; + _description = ""; + StateHasChanged(); + } + + private void ModuleChanged(ChangeEventArgs e) + { + _moduleDefinitionName = (string)e.Value; + if (_moduleDefinitionName != "-") { - _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories == "").ToList(); + var moduleDefinition = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == _moduleDefinitionName); + _description = "
    " + moduleDefinition.Description + "
    "; } else { - _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(category)).ToList(); + _description = ""; } - - _moduleDefinitionName = "-"; StateHasChanged(); } @@ -371,6 +399,7 @@ _message = "
    Module Added To Page
    "; _moduleDefinitionName = "-"; + _description = ""; _pane = ""; _title = ""; _containerType = ""; diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/EmptySiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/EmptySiteTemplate.cs new file mode 100644 index 00000000..51c4a29b --- /dev/null +++ b/Oqtane.Server/Infrastructure/SiteTemplates/EmptySiteTemplate.cs @@ -0,0 +1,47 @@ +using Oqtane.Models; +using Oqtane.Infrastructure; +using System.Collections.Generic; +using Oqtane.Repository; +using Oqtane.Shared; + +namespace Oqtane.SiteTemplates +{ + public class EmptySiteTemplate : ISiteTemplate + { + private readonly IPermissionRepository _permissionRepository; + + public EmptySiteTemplate(IPermissionRepository permissionRepository) + { + _permissionRepository = permissionRepository; + } + + public string Name + { + get { return "Empty Site Template"; } + } + + public List CreateSite(Site site) + { + List _pageTemplates = new List(); + + _pageTemplates.Add(new PageTemplate + { + Name = "Home", + Parent = "", + Path = "", + Icon = "home", + IsNavigation = true, + IsPersonalizable = false, + EditMode = false, + PagePermissions = _permissionRepository.EncodePermissions( new List { + new Permission(PermissionNames.View, Constants.AllUsersRole, true), + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }), + PageTemplateModules = new List() + }); + + return _pageTemplates; + } + } +} diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index 0023c2e8..bc91823a 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Oqtane.Extensions; using Oqtane.Models; @@ -37,6 +38,8 @@ namespace Oqtane.Repository public void UpdateModuleDefinition(ModuleDefinition moduleDefinition) { + _db.Entry(moduleDefinition).State = EntityState.Modified; + _db.SaveChanges(); _permissions.UpdatePermissions(moduleDefinition.SiteId, EntityNames.ModuleDefinition, moduleDefinition.ModuleDefinitionId, moduleDefinition.Permissions); _cache.Remove("moduledefinitions:" + moduleDefinition.SiteId.ToString()); } @@ -90,6 +93,18 @@ namespace Oqtane.Repository else { // existing module definition + if (!string.IsNullOrEmpty(moduledef.Name)) + { + moduledefinition.Name = moduledef.Name; + } + if (!string.IsNullOrEmpty(moduledef.Description)) + { + moduledefinition.Description = moduledef.Description; + } + if (!string.IsNullOrEmpty(moduledef.Categories)) + { + moduledefinition.Categories = moduledef.Categories; + } if (permissions.Count == 0) { _permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions); @@ -166,7 +181,7 @@ namespace Oqtane.Repository moduledefinition = new ModuleDefinition { Name = moduleType.Substring(moduleType.LastIndexOf(".") + 1), - Description = moduleType.Substring(moduleType.LastIndexOf(".") + 1), + Description = "Manage " + moduleType.Substring(moduleType.LastIndexOf(".") + 1), Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : ""), Version = new Version(1, 0, 0).ToString() }; @@ -175,6 +190,10 @@ namespace Oqtane.Repository moduledefinition.ModuleDefinitionName = qualifiedModuleType; moduledefinition.ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1]; moduledefinition.AssemblyName = assembly.FullName.Split(",")[0]; + if (string.IsNullOrEmpty(moduledefinition.Categories)) + { + moduledefinition.Categories = "Common"; + } if (moduledefinition.Categories == "Admin") { moduledefinition.Permissions = new List diff --git a/Oqtane.Server/Scripts/Master.00.00.00.sql b/Oqtane.Server/Scripts/Master.00.00.00.sql index 463dbdf6..5dcf652e 100644 --- a/Oqtane.Server/Scripts/Master.00.00.00.sql +++ b/Oqtane.Server/Scripts/Master.00.00.00.sql @@ -40,6 +40,9 @@ GO CREATE TABLE [dbo].[ModuleDefinition]( [ModuleDefinitionId] [int] IDENTITY(1,1) NOT NULL, [ModuleDefinitionName] [nvarchar](200) NOT NULL, + [Name] [nvarchar](200) NULL, + [Description] [nvarchar](2000) NULL, + [Categories] [nvarchar](200) NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, diff --git a/Oqtane.Shared/Models/ModuleDefinition.cs b/Oqtane.Shared/Models/ModuleDefinition.cs index 4832a5e2..0bc66da1 100644 --- a/Oqtane.Shared/Models/ModuleDefinition.cs +++ b/Oqtane.Shared/Models/ModuleDefinition.cs @@ -24,6 +24,9 @@ namespace Oqtane.Models public int ModuleDefinitionId { get; set; } public string ModuleDefinitionName { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string Categories { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } @@ -33,12 +36,6 @@ namespace Oqtane.Models [NotMapped] public int SiteId { get; set; } [NotMapped] - public string Name { get; set; } - [NotMapped] - public string Description { get; set; } - [NotMapped] - public string Categories { get; set; } - [NotMapped] public string Version { get; set; } [NotMapped] public string Owner { get; set; } From ce118096b7df9e133b9011a411e86eba21502be6 Mon Sep 17 00:00:00 2001 From: Sean Long Date: Sat, 18 Apr 2020 12:53:41 -0400 Subject: [PATCH 157/265] Updated for consistant delimiter presence with source --- Oqtane.Server/Controllers/FileController.cs | 6 +++--- Oqtane.Server/Controllers/FolderController.cs | 4 ++-- Oqtane.Server/Controllers/ModuleDefinitionController.cs | 4 ++-- Oqtane.Server/Controllers/UserController.cs | 6 ++++-- Oqtane.Server/Infrastructure/DatabaseManager.cs | 5 +++-- Oqtane.Server/Infrastructure/InstallationManager.cs | 3 ++- .../Infrastructure/SiteTemplates/DefaultSiteTemplate.cs | 2 +- 7 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 5b22b062..5e57e3e5 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -431,12 +431,12 @@ namespace Oqtane.Controllers private string GetFolderPath(Folder folder) { - return Path.Combine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path, " ").TrimEnd(' '); + return Path.Combine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path); } private string GetFolderPath(string folder) { - return Path.Combine(_environment.WebRootPath, folder, " ").TrimEnd(' '); + return Path.Combine(_environment.WebRootPath, folder); } private void CreateDirectory(string folderpath) @@ -448,7 +448,7 @@ namespace Oqtane.Controllers string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries); foreach (string folder in folders) { - path = Path.Combine(path, folder); + path = Path.Combine(path, folder," ").TrimEnd(' '); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index a1e48fc1..b74fcdde 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -110,7 +110,7 @@ namespace Oqtane.Controllers if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { Folder parent = _folders.GetFolder(folder.ParentId.Value); - folder.Path = Path.Combine(parent.Path, folder.Name); + folder.Path = Path.Combine(parent.Path, folder.Name," ").TrimEnd(' '); } folder = _folders.AddFolder(folder); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder); @@ -135,7 +135,7 @@ namespace Oqtane.Controllers if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { Folder parent = _folders.GetFolder(folder.ParentId.Value); - folder.Path = Path.Combine(parent.Path, folder.Name); + folder.Path = Path.Combine(parent.Path, folder.Name, " ").TrimEnd(' '); } folder = _folders.UpdateFolder(folder); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder); diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 03e1937f..ce6a7001 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -193,13 +193,13 @@ namespace Oqtane.Controllers if (moduleDefinition.Template == "internal") { - rootPath = rootFolder.FullName; + rootPath = Path.Combine(rootFolder.FullName," ").TrimEnd(' '); moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, Oqtane.Client"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server"; } else { - rootPath = Path.Combine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module"); + rootPath = Path.Combine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module", " ").TrimEnd(' '); moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Client"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Server"; } diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index dfcb5b48..fe1da778 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -13,6 +13,7 @@ using System.Net; using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Repository; +using System.IO; namespace Oqtane.Controllers { @@ -150,7 +151,8 @@ namespace Oqtane.Controllers } // add folder for user - Folder folder = _folders.GetFolder(user.SiteId, "Users"); + string usersPath = Path.Combine("Users"," ").TrimEnd(' '); + Folder folder = _folders.GetFolder(user.SiteId, usersPath); if (folder != null) { _folders.AddFolder(new Folder @@ -158,7 +160,7 @@ namespace Oqtane.Controllers SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", - Path = System.IO.Path.Combine(folder.Path, newUser.UserId.ToString()), + Path = Path.Combine(folder.Path, newUser.UserId.ToString(), " ").TrimEnd(' '), Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 40f30092..a51c0238 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -387,14 +387,15 @@ namespace Oqtane.Infrastructure } // add folder for user - var folder = folderRepository.GetFolder(user.SiteId, "Users"); + string usersPath = Path.Combine("Users", " ").TrimEnd(' '); + var folder = folderRepository.GetFolder(user.SiteId, usersPath); if (folder != null) folderRepository.AddFolder(new Folder { SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", - Path = Path.Combine(folder.Path, newUser.UserId.ToString()), + Path = Path.Combine(folder.Path, newUser.UserId.ToString(), " ").TrimEnd(' '), Order = 1, IsSystem = true, Permissions = new List diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index f37d719e..21121689 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -103,7 +103,8 @@ namespace Oqtane.Infrastructure case ".svg": case ".js": case ".css": - filename = Path.Combine(sourceFolder, entry.FullName.Replace("wwwroot", name)); + string entryPath = Path.Combine(entry.FullName.Replace("wwwroot", name).Split("/")); + filename = Path.Combine(sourceFolder, entryPath); if (!Directory.Exists(Path.GetDirectoryName(filename))) { Directory.CreateDirectory(Path.GetDirectoryName(filename)); diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index 150373a2..26dfaed0 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -134,7 +134,7 @@ namespace Oqtane.SiteTemplates if (System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "images", "logo.png"))) { - string folderpath = Path.Combine(_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(), "Sites", site.SiteId.ToString()); + string folderpath = Path.Combine(_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(), "Sites", site.SiteId.ToString()," ").TrimEnd(); System.IO.Directory.CreateDirectory(folderpath); if (!System.IO.File.Exists(Path.Combine(folderpath, "logo.png"))) { From c07ebdd41b691afd942bb5c731147394412653e3 Mon Sep 17 00:00:00 2001 From: Sean Long Date: Sat, 18 Apr 2020 12:54:12 -0400 Subject: [PATCH 158/265] Revert "Updated for consistant delimiter presence with source" This reverts commit ce118096b7df9e133b9011a411e86eba21502be6. --- Oqtane.Server/Controllers/FileController.cs | 6 +++--- Oqtane.Server/Controllers/FolderController.cs | 4 ++-- Oqtane.Server/Controllers/ModuleDefinitionController.cs | 4 ++-- Oqtane.Server/Controllers/UserController.cs | 6 ++---- Oqtane.Server/Infrastructure/DatabaseManager.cs | 5 ++--- Oqtane.Server/Infrastructure/InstallationManager.cs | 3 +-- .../Infrastructure/SiteTemplates/DefaultSiteTemplate.cs | 2 +- 7 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 5e57e3e5..5b22b062 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -431,12 +431,12 @@ namespace Oqtane.Controllers private string GetFolderPath(Folder folder) { - return Path.Combine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path); + return Path.Combine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path, " ").TrimEnd(' '); } private string GetFolderPath(string folder) { - return Path.Combine(_environment.WebRootPath, folder); + return Path.Combine(_environment.WebRootPath, folder, " ").TrimEnd(' '); } private void CreateDirectory(string folderpath) @@ -448,7 +448,7 @@ namespace Oqtane.Controllers string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries); foreach (string folder in folders) { - path = Path.Combine(path, folder," ").TrimEnd(' '); + path = Path.Combine(path, folder); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index b74fcdde..a1e48fc1 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -110,7 +110,7 @@ namespace Oqtane.Controllers if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { Folder parent = _folders.GetFolder(folder.ParentId.Value); - folder.Path = Path.Combine(parent.Path, folder.Name," ").TrimEnd(' '); + folder.Path = Path.Combine(parent.Path, folder.Name); } folder = _folders.AddFolder(folder); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder); @@ -135,7 +135,7 @@ namespace Oqtane.Controllers if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { Folder parent = _folders.GetFolder(folder.ParentId.Value); - folder.Path = Path.Combine(parent.Path, folder.Name, " ").TrimEnd(' '); + folder.Path = Path.Combine(parent.Path, folder.Name); } folder = _folders.UpdateFolder(folder); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder); diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index ce6a7001..03e1937f 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -193,13 +193,13 @@ namespace Oqtane.Controllers if (moduleDefinition.Template == "internal") { - rootPath = Path.Combine(rootFolder.FullName," ").TrimEnd(' '); + rootPath = rootFolder.FullName; moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, Oqtane.Client"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server"; } else { - rootPath = Path.Combine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module", " ").TrimEnd(' '); + rootPath = Path.Combine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module"); moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Client"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Server"; } diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index fe1da778..dfcb5b48 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -13,7 +13,6 @@ using System.Net; using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Repository; -using System.IO; namespace Oqtane.Controllers { @@ -151,8 +150,7 @@ namespace Oqtane.Controllers } // add folder for user - string usersPath = Path.Combine("Users"," ").TrimEnd(' '); - Folder folder = _folders.GetFolder(user.SiteId, usersPath); + Folder folder = _folders.GetFolder(user.SiteId, "Users"); if (folder != null) { _folders.AddFolder(new Folder @@ -160,7 +158,7 @@ namespace Oqtane.Controllers SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", - Path = Path.Combine(folder.Path, newUser.UserId.ToString(), " ").TrimEnd(' '), + Path = System.IO.Path.Combine(folder.Path, newUser.UserId.ToString()), Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index a51c0238..40f30092 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -387,15 +387,14 @@ namespace Oqtane.Infrastructure } // add folder for user - string usersPath = Path.Combine("Users", " ").TrimEnd(' '); - var folder = folderRepository.GetFolder(user.SiteId, usersPath); + var folder = folderRepository.GetFolder(user.SiteId, "Users"); if (folder != null) folderRepository.AddFolder(new Folder { SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", - Path = Path.Combine(folder.Path, newUser.UserId.ToString(), " ").TrimEnd(' '), + Path = Path.Combine(folder.Path, newUser.UserId.ToString()), Order = 1, IsSystem = true, Permissions = new List diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index 21121689..f37d719e 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -103,8 +103,7 @@ namespace Oqtane.Infrastructure case ".svg": case ".js": case ".css": - string entryPath = Path.Combine(entry.FullName.Replace("wwwroot", name).Split("/")); - filename = Path.Combine(sourceFolder, entryPath); + filename = Path.Combine(sourceFolder, entry.FullName.Replace("wwwroot", name)); if (!Directory.Exists(Path.GetDirectoryName(filename))) { Directory.CreateDirectory(Path.GetDirectoryName(filename)); diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index 26dfaed0..150373a2 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -134,7 +134,7 @@ namespace Oqtane.SiteTemplates if (System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "images", "logo.png"))) { - string folderpath = Path.Combine(_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(), "Sites", site.SiteId.ToString()," ").TrimEnd(); + string folderpath = Path.Combine(_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(), "Sites", site.SiteId.ToString()); System.IO.Directory.CreateDirectory(folderpath); if (!System.IO.File.Exists(Path.Combine(folderpath, "logo.png"))) { From f0043f53eefac457f15c5b7a2d7622d993a2bae5 Mon Sep 17 00:00:00 2001 From: Sean Long Date: Sat, 18 Apr 2020 14:57:31 -0400 Subject: [PATCH 159/265] OS independent file paths & Utility.PathCombine added System.IO.Path.Combine provides cross-platform support for system paths, however rooted paths discarding of earlier segments Utilities.PathCombine ensures if any parameters start with root chacters does not discard previous Utilities.PathCombine allows appending of "\\" to translate to the correct cross-platform result --- Oqtane.Client/Services/FileService.cs | 2 +- Oqtane.Client/Services/FolderService.cs | 2 +- Oqtane.Server/Controllers/FileController.cs | 6 +++--- Oqtane.Server/Controllers/FolderController.cs | 4 ++-- .../Controllers/ModuleDefinitionController.cs | 8 ++++---- Oqtane.Server/Controllers/UserController.cs | 4 ++-- Oqtane.Server/Infrastructure/DatabaseManager.cs | 4 ++-- .../Infrastructure/InstallationManager.cs | 3 ++- .../SiteTemplates/DefaultSiteTemplate.cs | 2 +- Oqtane.Server/Repository/SiteRepository.cs | 3 ++- Oqtane.Shared/Shared/Utilities.cs | 17 +++++++++++++++++ 11 files changed, 37 insertions(+), 18 deletions(-) diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index c073a460..871a123f 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -44,7 +44,7 @@ namespace Oqtane.Services { if (!(folderPath.EndsWith(System.IO.Path.DirectorySeparatorChar) || folderPath.EndsWith(System.IO.Path.AltDirectorySeparatorChar))) { - folderPath = System.IO.Path.Combine(folderPath, " ").TrimEnd(' '); + folderPath = Utilities.PathCombine(folderPath,"\\"); } var path = WebUtility.UrlEncode(folderPath); diff --git a/Oqtane.Client/Services/FolderService.cs b/Oqtane.Client/Services/FolderService.cs index 6c3fd69d..0446b285 100644 --- a/Oqtane.Client/Services/FolderService.cs +++ b/Oqtane.Client/Services/FolderService.cs @@ -40,7 +40,7 @@ namespace Oqtane.Services { if (!(folderPath.EndsWith(System.IO.Path.DirectorySeparatorChar) || folderPath.EndsWith(System.IO.Path.AltDirectorySeparatorChar))) { - folderPath = System.IO.Path.Combine(folderPath, " ").TrimEnd(' '); + folderPath = Utilities.PathCombine(folderPath, "\\"); } var path = WebUtility.UrlEncode(folderPath); diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 5b22b062..d6e94644 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -431,12 +431,12 @@ namespace Oqtane.Controllers private string GetFolderPath(Folder folder) { - return Path.Combine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path, " ").TrimEnd(' '); + return Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path); } private string GetFolderPath(string folder) { - return Path.Combine(_environment.WebRootPath, folder, " ").TrimEnd(' '); + return Utilities.PathCombine(_environment.WebRootPath, folder); } private void CreateDirectory(string folderpath) @@ -448,7 +448,7 @@ namespace Oqtane.Controllers string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries); foreach (string folder in folders) { - path = Path.Combine(path, folder); + path = Utilities.PathCombine(path, folder,"\\"); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index a1e48fc1..55c19039 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -110,7 +110,7 @@ namespace Oqtane.Controllers if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { Folder parent = _folders.GetFolder(folder.ParentId.Value); - folder.Path = Path.Combine(parent.Path, folder.Name); + folder.Path = Utilities.PathCombine(parent.Path, folder.Name,"\\"); } folder = _folders.AddFolder(folder); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder); @@ -135,7 +135,7 @@ namespace Oqtane.Controllers if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { Folder parent = _folders.GetFolder(folder.ParentId.Value); - folder.Path = Path.Combine(parent.Path, folder.Name); + folder.Path = Utilities.PathCombine(parent.Path, folder.Name,"\\"); } folder = _folders.UpdateFolder(folder); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder); diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 03e1937f..4535b6f7 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -189,17 +189,17 @@ namespace Oqtane.Controllers { string rootPath; DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath); - string templatePath = Path.Combine(rootFolder.FullName, "Oqtane.Client", "Modules", "Admin", "ModuleCreator", "Templates",moduleDefinition.Template," ").TrimEnd(' '); + string templatePath = Utilities.PathCombine(rootFolder.FullName, "Oqtane.Client", "Modules", "Admin", "ModuleCreator", "Templates",moduleDefinition.Template,"\\"); if (moduleDefinition.Template == "internal") { - rootPath = rootFolder.FullName; + rootPath = Utilities.PathCombine(rootFolder.FullName,"\\"); moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, Oqtane.Client"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server"; } else { - rootPath = Path.Combine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module"); + rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module","\\"); moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Client"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Server"; } @@ -218,7 +218,7 @@ namespace Oqtane.Controllers private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string rootFolder, string templatePath, ModuleDefinition moduleDefinition) { // process folder - string folderPath = Path.Combine(rootPath, current.FullName.Replace(templatePath, "")); + string folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, "")); folderPath = folderPath.Replace("[Owner]", moduleDefinition.Owner); folderPath = folderPath.Replace("[Module]", moduleDefinition.Name); if (!Directory.Exists(folderPath)) diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index dfcb5b48..bfba1459 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -150,7 +150,7 @@ namespace Oqtane.Controllers } // add folder for user - Folder folder = _folders.GetFolder(user.SiteId, "Users"); + Folder folder = _folders.GetFolder(user.SiteId, Utilities.PathCombine("Users","\\")); if (folder != null) { _folders.AddFolder(new Folder @@ -158,7 +158,7 @@ namespace Oqtane.Controllers SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", - Path = System.IO.Path.Combine(folder.Path, newUser.UserId.ToString()), + Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),"\\"), Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 838dd582..5ca2eaa2 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -387,14 +387,14 @@ namespace Oqtane.Infrastructure } // add folder for user - var folder = folderRepository.GetFolder(user.SiteId, "Users"); + var folder = folderRepository.GetFolder(user.SiteId, Utilities.PathCombine("Users","\\")); if (folder != null) folderRepository.AddFolder(new Folder { SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", - Path = Path.Combine(folder.Path, newUser.UserId.ToString()), + Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),"\\"), Order = 1, IsSystem = true, Permissions = new List diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index f37d719e..4cfbaa6b 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -103,7 +103,8 @@ namespace Oqtane.Infrastructure case ".svg": case ".js": case ".css": - filename = Path.Combine(sourceFolder, entry.FullName.Replace("wwwroot", name)); + string entryPath = Utilities.PathCombine(entry.FullName.Replace("wwwroot", name).Split('/')); + filename = Path.Combine(sourceFolder, entryPath); if (!Directory.Exists(Path.GetDirectoryName(filename))) { Directory.CreateDirectory(Path.GetDirectoryName(filename)); diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index 150373a2..91f4bc39 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -134,7 +134,7 @@ namespace Oqtane.SiteTemplates if (System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "images", "logo.png"))) { - string folderpath = Path.Combine(_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(), "Sites", site.SiteId.ToString()); + string folderpath = Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(), "Sites", site.SiteId.ToString(),"\\"); System.IO.Directory.CreateDirectory(folderpath); if (!System.IO.File.Exists(Path.Combine(folderpath, "logo.png"))) { diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 2d5db9e7..4405536a 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using Microsoft.EntityFrameworkCore; @@ -583,7 +584,7 @@ namespace Oqtane.Repository }); _folderRepository.AddFolder(new Folder { - SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = "Users", Order = 1, IsSystem = true, + SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = Utilities.PathCombine("Users","\\"), Order = 1, IsSystem = true, Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 662d524d..0f7d603d 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.IO; using System.Text; using System.Text.RegularExpressions; @@ -236,5 +237,21 @@ namespace Oqtane.Shared @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250)); } + + public static string PathCombine(params string[] segments) + { + var separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + + for (int i =1;i < segments.Length; i++){ + if(Path.IsPathRooted(segments[i])){ + segments[i] = segments[i].TrimStart(separators); + if(String.IsNullOrEmpty(segments[i])){ + segments[i]=" "; + } + } + } + + return Path.Combine(segments).TrimEnd(); + } } } From 72995cd8faea33c80ca4108f77ba58f22e948298 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 20 Apr 2020 18:05:37 -0400 Subject: [PATCH 160/265] added system info admin page/module, improved UI for framework, module, and theme install/upgrade, added version to ModuleDefinitions, fixed bug in logging logic introduced during code standardization --- .../Modules/Admin/ModuleDefinitions/Add.razor | 81 ++++++------ .../Modules/Admin/SystemInfo/Index.razor | 42 +++++-- Oqtane.Client/Modules/Admin/Themes/Add.razor | 116 +++++++++++------- .../Modules/Admin/Upgrade/Index.razor | 61 +++++---- Oqtane.Client/Program.cs | 1 + .../Services/Interfaces/ISystemService.cs | 10 ++ Oqtane.Client/Services/SystemService.cs | 32 +++++ Oqtane.Server/Controllers/SystemController.cs | 35 ++++++ Oqtane.Server/Infrastructure/LogManager.cs | 2 +- .../Repository/ModuleDefinitionRepository.cs | 12 +- Oqtane.Server/Repository/SiteRepository.cs | 28 +++++ Oqtane.Server/Scripts/Master.00.00.00.sql | 1 + Oqtane.Server/Startup.cs | 1 + Oqtane.Shared/Models/ModuleDefinition.cs | 3 +- 14 files changed, 303 insertions(+), 122 deletions(-) create mode 100644 Oqtane.Client/Services/Interfaces/ISystemService.cs create mode 100644 Oqtane.Client/Services/SystemService.cs create mode 100644 Oqtane.Server/Controllers/SystemController.cs diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor index 49e70702..a63784bc 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Add.razor @@ -5,42 +5,47 @@ @inject IModuleDefinitionService ModuleDefinitionService @inject IPackageService PackageService -
    - + diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor index 6cf41e3f..e6ae6b68 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor @@ -9,10 +9,10 @@ + + + + + + + + + + + + + + + + + +
    - + - @if (userroles != null) { @@ -26,18 +26,18 @@
    - + - +
    - + - +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + + + + + +
    + +
    +
    + Cancel -
    -
    +

    @code { private int _moduleDefinitionId; private string _name; + private string _version; + private string _categories; + private string _moduledefinitionname = ""; + private string _description = ""; + private string _owner = ""; + private string _url = ""; + private string _contact = ""; + private string _license = ""; private string _permissions; private string _createdby; private DateTime _createdon; @@ -49,6 +131,14 @@ if (moduleDefinition != null) { _name = moduleDefinition.Name; + _version = moduleDefinition.Version; + _categories = moduleDefinition.Categories; + _moduledefinitionname = moduleDefinition.ModuleDefinitionName; + _description = moduleDefinition.Description; + _owner = moduleDefinition.Owner; + _url = moduleDefinition.Url; + _contact = moduleDefinition.Contact; + _license = moduleDefinition.License; _permissions = moduleDefinition.Permissions; _createdby = moduleDefinition.CreatedBy; _createdon = moduleDefinition.CreatedOn; diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index 44a55284..c8a90d23 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -5,90 +5,73 @@ @inject IModuleService ModuleService @inject IPageModuleService PageModuleService -@if (_containers != null) -{ -
    -
    - - - -
    -
    -
    - - - - - - - - - - - - - - - - - - -
    - - - -
    - - - -
    - - -
    - - - -
    -
    - @if (_settingsModuleType != null) - { -
    -
    - @DynamicComponent -
    - } -
    -
    -
    - - - Cancel -} + + + @if (_containers != null) + { + + + + + + + + + + + + + +
    + + + +
    + + + +
    + + + +
    + } +
    + + @if (_containers != null) + { + + + + +
    + +
    + } +
    + @if (_settingsModuleType != null) + { + + @DynamicComponent + + } +
    + +Cancel @code { private Dictionary _containers; diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 2b495fb9..7c53755a 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -4,164 +4,189 @@ @inject IPageService PageService @inject IThemeService ThemeService -@if (_themeList != null) -{ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + +
    - - - -
    - - - -
    - - - -
    - - - -
    - - - - @if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">")) - { - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    + + + +
    + + + +
    + + + + @if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">")) { - + } - - } -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - -
    - - Cancel -} +
    + + + +
    + + + +
    + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    +
    + } + + + + + + +
    + +
    +
    + + +Cancel @code { - private Dictionary _themes = new Dictionary(); - private Dictionary _panelayouts = new Dictionary(); + private Dictionary _themes; + private Dictionary _panelayouts; private List _themeList; private List _pageList; private string _name; @@ -191,12 +216,12 @@ _pageList = PageState.Pages; _children = PageState.Pages.Where(item => item.ParentId == null).ToList(); - _themes = ThemeService.GetThemeTypes(_themeList); _themetype = PageState.Site.DefaultThemeType; - - _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); _layouttype = PageState.Site.DefaultLayoutType; + _themes = ThemeService.GetThemeTypes(_themeList); + _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); + _permissions = string.Empty; } catch (Exception ex) @@ -211,13 +236,26 @@ try { _parentid = (string)e.Value; + _children = new List(); if (_parentid == "-1") { - _children = PageState.Pages.Where(item => item.ParentId == null).ToList(); + foreach (Page p in PageState.Pages.Where(item => item.ParentId == null)) + { + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) + { + _children.Add(p); + } + } } else { - _children = PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)).ToList(); + 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(); } @@ -241,7 +279,6 @@ { _panelayouts = new Dictionary(); } - StateHasChanged(); } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 6ac773a0..056cc9bf 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -4,185 +4,202 @@ @inject IPageService PageService @inject IThemeService ThemeService -@if (_themeList != null) -{ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + +
    - - - -
    - - - -
    - - - -
    - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    + + + +
    + + + +
    + + + + @if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">")) { - + } - else - { - - } - } - -
    - - - - @if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">")) - { - - } -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - -
    - - Cancel -
    -
    - -} +
    + + + +
    + + + +
    + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    +
    +

    + + } + + + + + + +
    + +
    +
    + + +Cancel @code { - private Dictionary _themes = new Dictionary(); - private Dictionary _panelayouts = new Dictionary(); + private Dictionary _themes; + private Dictionary _panelayouts; private List _themeList; private List _pageList; private int _pageId; @@ -277,13 +294,26 @@ try { _parentid = (string)e.Value; + _children = new List(); if (_parentid == "-1") { - _children = PageState.Pages.Where(item => item.ParentId == null).ToList(); + foreach(Page p in PageState.Pages.Where(item => item.ParentId == null)) + { + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) + { + _children.Add(p); + } + } } else { - _children = PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)).ToList(); + 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) { diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index 6711617a..0758e80d 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -6,170 +6,161 @@ @inject ISettingService SettingService @inject INotificationService NotificationService -@if (PageState.User != null && profiles != null) -{ -
    -
    - - - -
    -
    - @if (photofileid != -1) - { - @displayname - } - else - { -
    - } - - - - - - - - - - - - - - - - - - - - - - - - - - - @foreach (Profile profile in profiles) - { - var p = profile; - if (p.Category != category) - { - - - - category = p.Category; - } - - - - - } -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - @p.Category -
    - - - -
    - - -
    - -
    + + + @if (PageState.User != null) + { + @if (photofileid != -1) + { + @displayname + } + else + {
    - -

    - @if (filter == "to") + } + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + } +
    + + @if (profiles != null) + { + + @foreach (Profile profile in profiles) { - -
    -
    - - - - - - - - - - - - - - - - - + var p = profile; + if (p.Category != category) + { + + + + category = p.Category; + } + + + + } - else - { - -
    -
    - - - - - - - - - - - - - - - - - - } -

    - - - - - -} +
      FromSubjectReceived@(context.FromUser == null ? "System" : context.FromUser.DisplayName)@context.Subject@context.CreatedOn@(context.Body.Length > 100 ? context.Body.Substring(0,100) : context.Body)
    + @p.Category +
    + + + +
      ToSubjectSent@(context.ToUser == null ? context.ToEmail : context.ToUser.DisplayName)@context.Subject@context.CreatedOn@(context.Body.Length > 100 ? context.Body.Substring(0,100) : context.Body)
    + + + } +
    + + @if (notifications != null) + { + +

    + @if (filter == "to") + { + +
    +
      FromSubjectReceived@(context.FromUser == null ? "System" : context.FromUser.DisplayName)@context.Subject@context.CreatedOn@(context.Body.Length > 100 ? context.Body.Substring(0, 100) : context.Body)  ToSubjectSent@(context.ToUser == null ? context.ToEmail : context.ToUser.DisplayName)@context.Subject@context.CreatedOn@(context.Body.Length > 100 ? context.Body.Substring(0, 100) : context.Body)Role@permission.PermissionName @EntityName@permission.PermissionName
    User@permission.PermissionName @EntityName@permission.PermissionName
    - + - +
    - + - +
    - - - - -
    - - - -
    - @if (_packages != null) { -
    -

    Available Modules

    + + @if (_packages.Count > 0) + { + + + +
    +
    NameVersion@context.Name@context.Version + +
    + + + + +
    + + + +
    + + - -
    -
    NameVersion@context.Name@context.Version - -
    + + + + @@ -31,7 +40,7 @@ @@ -39,7 +48,7 @@
    - + - @_version +
    - + - @_runtime +
    - + - @_netcore + +
    + + +
    - @_serverpath +
    - @_servertime +
    @@ -49,16 +58,23 @@ private string _version = string.Empty; private string _runtime = string.Empty; - private string _netcore = string.Empty; + private string _clrversion = string.Empty; + private string _osversion = string.Empty; private string _serverpath = string.Empty; private string _servertime = string.Empty; - protected override void OnInitialized() + protected override async Task OnInitializedAsync() { _version = Constants.Version; _runtime = PageState.Runtime.ToString(); - _netcore = string.Empty; - _serverpath = string.Empty; - _servertime = string.Empty; + + Dictionary systeminfo = await SystemService.GetSystemInfoAsync(); + if (systeminfo != null) + { + _clrversion = systeminfo["clrversion"]; + _osversion = systeminfo["osversion"]; + _serverpath = systeminfo["serverpath"]; + _servertime = systeminfo["servertime"]; + } } } diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor index dcf822f9..5ad4f5c6 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Add.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor @@ -5,70 +5,100 @@ @inject IThemeService ThemeService @inject IPackageService PackageService - - - - - -
    - - - -
    - -@if (packages != null) +@if (_packages != null) { -
    -

    Available Themes

    - - -
    -
    NameVersion@context.Name@context.Version - - NameVersion@context.Name@context.Version + +
    + + + + +
    + + + +
    + + Cancel +} @code { - private List packages; + private List _packages; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnInitializedAsync() { - var themes = await ThemeService.GetThemesAsync(); - packages = await PackageService.GetPackagesAsync("theme"); - - foreach(Package package in packages.ToArray()) + try { - if (themes.Exists(item => Utilities.GetTypeName(item.ThemeName) == package.PackageId)) + var themes = await ThemeService.GetThemesAsync(); + _packages = await PackageService.GetPackagesAsync("theme"); + + foreach (Package package in _packages.ToArray()) { - packages.Remove(package); + if (themes.Exists(item => Utilities.GetTypeName(item.ThemeName) == package.PackageId)) + { + _packages.Remove(package); + } } } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Packages {Error}", ex.Message); + AddModuleMessage("Error Loading Packages", MessageType.Error); + } } private async Task InstallThemes() { - await ThemeService.InstallThemesAsync(); - NavigationManager.NavigateTo(NavigateUrl()); + try + { + await ThemeService.InstallThemesAsync(); + NavigationManager.NavigateTo(NavigateUrl()); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Installating Theme"); + } } private async Task DownloadTheme(string packageid, string version) { - await PackageService.DownloadPackageAsync(packageid, version, "Themes"); - AddModuleMessage("Theme Downloaded Successfully. Click Install To Complete Installation.", MessageType.Success); - StateHasChanged(); + try + { + await PackageService.DownloadPackageAsync(packageid, version, "Themes"); + await logger.LogInformation("Theme {ThemeName} {Version} Downloaded Successfully", packageid, version); + AddModuleMessage("Themes Downloaded Successfully. Click Install To Complete Installation.", MessageType.Success); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Downloading Module {ThemeName} {Version}", packageid, version); + AddModuleMessage("Error Downloading Theme", MessageType.Error); + } + } } -} diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index e70ab017..2aa2ce86 100644 --- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor +++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor @@ -5,42 +5,55 @@ @inject IPackageService PackageService @inject IInstallationService InstallationService - - - - - -
    - - - -
    - - -@if (upgradeavailable) +@if (_package != null) { -
    -

    Upgrade Available

    - - + + + @if (_upgradeavailable) + { + + @("Framework") @_package.Version + } + else + { + + } + + @if (_upgradeavailable) + { + + + + + + +
    + + + +
    +
    + } +
    } @code { - private bool upgradeavailable = false; + private Package _package; + private bool _upgradeavailable = false; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnInitializedAsync() { - var packages = await PackageService.GetPackagesAsync("framework"); - var package = packages.FirstOrDefault(); - if (package != null) + List packages = await PackageService.GetPackagesAsync("framework"); + _package = packages.FirstOrDefault(); + if (_package != null) { - upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) > 0); + _upgradeavailable = (Version.Parse(_package.Version).CompareTo(Version.Parse(Constants.Version)) > 0); } - if (!upgradeavailable) + else { - AddModuleMessage("Framework Is Up To Date", MessageType.Info); + _package = new Package { Name = Constants.PackageId, Version = Constants.Version }; } } diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index 9e4e1261..8c9b118a 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -52,6 +52,7 @@ namespace Oqtane.Client builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); // dynamically register module contexts and repository services Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); diff --git a/Oqtane.Client/Services/Interfaces/ISystemService.cs b/Oqtane.Client/Services/Interfaces/ISystemService.cs new file mode 100644 index 00000000..c450cdc7 --- /dev/null +++ b/Oqtane.Client/Services/Interfaces/ISystemService.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Oqtane.Services +{ + public interface ISystemService + { + Task> GetSystemInfoAsync(); + } +} diff --git a/Oqtane.Client/Services/SystemService.cs b/Oqtane.Client/Services/SystemService.cs new file mode 100644 index 00000000..0823a79c --- /dev/null +++ b/Oqtane.Client/Services/SystemService.cs @@ -0,0 +1,32 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using Oqtane.Shared; +using System.Collections.Generic; + +namespace Oqtane.Services +{ + public class SystemService : ServiceBase, ISystemService + { + + private readonly SiteState _siteState; + private readonly NavigationManager _navigationManager; + + public SystemService(HttpClient http, SiteState siteState, NavigationManager navigationManager) : base(http) + { + + _siteState = siteState; + _navigationManager = navigationManager; + } + + private string Apiurl + { + get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "System"); } + } + + public async Task> GetSystemInfoAsync() + { + return await GetJsonAsync>(Apiurl); + } + } +} diff --git a/Oqtane.Server/Controllers/SystemController.cs b/Oqtane.Server/Controllers/SystemController.cs new file mode 100644 index 00000000..bfd8d97a --- /dev/null +++ b/Oqtane.Server/Controllers/SystemController.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using System.Collections.Generic; +using Oqtane.Shared; +using System; +using Microsoft.AspNetCore.Hosting; + +namespace Oqtane.Controllers +{ + [Route("{site}/api/[controller]")] + public class SystemController : Controller + { + private readonly IWebHostEnvironment _environment; + + public SystemController(IWebHostEnvironment environment) + { + _environment = environment; + } + + // GET: api/ + [HttpGet] + [Authorize(Roles = Constants.HostRole)] + public Dictionary Get() + { + Dictionary systeminfo = new Dictionary(); + systeminfo.Add("clrversion", Environment.Version.ToString()); + systeminfo.Add("osversion", Environment.OSVersion.ToString()); + systeminfo.Add("machinename", Environment.MachineName); + systeminfo.Add("serverpath", _environment.ContentRootPath); + systeminfo.Add("servertime", DateTime.Now.ToString()); + return systeminfo; + } + + } +} diff --git a/Oqtane.Server/Infrastructure/LogManager.cs b/Oqtane.Server/Infrastructure/LogManager.cs index a213cb10..b46bc8dc 100644 --- a/Oqtane.Server/Infrastructure/LogManager.cs +++ b/Oqtane.Server/Infrastructure/LogManager.cs @@ -75,7 +75,7 @@ namespace Oqtane.Infrastructure log.Url = $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}"; } - Type type = @class.GetType(); + Type type = Type.GetType(@class.ToString()); if (type != null) { log.Category = type.AssemblyQualifiedName; diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index bc91823a..4803ba9e 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -105,6 +105,10 @@ namespace Oqtane.Repository { moduledefinition.Categories = moduledef.Categories; } + if (!string.IsNullOrEmpty(moduledef.Version)) + { + moduledefinition.Version = moduledef.Version; + } if (permissions.Count == 0) { _permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions); @@ -173,23 +177,29 @@ namespace Oqtane.Repository .FirstOrDefault(item => item.GetInterfaces().Contains(typeof(IModule))); if (moduletype != null) { + // get property values from IModule var moduleobject = Activator.CreateInstance(moduletype); moduledefinition = (ModuleDefinition)moduletype.GetProperty("ModuleDefinition").GetValue(moduleobject); } else { + // set default property values moduledefinition = new ModuleDefinition { Name = moduleType.Substring(moduleType.LastIndexOf(".") + 1), Description = "Manage " + moduleType.Substring(moduleType.LastIndexOf(".") + 1), Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : ""), - Version = new Version(1, 0, 0).ToString() + Version = "1.0.0" }; } // set internal properties moduledefinition.ModuleDefinitionName = qualifiedModuleType; moduledefinition.ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1]; moduledefinition.AssemblyName = assembly.FullName.Split(",")[0]; + if (assembly.FullName.StartsWith("Oqtane.Client")) + { + moduledefinition.Version = Constants.Version; + } if (string.IsNullOrEmpty(moduledefinition.Categories)) { moduledefinition.Categories = "Common"; diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 4cba5e20..c726ccc7 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -388,6 +388,34 @@ namespace Oqtane.Repository } }); pageTemplates.Add(new PageTemplate + { + Name = "System Info", + Parent = "Admin", + Path = "admin/system", + Icon = "medical-cross", + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.SystemInfo.Index).ToModuleDefinitionName(), Title = "System Info", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate { Name = "Upgrade Service", Parent = "Admin", Path = "admin/upgrade", Icon = Icons.Aperture, IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = new List diff --git a/Oqtane.Server/Scripts/Master.00.00.00.sql b/Oqtane.Server/Scripts/Master.00.00.00.sql index 5dcf652e..0b10fb01 100644 --- a/Oqtane.Server/Scripts/Master.00.00.00.sql +++ b/Oqtane.Server/Scripts/Master.00.00.00.sql @@ -43,6 +43,7 @@ CREATE TABLE [dbo].[ModuleDefinition]( [Name] [nvarchar](200) NULL, [Description] [nvarchar](2000) NULL, [Categories] [nvarchar](200) NULL, + [Version] [nvarchar](50) NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 917632b6..52599d6e 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -102,6 +102,7 @@ namespace Oqtane services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddSingleton(); diff --git a/Oqtane.Shared/Models/ModuleDefinition.cs b/Oqtane.Shared/Models/ModuleDefinition.cs index 0bc66da1..82836f0c 100644 --- a/Oqtane.Shared/Models/ModuleDefinition.cs +++ b/Oqtane.Shared/Models/ModuleDefinition.cs @@ -27,6 +27,7 @@ namespace Oqtane.Models public string Name { get; set; } public string Description { get; set; } public string Categories { get; set; } + public string Version { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } @@ -36,8 +37,6 @@ namespace Oqtane.Models [NotMapped] public int SiteId { get; set; } [NotMapped] - public string Version { get; set; } - [NotMapped] public string Owner { get; set; } [NotMapped] public string Url { get; set; } From ed0cc42852491e0e93291a230e37c3f3499bc5d0 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Tue, 21 Apr 2020 19:02:06 +0300 Subject: [PATCH 161/265] Remove unnecessary modules --- Oqtane.Client/Modules/Counter/Index.razor | 16 ------- Oqtane.Client/Modules/Counter/ModuleInfo.cs | 14 ------- Oqtane.Client/Modules/Weather/Index.razor | 42 ------------------- .../Modules/Weather/Models/WeatherForecast.cs | 12 ------ Oqtane.Client/Modules/Weather/ModuleInfo.cs | 14 ------- .../Services/IWeatherForecastService.cs | 10 ----- .../Services/WeatherForecastService.cs | 26 ------------ 7 files changed, 134 deletions(-) delete mode 100644 Oqtane.Client/Modules/Counter/Index.razor delete mode 100644 Oqtane.Client/Modules/Counter/ModuleInfo.cs delete mode 100644 Oqtane.Client/Modules/Weather/Index.razor delete mode 100644 Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs delete mode 100644 Oqtane.Client/Modules/Weather/ModuleInfo.cs delete mode 100644 Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs delete mode 100644 Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs diff --git a/Oqtane.Client/Modules/Counter/Index.razor b/Oqtane.Client/Modules/Counter/Index.razor deleted file mode 100644 index 9be7d4bf..00000000 --- a/Oqtane.Client/Modules/Counter/Index.razor +++ /dev/null @@ -1,16 +0,0 @@ -@namespace Oqtane.Modules.Counter -@inherits ModuleBase -Current count: @currentCount -
    - -
    -
    - -@code { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } -} diff --git a/Oqtane.Client/Modules/Counter/ModuleInfo.cs b/Oqtane.Client/Modules/Counter/ModuleInfo.cs deleted file mode 100644 index 053017f9..00000000 --- a/Oqtane.Client/Modules/Counter/ModuleInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Oqtane.Models; - -namespace Oqtane.Modules.Counter -{ - public class ModuleInfo : IModule - { - public ModuleDefinition ModuleDefinition => new ModuleDefinition - { - Name = "Counter", - Description = "Increments a counter", - Version = "1.0.0" - }; - } -} diff --git a/Oqtane.Client/Modules/Weather/Index.razor b/Oqtane.Client/Modules/Weather/Index.razor deleted file mode 100644 index 2d20ff38..00000000 --- a/Oqtane.Client/Modules/Weather/Index.razor +++ /dev/null @@ -1,42 +0,0 @@ -@using Oqtane.Modules.Weather.Services -@namespace Oqtane.Modules.Weather -@inherits ModuleBase - -@if (forecasts == null) -{ -

    Loading...

    -} -else -{ - - - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
    DateTemp. (C)Temp. (F)Summary
    @forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
    -} - -@code { - private WeatherForecast[] forecasts; - - protected override async Task OnInitializedAsync() - { - WeatherForecastService forecastservice = new WeatherForecastService(); - forecasts = await forecastservice.GetForecastAsync(DateTime.UtcNow); - } -} \ No newline at end of file diff --git a/Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs b/Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs deleted file mode 100644 index 7e02ea8a..00000000 --- a/Oqtane.Client/Modules/Weather/Models/WeatherForecast.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Oqtane.Modules.Weather -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - public int TemperatureC { get; set; } - public int TemperatureF { get; set; } - public string Summary { get; set; } - } -} diff --git a/Oqtane.Client/Modules/Weather/ModuleInfo.cs b/Oqtane.Client/Modules/Weather/ModuleInfo.cs deleted file mode 100644 index 0588c563..00000000 --- a/Oqtane.Client/Modules/Weather/ModuleInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Oqtane.Models; - -namespace Oqtane.Modules.Weather -{ - public class ModuleInfo : IModule - { - public ModuleDefinition ModuleDefinition => new ModuleDefinition - { - Name = "Weather", - Description = "Displays random weather using a service", - Version = "1.0.0" - }; - } -} diff --git a/Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs b/Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs deleted file mode 100644 index 743848fb..00000000 --- a/Oqtane.Client/Modules/Weather/Services/IWeatherForecastService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Oqtane.Modules.Weather.Services -{ - public interface IWeatherForecastService - { - Task GetForecastAsync(DateTime startDate); - } -} diff --git a/Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs b/Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs deleted file mode 100644 index e6ad13d9..00000000 --- a/Oqtane.Client/Modules/Weather/Services/WeatherForecastService.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Oqtane.Modules; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace Oqtane.Modules.Weather.Services -{ - public class WeatherForecastService : IWeatherForecastService - { - private static string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - public Task GetForecastAsync(DateTime startDate) - { - var rng = new Random(); - return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = startDate.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }).ToArray()); - } - } -} From f83778fd70248d29e41bafb0e0eb926f9f413700 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Tue, 21 Apr 2020 19:56:24 +0300 Subject: [PATCH 162/265] Updated to Blazor WebAssembly 3.2 preview 4 --- Oqtane.Client/Oqtane.Client.csproj | 6 +++--- Oqtane.Server/Oqtane.Server.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index 68dad12d..acc4da87 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -27,10 +27,10 @@ - - + + - + diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index bbf00ff7..fd6d457d 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -26,7 +26,7 @@ - + From ab5257cea29d9b62f29860a704f6199e7f41f66c Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 21 Apr 2020 15:16:12 -0400 Subject: [PATCH 163/265] Security fixes for Site Administrators to ensure proper access. Improvements to User and Role management components. Fix logic in CreateUser so that it does not prevent Administrators from creating users. --- Oqtane.Client/Modules/Admin/Logs/Detail.razor | 2 +- Oqtane.Client/Modules/Admin/Logs/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Roles/Index.razor | 2 + Oqtane.Client/Modules/Admin/Roles/Users.razor | 201 ++++ .../Modules/Admin/UserProfile/Index.razor | 16 +- Oqtane.Client/Modules/Admin/Users/Add.razor | 141 +-- Oqtane.Client/Modules/Admin/Users/Edit.razor | 214 ++--- Oqtane.Client/Modules/Admin/Users/Roles.razor | 36 +- .../Themes/Controls/ControlPanel.razor | 2 +- Oqtane.Server/Controllers/SiteController.cs | 2 +- Oqtane.Server/Controllers/TenantController.cs | 4 +- Oqtane.Server/Controllers/UserController.cs | 8 +- .../Infrastructure/DatabaseManager.cs | 4 +- Oqtane.Server/Repository/SiteRepository.cs | 867 ++++++++++-------- Oqtane.Shared/Shared/SettingKeys.cs | 1 - 15 files changed, 910 insertions(+), 592 deletions(-) create mode 100644 Oqtane.Client/Modules/Admin/Roles/Users.razor diff --git a/Oqtane.Client/Modules/Admin/Logs/Detail.razor b/Oqtane.Client/Modules/Admin/Logs/Detail.razor index 02d4c804..fd484889 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Detail.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Detail.razor @@ -152,7 +152,7 @@ private string _properties = string.Empty; private string _server = string.Empty; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnInitializedAsync() { diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor index e87450bd..730c6d13 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor @@ -76,7 +76,7 @@ else private string _rows = "10"; private List _logs; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; protected override async Task OnInitializedAsync() { diff --git a/Oqtane.Client/Modules/Admin/Roles/Index.razor b/Oqtane.Client/Modules/Admin/Roles/Index.razor index 0acfa614..881a9348 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Index.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Index.razor @@ -12,6 +12,7 @@ else
    +
          Name @context.Name
    + + + + + + + + + + + + + + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + Cancel + +
    +

    + +

    +
    Users @context.User.DisplayName + +
    diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor index 561185f6..b6aa116a 100644 --- a/Oqtane.Client/Modules/Admin/Users/Add.razor +++ b/Oqtane.Client/Modules/Admin/Users/Add.razor @@ -5,75 +5,86 @@ @inject IProfileService ProfileService @inject ISettingService SettingService -@if (profiles != null) -{ - - - - - - - - - - - - - - - - - - - - - - - @foreach (Profile profile in profiles) + + + @if (profiles != null) { - var p = profile; - if (p.Category != category) - { +
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - + + - category = p.Category; - } - - - - + + + + + + + + + + + + + + + + +
    - @p.Category - + + + +
    - - - -
    + + + +
    + + + +
    + + + +
    + + + +
    } -
    - - Cancel -} + + + @if (profiles != null) + { + + @foreach (Profile profile in profiles) + { + var p = profile; + if (p.Category != category) + { + + + + category = p.Category; + } + + + + + } +
    + @p.Category +
    + + + +
    + } +
    + + + +Cancel @code { private string username = string.Empty; diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index 02fcf751..3aa30e1b 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -5,105 +5,115 @@ @inject IProfileService ProfileService @inject ISettingService SettingService -@if (profiles != null) +@if (PageState.User != null && photofileid != -1) { - @if (photofileid != -1) - { - @displayname - } - else - { -
    - } - - - - - - - - - - - - - - - - - - - - - - - - - - - @foreach (Profile profile in profiles) - { - var p = profile; - if (p.Category != category) - { - - - - category = p.Category; - } - - - - - } - - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - @p.Category -
    - - - -
    - - - -
    - - Cancel -
    -
    - + @displayname } +else +{ +
    +} + + + @if (profiles != null) + { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + } +
    + + @if (profiles != null) + { + + @foreach (Profile profile in profiles) + { + var p = profile; + if (p.Category != category) + { + + + + category = p.Category; + } + + + + + } +
    + @p.Category +
    + + + +
    + } +
    +
    + + +Cancel +

    + @code { private int userid; @@ -140,12 +150,12 @@ username = user.Username; email = user.Email; displayname = user.DisplayName; - + if (user.PhotoFileId != null) { photofileid = user.PhotoFileId.Value; } - + settings = await SettingService.GetUserSettingsAsync(user.UserId); createdby = user.CreatedBy; createdon = user.CreatedOn; @@ -170,7 +180,7 @@ { try { - if (username != string.Empty && password != string.Empty && confirm != string.Empty && email != string.Empty) + if (username != string.Empty && email != string.Empty) { if (password == confirm) { @@ -182,12 +192,12 @@ user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; user.PhotoFileId = null; photofileid = filemanager.GetFileId(); - + if (photofileid != -1) { user.PhotoFileId = photofileid; } - + user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted)); user = await UserService.UpdateUserAsync(user); diff --git a/Oqtane.Client/Modules/Admin/Users/Roles.razor b/Oqtane.Client/Modules/Admin/Users/Roles.razor index 82662442..a4e946df 100644 --- a/Oqtane.Client/Modules/Admin/Users/Roles.razor +++ b/Oqtane.Client/Modules/Admin/Users/Roles.razor @@ -1,6 +1,7 @@ @namespace Oqtane.Modules.Admin.Users @inherits ModuleBase @inject IRoleService RoleService +@inject IUserService UserService @inject IUserRoleService UserRoleService @if (userroles == null) @@ -12,7 +13,15 @@ else + + + + + @@ -66,6 +75,7 @@ else @code { private int userid; + private string name = string.Empty; private List roles; private int roleid = -1; private string effectivedate = string.Empty; @@ -79,6 +89,8 @@ else try { userid = Int32.Parse(PageState.QueryString["id"]); + User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); + name = user.DisplayName; roles = await RoleService.GetRolesAsync(PageState.Site.SiteId); await GetUserRoles(); } @@ -120,7 +132,7 @@ else { userrole.EffectiveDate = DateTime.Parse(effectivedate); } - + if (string.IsNullOrEmpty(expirydate)) { userrole.ExpiryDate = null; @@ -136,7 +148,7 @@ else userrole = new UserRole(); userrole.UserId = userid; userrole.RoleId = roleid; - + if (string.IsNullOrEmpty(effectivedate)) { userrole.EffectiveDate = null; @@ -145,7 +157,7 @@ else { userrole.EffectiveDate = DateTime.Parse(effectivedate); } - + if (string.IsNullOrEmpty(expirydate)) { userrole.ExpiryDate = null; @@ -154,10 +166,10 @@ else { userrole.ExpiryDate = DateTime.Parse(expirydate); } - + await UserRoleService.AddUserRoleAsync(userrole); } - + await GetUserRoles(); await logger.LogInformation("User Assigned To Role {UserRole}", userrole); AddModuleMessage("User Assigned To Role", MessageType.Success); diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index d6fe191d..fe9e5486 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -205,7 +205,7 @@ @if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions)) { } diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index 1adfe517..bd24c994 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -70,7 +70,7 @@ namespace Oqtane.Controllers // PUT api//5 [HttpPut("{id}")] - [Authorize(Roles = Constants.HostRole)] + [Authorize(Roles = Constants.AdminRole)] public Site Put(int id, [FromBody] Site site) { if (ModelState.IsValid) diff --git a/Oqtane.Server/Controllers/TenantController.cs b/Oqtane.Server/Controllers/TenantController.cs index 03e4b1f4..bfacbd1c 100644 --- a/Oqtane.Server/Controllers/TenantController.cs +++ b/Oqtane.Server/Controllers/TenantController.cs @@ -23,7 +23,7 @@ namespace Oqtane.Controllers // GET: api/ [HttpGet] - [Authorize(Roles = Constants.HostRole)] + [Authorize(Roles = Constants.AdminRole)] public IEnumerable Get() { return _tenants.GetTenants(); @@ -31,7 +31,7 @@ namespace Oqtane.Controllers // GET api//5 [HttpGet("{id}")] - [Authorize(Roles = Constants.HostRole)] + [Authorize(Roles = Constants.AdminRole)] public Tenant Get(int id) { return _tenants.GetTenant(id); diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index c0cec6a8..dec2c5e4 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -93,14 +93,14 @@ namespace Oqtane.Controllers bool verified; bool allowregistration; - if (user.Username == Constants.HostUser) + if (user.Username == Constants.HostUser || User.IsInRole(Constants.AdminRole)) { verified = true; allowregistration = true; } else - { - verified = User.IsInRole(Constants.AdminRole); // only users created by administrators are verified + { + verified = false; allowregistration = _sites.GetSite(user.SiteId).AllowRegistration; } @@ -308,7 +308,7 @@ namespace Oqtane.Controllers public async Task Logout([FromBody] User user) { await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme); - _logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout {Username}", user.Username); + _logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout {Username}", (user != null) ? user.Username : ""); } // POST api//verify diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index dabe8c78..63186155 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -348,10 +348,10 @@ namespace Oqtane.Infrastructure var user = new User { SiteId = site.SiteId, - Username = GetInstallationConfig(SettingKeys.HostUserKey, Constants.HostUser), + Username = Constants.HostUser, Password = password, Email = email, - DisplayName = GetInstallationConfig(SettingKeys.HostUserKey, Constants.HostUser), + DisplayName = Constants.HostUser }; CreateHostUser(folders, userRoles, roles, users, identityUserManager, user); tenant.IsInitialized = true; diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index c726ccc7..f85294df 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -51,395 +51,16 @@ namespace Oqtane.Repository { if (pageTemplates == null) pageTemplates = new List(); + // user pages pageTemplates.Add(new PageTemplate { - Name = "Admin", Parent = "", Path = "admin", Icon = "", IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Dashboard.Index).ToModuleDefinitionName(), Title = "Admin Dashboard", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Site Management", Parent = "Admin", Path = "admin/sites", Icon = Icons.Globe, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sites.Index).ToModuleDefinitionName(), Title = "Site Management", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Site Settings", Parent = "Admin", Path = "admin/site", Icon = Icons.Home, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Site.Index).ToModuleDefinitionName(), Title = "Site Settings", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Page Management", Parent = "Admin", Path = "admin/pages", Icon = Icons.Layers, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Pages.Index).ToModuleDefinitionName(), Title = "Page Management", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "User Management", Parent = "Admin", Path = "admin/users", Icon = Icons.People, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Users.Index).ToModuleDefinitionName(), Title = "User Management", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Profile Management", Parent = "Admin", Path = "admin/profiles", Icon = Icons.Person, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Profiles.Index).ToModuleDefinitionName(), Title = "Profile Management", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Role Management", Parent = "Admin", Path = "admin/roles", Icon = Icons.LockLocked, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Roles.Index).ToModuleDefinitionName(), Title = "Role Management", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Event Log", Parent = "Admin", Path = "admin/log", Icon = Icons.MagnifyingGlass, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Logs.Index).ToModuleDefinitionName(), Title = "Event Log", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "File Management", Parent = "Admin", Path = "admin/files", Icon = Icons.File, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Files.Index).ToModuleDefinitionName(), Title = "File Management", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Recycle Bin", Parent = "Admin", Path = "admin/recyclebin", Icon = Icons.Trash, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.RecycleBin.Index).ToModuleDefinitionName(), Title = "Recycle Bin", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Tenant Management", Parent = "Admin", Path = "admin/tenants", Icon = Icons.List, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Tenants.Index).ToModuleDefinitionName(), Title = "Tenant Management", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Module Management", Parent = "Admin", Path = "admin/modules", Icon = Icons.Browser, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.ModuleDefinitions.Index).ToModuleDefinitionName(), Title = "Module Management", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = Icons.Brush, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Themes.Index).ToModuleDefinitionName(), Title = "Theme Management", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = Icons.Timer, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Jobs.Index).ToModuleDefinitionName(), Title = "Scheduled Jobs", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Sql Management", - Parent = "Admin", - Path = "admin/sql", - Icon = "spreadsheet", + Name = "Login", + Parent = "", + Path = "login", + Icon = Icons.LockLocked, IsNavigation = false, IsPersonalizable = false, - EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sql.Index).ToModuleDefinitionName(), Title = "Sql Management", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "System Info", - Parent = "Admin", - Path = "admin/system", - Icon = "medical-cross", - IsNavigation = false, - IsPersonalizable = false, - EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.SystemInfo.Index).ToModuleDefinitionName(), Title = "System Info", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Upgrade Service", Parent = "Admin", Path = "admin/upgrade", Icon = Icons.Aperture, IsNavigation = false, IsPersonalizable = false, EditMode = true, - PagePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - PageTemplateModules = new List - { - new PageTemplateModule - { - ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Upgrade.Index).ToModuleDefinitionName(), Title = "Upgrade Service", Pane = "Content", - ModulePermissions = new List - { - new Permission(PermissionNames.View, Constants.AdminRole, true), - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }.EncodePermissions(), - Content = "" - } - } - }); - pageTemplates.Add(new PageTemplate - { - Name = "Login", Parent = "", Path = "login", Icon = Icons.LockLocked, IsNavigation = false, IsPersonalizable = false, EditMode = false, + EditMode = false, PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -463,7 +84,13 @@ namespace Oqtane.Repository }); pageTemplates.Add(new PageTemplate { - Name = "Register", Parent = "", Path = "register", Icon = Icons.Person, IsNavigation = false, IsPersonalizable = false, EditMode = false, + Name = "Register", + Parent = "", + Path = "register", + Icon = Icons.Person, + IsNavigation = false, + IsPersonalizable = false, + EditMode = false, PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -488,7 +115,13 @@ namespace Oqtane.Repository pageTemplates.Add(new PageTemplate { - Name = "Reset", Parent = "", Path = "reset", Icon = Icons.Person, IsNavigation = false, IsPersonalizable = false, EditMode = false, + Name = "Reset", + Parent = "", + Path = "reset", + Icon = Icons.Person, + IsNavigation = false, + IsPersonalizable = false, + EditMode = false, PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -512,7 +145,13 @@ namespace Oqtane.Repository }); pageTemplates.Add(new PageTemplate { - Name = "Profile", Parent = "", Path = "profile", Icon = Icons.Person, IsNavigation = false, IsPersonalizable = false, EditMode = false, + Name = "Profile", + Parent = "", + Path = "profile", + Icon = Icons.Person, + IsNavigation = false, + IsPersonalizable = false, + EditMode = false, PagePermissions = new List { new Permission(PermissionNames.View, Constants.AdminRole, true), @@ -534,6 +173,450 @@ namespace Oqtane.Repository } } }); + + // admin pages + pageTemplates.Add(new PageTemplate + { + Name = "Admin", Parent = "", Path = "admin", Icon = "", IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Dashboard.Index).ToModuleDefinitionName(), Title = "Admin Dashboard", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "Site Settings", + Parent = "Admin", + Path = "admin/site", + Icon = Icons.Home, + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Site.Index).ToModuleDefinitionName(), Title = "Site Settings", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "Page Management", + Parent = "Admin", + Path = "admin/pages", + Icon = Icons.Layers, + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Pages.Index).ToModuleDefinitionName(), Title = "Page Management", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "User Management", + Parent = "Admin", + Path = "admin/users", + Icon = Icons.People, + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Users.Index).ToModuleDefinitionName(), Title = "User Management", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "Profile Management", + Parent = "Admin", + Path = "admin/profiles", + Icon = Icons.Person, + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Profiles.Index).ToModuleDefinitionName(), Title = "Profile Management", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "Role Management", + Parent = "Admin", + Path = "admin/roles", + Icon = Icons.LockLocked, + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Roles.Index).ToModuleDefinitionName(), Title = "Role Management", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "File Management", + Parent = "Admin", + Path = "admin/files", + Icon = Icons.File, + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Files.Index).ToModuleDefinitionName(), Title = "File Management", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "Recycle Bin", + Parent = "Admin", + Path = "admin/recyclebin", + Icon = Icons.Trash, + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.RecycleBin.Index).ToModuleDefinitionName(), Title = "Recycle Bin", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.AdminRole, true), + new Permission(PermissionNames.Edit, Constants.AdminRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + + // host pages + pageTemplates.Add(new PageTemplate + { + Name = "Event Log", + Parent = "Admin", + Path = "admin/log", + Icon = Icons.MagnifyingGlass, + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Logs.Index).ToModuleDefinitionName(), Title = "Event Log", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); pageTemplates.Add(new PageTemplate + { + Name = "Tenant Management", + Parent = "Admin", + Path = "admin/tenants", + Icon = Icons.List, + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Tenants.Index).ToModuleDefinitionName(), Title = "Tenant Management", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "Site Management", Parent = "Admin", Path = "admin/sites", Icon = Icons.Globe, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sites.Index).ToModuleDefinitionName(), Title = "Site Management", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "Module Management", Parent = "Admin", Path = "admin/modules", Icon = Icons.Browser, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.ModuleDefinitions.Index).ToModuleDefinitionName(), Title = "Module Management", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = Icons.Brush, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Themes.Index).ToModuleDefinitionName(), Title = "Theme Management", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = Icons.Timer, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Jobs.Index).ToModuleDefinitionName(), Title = "Scheduled Jobs", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "Sql Management", + Parent = "Admin", + Path = "admin/sql", + Icon = "spreadsheet", + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sql.Index).ToModuleDefinitionName(), Title = "Sql Management", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "System Info", + Parent = "Admin", + Path = "admin/system", + Icon = "medical-cross", + IsNavigation = false, + IsPersonalizable = false, + EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.SystemInfo.Index).ToModuleDefinitionName(), Title = "System Info", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + pageTemplates.Add(new PageTemplate + { + Name = "System Update", Parent = "Admin", Path = "admin/update", Icon = Icons.Aperture, IsNavigation = false, IsPersonalizable = false, EditMode = true, + PagePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + PageTemplateModules = new List + { + new PageTemplateModule + { + ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Upgrade.Index).ToModuleDefinitionName(), Title = "System Update", Pane = "Content", + ModulePermissions = new List + { + new Permission(PermissionNames.View, Constants.HostRole, true), + new Permission(PermissionNames.Edit, Constants.HostRole, true) + }.EncodePermissions(), + Content = "" + } + } + }); + return pageTemplates; } @@ -615,10 +698,7 @@ namespace Oqtane.Repository Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" }); - var _pageTemplates = CreateAdminPages(); - CreatePages(site, _pageTemplates); - - // process site template + // process site template first if (string.IsNullOrEmpty(site.SiteTemplateType)) { var section = _config.GetSection("Installation:SiteTemplate"); @@ -648,6 +728,9 @@ namespace Oqtane.Repository CreatePages(site, pageTemplates); } } + + // create admin pages + CreatePages(site, CreateAdminPages()); } private void CreatePages(Site site, List pageTemplates) diff --git a/Oqtane.Shared/Shared/SettingKeys.cs b/Oqtane.Shared/Shared/SettingKeys.cs index 7db38bac..6524e333 100644 --- a/Oqtane.Shared/Shared/SettingKeys.cs +++ b/Oqtane.Shared/Shared/SettingKeys.cs @@ -4,7 +4,6 @@ { public const string InstallationSection = "Installation"; public const string DefaultAliasKey = "DefaultAlias"; - public const string HostUserKey = "HostUser"; public const string HostPasswordKey = "HostPassword"; public const string HostEmailKey = "HostEmail"; public const string SiteTemplateKey = "SiteTemplate"; From 457debf35e155f24795b2e688120d0d1ad48f039 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 22 Apr 2020 08:28:05 -0400 Subject: [PATCH 164/265] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aeabf4ce..859b106c 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ Please note that this project is governed by the **[.NET Foundation Contributor # Roadmap This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist. -Note: We are planning to release V1 at the same time that Blazor WebAssembly ships in May 2020 +Note: We are planning to release V1 at the same time that Blazor WebAssembly ships on May 21, 2020 -V1 (MVP) +V1 (MVP) - [x] Multi-Tenant ( Shared Database & Isolated Database ) - [x] Modular Architecture / Headless API - [x] Dynamic Page Compositing Model / Site & Page Management From 845d38caf6c630e541a22e227284efa4ef788060 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Thu, 23 Apr 2020 00:22:12 +0300 Subject: [PATCH 165/265] Remove AddBaseAddressHttpClient() --- Oqtane.Client/Program.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index 8c9b118a..74fa8de4 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -5,6 +5,7 @@ using Oqtane.Services; using System.Reflection; using System; using System.Linq; +using System.Net.Http; using Oqtane.Modules; using Oqtane.Shared; using Oqtane.Providers; @@ -19,7 +20,9 @@ namespace Oqtane.Client var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("app"); - builder.Services.AddBaseAddressHttpClient(); + builder.Services.AddSingleton( + new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) } + ); builder.Services.AddOptions(); // register auth services From 2278e499831194c7b87fc5d66102c6ddb471cd85 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Thu, 23 Apr 2020 03:36:32 +0300 Subject: [PATCH 166/265] Remove wwwroot from Oqtane.Client --- Oqtane.Client/wwwroot/css/app.css | 176 ---- .../wwwroot/css/open-iconic/FONT-LICENSE | 86 -- .../wwwroot/css/open-iconic/ICON-LICENSE | 21 - .../wwwroot/css/open-iconic/README.md | 114 --- .../font/css/open-iconic-bootstrap.min.css | 1 - .../open-iconic/font/fonts/open-iconic.eot | Bin 28196 -> 0 bytes .../open-iconic/font/fonts/open-iconic.otf | Bin 20996 -> 0 bytes .../open-iconic/font/fonts/open-iconic.svg | 543 ---------- .../open-iconic/font/fonts/open-iconic.ttf | Bin 28028 -> 0 bytes .../open-iconic/font/fonts/open-iconic.woff | Bin 14984 -> 0 bytes .../wwwroot/css/quill/quill1.3.6.bubble.css | 952 ------------------ .../wwwroot/css/quill/quill1.3.6.snow.css | 945 ----------------- Oqtane.Client/wwwroot/favicon.ico | Bin 5430 -> 0 bytes Oqtane.Client/wwwroot/images/checked.png | Bin 427 -> 0 bytes Oqtane.Client/wwwroot/images/help.png | Bin 801 -> 0 bytes Oqtane.Client/wwwroot/images/null.png | Bin 177 -> 0 bytes Oqtane.Client/wwwroot/images/unchecked.png | Bin 438 -> 0 bytes Oqtane.Client/wwwroot/js/interop.js | 259 ----- .../wwwroot/js/quill-blot-formatter.min.js | 1 - Oqtane.Client/wwwroot/js/quill1.3.6.min.js | 8 - Oqtane.Client/wwwroot/js/site.js | 2 - Oqtane.Client/wwwroot/loading.gif | Bin 8238 -> 0 bytes Oqtane.Client/wwwroot/oqtane.png | Bin 14299 -> 0 bytes Oqtane.Client/wwwroot/service-worker.js | 2 - 24 files changed, 3110 deletions(-) delete mode 100644 Oqtane.Client/wwwroot/css/app.css delete mode 100644 Oqtane.Client/wwwroot/css/open-iconic/FONT-LICENSE delete mode 100644 Oqtane.Client/wwwroot/css/open-iconic/ICON-LICENSE delete mode 100644 Oqtane.Client/wwwroot/css/open-iconic/README.md delete mode 100644 Oqtane.Client/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css delete mode 100644 Oqtane.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot delete mode 100644 Oqtane.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf delete mode 100644 Oqtane.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.svg delete mode 100644 Oqtane.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf delete mode 100644 Oqtane.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff delete mode 100644 Oqtane.Client/wwwroot/css/quill/quill1.3.6.bubble.css delete mode 100644 Oqtane.Client/wwwroot/css/quill/quill1.3.6.snow.css delete mode 100644 Oqtane.Client/wwwroot/favicon.ico delete mode 100644 Oqtane.Client/wwwroot/images/checked.png delete mode 100644 Oqtane.Client/wwwroot/images/help.png delete mode 100644 Oqtane.Client/wwwroot/images/null.png delete mode 100644 Oqtane.Client/wwwroot/images/unchecked.png delete mode 100644 Oqtane.Client/wwwroot/js/interop.js delete mode 100644 Oqtane.Client/wwwroot/js/quill-blot-formatter.min.js delete mode 100644 Oqtane.Client/wwwroot/js/quill1.3.6.min.js delete mode 100644 Oqtane.Client/wwwroot/js/site.js delete mode 100644 Oqtane.Client/wwwroot/loading.gif delete mode 100644 Oqtane.Client/wwwroot/oqtane.png delete mode 100644 Oqtane.Client/wwwroot/service-worker.js diff --git a/Oqtane.Client/wwwroot/css/app.css b/Oqtane.Client/wwwroot/css/app.css deleted file mode 100644 index c28214d4..00000000 --- a/Oqtane.Client/wwwroot/css/app.css +++ /dev/null @@ -1,176 +0,0 @@ -@import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); - -html, body { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -app { - position: relative; - display: flex; - flex-direction: column; -} - -/* Control Panel */ -.app-controlpanel { - height: 100%; - width: 0%; - position: fixed; /* Stay in place */ - z-index: 9999; /* Sit on top */ - right: 0; - top: 0; - overflow-x: hidden; /* Disable horizontal scroll */ - transition: 0.5s; /* 0.5 second transition effect to slide in or slide down the overlay (height or width, depending on reveal) */ -} - - /* Position the content inside the overlay */ - .app-controlpanel .card-body { - position: relative; - width: 100%; /* 100% width */ - } - - /* Pad the navigation links */ - .app-controlpanel .nav-item { - font-size: 0.9rem; - padding-bottom: 0.5rem; - } - - .app-controlpanel .card-body .control-label { - color: white; - } - -/* Admin Modal */ -.app-admin-modal .modal { - position: fixed; /* Stay in place */ - z-index: 9999; /* Sit on top */ - left: 0; - top: 0; - display: block; - width: 100%; /* Full width */ - height: 100%; /* Full height */ - overflow: auto; /* Enable scroll if needed */ - background: rgba(0,0,0,0.3); /* Dim background */ -} - -.app-admin-modal .modal-dialog { - width: 100%; /* Full width */ - height: 100%; /* Full height */ - max-width: none; /* Override default of 500px */ -} - -.app-admin-modal .modal-content { - margin: 5% auto; /* 5% from the top and centered */ - width: 80%; /* Could be more or less, depending on screen size */ -} - -.app-pane-admin-border { - width: 100%; - border-width: 1px; - border-style: dashed; - border-color: gray; -} - -.app-pane-admin-title { - width: 100%; - text-align: center; - color: gray; -} - -.app-progress-indicator { - background: rgba(0,0,0,0.2) url('../loading.gif') no-repeat 50% 50%; - width: 100%; - height: 100%; - position: fixed; - top: 0; - left: 0; - z-index: 9999; /* Sit on top */ -} - -.app-rule { - width: 100%; - color: gray; - height: 1px; - background-color: gray; -} - -.app-link-unstyled, .app-link-unstyled:visited, .app-link-unstyled:hover, .app-link-unstyled:active, .app-link-unstyled:focus, .app-link-unstyled:active:hover { - font-style: inherit; - color: inherit; - background-color: transparent; - font-size: inherit; - text-decoration: none; - font-variant: inherit; - font-weight: inherit; - line-height: inherit; - font-family: inherit; - border-radius: inherit; - border: inherit; - outline: inherit; - box-shadow: inherit; - padding: inherit; - vertical-align: inherit; -} - -/* Tooltips */ -.app-tooltip { - cursor: help; - position: relative; -} - - .app-tooltip::before, - .app-tooltip::after { - left: 50%; - opacity: 0; - position: absolute; - z-index: -100; - } - - .app-tooltip:hover::before, - .app-tooltip:focus::before, - .app-tooltip:hover::after, - .app-tooltip:focus::after { - opacity: 1; - transform: scale(1) translateY(0); - z-index: 100; - } - - .app-tooltip::before { - border-style: solid; - border-width: 1em 0.75em 0 0.75em; - border-color: #3E474F transparent transparent transparent; - bottom: 100%; - content: ""; - margin-left: -0.5em; - transition: all .65s cubic-bezier(.84,-0.18,.31,1.26), opacity .65s .5s; - transform: scale(.6) translateY(-90%); - } - - .app-tooltip:hover::before, - .app-tooltip:focus::before { - transition: all .65s cubic-bezier(.84,-0.18,.31,1.26) .2s; - } - - .app-tooltip::after { - background: #3E474F; - border-radius: .25em; - bottom: 180%; - color: #EDEFF0; - content: attr(data-tip); - margin-left: -8.75em; - padding: 1em; - transition: all .65s cubic-bezier(.84,-0.18,.31,1.26) .2s; - transform: scale(.6) translateY(50%); - width: 17.5em; - } - - .app-tooltip:hover::after, - .app-tooltip:focus::after { - transition: all .65s cubic-bezier(.84,-0.18,.31,1.26); - } - -@media (max-width: 760px) { - .app-tooltip::after { - font-size: .75em; - margin-left: -5em; - width: 10em; - } -} \ No newline at end of file diff --git a/Oqtane.Client/wwwroot/css/open-iconic/FONT-LICENSE b/Oqtane.Client/wwwroot/css/open-iconic/FONT-LICENSE deleted file mode 100644 index a1dc03f3..00000000 --- a/Oqtane.Client/wwwroot/css/open-iconic/FONT-LICENSE +++ /dev/null @@ -1,86 +0,0 @@ -SIL OPEN FONT LICENSE Version 1.1 - -Copyright (c) 2014 Waybury - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Oqtane.Client/wwwroot/css/open-iconic/ICON-LICENSE b/Oqtane.Client/wwwroot/css/open-iconic/ICON-LICENSE deleted file mode 100644 index 2199f4a6..00000000 --- a/Oqtane.Client/wwwroot/css/open-iconic/ICON-LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Waybury - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/Oqtane.Client/wwwroot/css/open-iconic/README.md b/Oqtane.Client/wwwroot/css/open-iconic/README.md deleted file mode 100644 index 6b810e47..00000000 --- a/Oqtane.Client/wwwroot/css/open-iconic/README.md +++ /dev/null @@ -1,114 +0,0 @@ -[Open Iconic v1.1.1](http://useiconic.com/open) -=========== - -### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons) - - - -## What's in Open Iconic? - -* 223 icons designed to be legible down to 8 pixels -* Super-light SVG files - 61.8 for the entire set -* SVG sprite—the modern replacement for icon fonts -* Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats -* Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats -* PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px. - - -## Getting Started - -#### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections. - -### General Usage - -#### Using Open Iconic's SVGs - -We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute). - -``` -icon name -``` - -#### Using Open Iconic's SVG Sprite - -Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack. - -Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.* - -``` - - - -``` - -Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions. - -``` -.icon { - width: 16px; - height: 16px; -} -``` - -Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag. - -``` -.icon-account-login { - fill: #f00; -} -``` - -To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/). - -#### Using Open Iconic's Icon Font... - - -##### …with Bootstrap - -You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}` - - -``` - -``` - - -``` - -``` - -##### …with Foundation - -You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}` - -``` - -``` - - -``` - -``` - -##### …on its own - -You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}` - -``` - -``` - -``` - -``` - - -## License - -### Icons - -All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT). - -### Fonts - -All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web). diff --git a/Oqtane.Client/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css b/Oqtane.Client/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css deleted file mode 100644 index 4664f2e8..00000000 --- a/Oqtane.Client/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css +++ /dev/null @@ -1 +0,0 @@ -@font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'} \ No newline at end of file diff --git a/Oqtane.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot b/Oqtane.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot deleted file mode 100644 index f98177dbf711863eff7c90f84d5d419d02d99ba8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28196 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 f%X4~$E_2;^J#ifP;CI~=<%5iE_!YyhznS - - - - -Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014 - By P.J. Onori -Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Oqtane.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf b/Oqtane.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf deleted file mode 100644 index fab604866cd5e55ef4525ea22e420c411f510b01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28028 zcmdtKd3;;feJ6U)xmZaM3$bwn2@oW}1>CTclqiYRLQA$5Y6)oBGM7s&UL;16CB=~) zH%=W_6UVZgVeQ0|xQgR(6Hfyvk(0O_J9V>Qn%H&oG)e0>@i>{hWS-onNvmm7eN1S+ zzjH1~P?8h3Z~l59fphM;=bq(ve&@HJt1v}T9Lj@=s?4rmzvGs>e#M?e$-DSAY}wuu zPnxz(DGIB>^~Cf&le3EBXMcd}6Zj5KA3GXEIX;t*;HP5m?7n+mKJ@c`Tz^VYD(~Jm zd1MylPFz2T)UxmH5A7ZOe}4HVio)j=WvpiZ%%sNt@lV$&%8rY;pWcrG(tJLATS5ef5?>;m=`T3U~TdOF!ucQC(+%tJ%mOWkhLq)lj+7BL_yl3W< z|K$8OuAf04Cua{GIr?|bL{U+0Z%`D&^z7l8*&pAf{=TBzgX+qM@uk@--(Pw5FDd=Y zzv;PiF*WcaJFOVej)kLlWmcx_K_#l7Hdl-))s-Jiaq+Wt?>bHS=G)5KZ>d2Pj^cL) zspv_s6cktVJbfGVdn<57wHg$I5=3giAFkhi>*`hfDp#)t<$c^@rlkfMM*)4yKjpoZ zm;e7O&j~k_zvW&)&a7B2n1DOHt25zBxS|PHxb6pE|LkYEcj28n_7e#qH3-ZzD|Xba zuyCr&LatB>-zH{GA;V(qa?!?47iYCXp*YJ<^ZA9f8oR8`&1u?oZB#99!|V;=FIv_H zHB=}yp=sKjTsBRN!=aeIVp3RFXLZmQUKG&EInIE&niKmm!2v$!20ko9;D~#VS11nc$`+=KtG~yf>$N>ebwp;yRE`v zGH}Jv)#<|c{rH;oR1LoSw#IV{&!ba4$LBE(`n=!v1WX7n_@h>+xl&r**uQ0L1!}B7 zt%+QDbF_1>eooBQh?%++pHi_R?rNvaVp0_&7C-Jcx2Da0VHnH(`yji@Q4AK*~y%C}@R$UciWpw&Fz=BN&REs|Hb5 z;$@}9KzIq9aGHV#O5h8E}wr4JV`QcE{(tKyortc-Ac zv8~hc$>PQ3trZG48duddZHX0S*S59PQlWs6zK{7a+O3K5cJSm-tA>$kafivtXzwF&by768I+`}rql(K|3%uZ`sLDML~eis`agzI^b!&%^)q#exy z{uPQ>X;RvWcC-W=e9lS}(GIuYlzx?4YHksgUImQXzoMzdf+Q*$Kg_9fyOSJZs$*<<+E(%oGdnwYpO{(HB(_-7zv zf{W|>&!PC0imz2WsU5X!4}vIr{4C;UXb`h{hi!c4o#Kn{u+t~=S@!wOPZV$8Jb5y& z2B{D?Kb}81xtV=Fdw=ovEV7czOS)@RtV$L75Hy$i0P=${%0+O6L9*X{n_ULtT`Uma zcpe2nR-kN&c4Mx7aJ`5UC-`?oL-n;aHU{{!w7-%2v5+p0DI98!q+H=t!kzY;Lk8jw z9$!4Yk|kTp^6XKUi`{*~_MqmmFZ`|Dqdj=ZUUQlSi+|q{2y_IPLnLaD+1c-X(xDa4 z*gYOQJE*Z**8?vU0$$A%qWMuB6`;a#{Ho zt(sfqBHoMjtCFy>n+Y~b9K*m+LKs3S=}r*hvY}^>Jv{vG+rtlQg~72wVC>ju4rR7% z$sGF3*uqQggM&0jfww#&+H;~s;H}GHHxf>{6Grf~aLOFbL^J-3H)Hl@=HhJ6PkvH7 z8{f2PZf?^i$TM?l@X8ZUUAdwcfOZf$EZYxWC7`sT-KIvruTtPDUw=L zK&%PU2IwJhOkYnG7;3ptY2dV;w43plfJ`Z{ovO3g_gK62-G8vEK~3AYZ{eI3GQtww z@naTIz&YGdTO;7iFb!-NY#O#Y?0Lu^g&BK5+2eYB9kt&Chy zfn`Q4M6*FP82LQSjArinLqVwK=$geu>6<*q=jB~2_&j$6Ca}PZ|3b3InB*GPsR8WC zdaR*a?n&0fd}iig5CvB;D?tY9&>S72HQ@i#6f+u&|KzB3ZAsgz*zsapcJtE*H?CND z(=BR1jTz0wKd7>$x43E@tfF{qbN1lV&EbE1ts7D9GGDu?OG5h7FYwkgf$VxLUl*#P#m;wC zHy9Wj9BCPLIK2U%W3wr4q*}&xM$b{3ll^&h&^+u5hcn=JN7hh-m1 zUgY!Eg_o@Ci6@G-`&Hk0cZbvNW=`vi*luVYA0ZEs-s1)rt%np7R@|$dpbgX{mqGDrvr8pyH$VUJ#p{eOwmGZp&nc8YPIm z*Gqe^tGyMQPwYJa8z?`>2;_3sX zzCdyw-DiScxfm(eg1j!u3zB9pwPDrk6lbXw+0Ifwq8%#>vD54{>7}xcq{~ehO9(P< zALw#-N2Ix$ldJ~$!4UT~G4MeLq#}SSf<4y5q~rirF2v3jJ*|iQU?^1886#}I!lG_d zy_LnY6<*bzuBw=0M&@l~+a$}X0^=JH6Hh1O9908c; zM24g{$zMn|S**+aX1^KBA#1BaN`;`eysqH2ZYzW2g4@MeR3kJH8QJdA7^F_c%u#cc zmXKPcMWmFrIxV;^*H-~nwrliPJmz0iUom!V^aVD&sCQ=N^)>B~OnXf`8B7acfS?sM zmz3BmqjPhm|D_g7CAdXH6XO%~$OS3Oav@MHWMv=`v3~r7K+uWp8xx>F#1a-+V=~Qv zF`Fvw#f$dJO~t?4#4h8)Ub%1#ziJRv9mOb#dp8scdT}K`RcWVwm*fsJ=wJ=-+Y5Wh zGJU7C+glS}pWhtmVI_r!+kTVJ|0Z8Nt2IYPTY8;k8V}vL`9e!*w5``x2K!p@dCP@J zqnH~wX@C(UGlzwx3v(o{l^9}fkQ-uq0ZwKx(D*cab^n>pe(Nic3yZ&MI5y^bY@=#m zChiT)6$*16H3+kob7x;&O`PP)cwb`d*sjCS9UuZw1#tWlj0FyOKb%#EBWezp zhTw;O0^xfl3+sJ9S}43FdcO5a0lN@{qts`ip!YX)1!5)OjlKwvrS4OW{UP*~#rX;) zLrhdQof|3+jUA&&@p;+iP!1Gv*WqPju2dQ^X0J`?3GTQb93RXd05g{0xYX{I58ra< zxsHL3+B2+|0JqcwWX>adoK4B}{xgMZ`yyPBV^*P;I)DpR6~ul(>sW%pJYe>Rqpbslp0X^vu63MFpo-IU6@N$SCoJNeMx8o)D97z!m@tlv(mI$ z_AG!vnmwd~S*c6Nr=`uUyzkPujZ5P;`h{gy@;nS%@0}F40_I7`LvmCU{JmdUsjOGF zD6ZA^jT?rC1_x4ou{Mulf>DEz2bSiv6fL2=39bdS7w9i&4y4JXSQw%|!el_I9Z4Q$ zDG01&A!rFgAP3Afg8NXMc4GO(m%!D$adxC5fK3AAxq__%vqFqG8iev2JRu*qp@Q62 zfsQZ1C?)F0siXs&TJQ_8rz^0}Objx#D+!&*3+C6HBEhQw1xxi?E8e|SfZ(UwmBEXM z-nk+5LH4QfkP#RTmL(%kiReXDqq~HZ*U&u@<+Kk8UVSa)6Kpn4BkiDNptUIDJ=SY@ zkBcBzYMiV{WwxV*=RsldIPBMY8zuXlUxEGF<1E?hVZYXuO{sF?wJ0zat_j%kx*L8!tfj+p%JQRk~3}w^rf?yJY zV*aWYrv`*%%l5>JXW1UopyOI`2*sdC8Wo|OnqPt!t+O9|CrR+?>x$HS#99MhC8K(2 ztxNDSC)1fhPHLFk45>^sQo2`KrV{UaMSyb7V^>v+&%V1B#*MK-)2&Wo$pGuMh#??- z+z~K1Z#9v)+g`idzW#bVq1{gMoUr|qNgVcP>@oPGNQ;2&gN*d=zAY>uP$%G?qB$?& znJS(q+O69ljM647X$7?cVnO&T+z#}dTz3P!v*_0-o^!(wrnZ&|G}6Dq_LPY(g6PNI zDl5^)A=|6O>OzmUsWc9Nn`{cOo`#dH{)|vzg>p(T)qv(28GVPgfc0(R^Y45C`{3jk z>T)^vff3@4BL`@XVqJxtWK=AQ4deCDx>mdFRTV_l$&Uk@0RAA#w-SjGUnp%cc6wng zBttUz3)V#z9g-ypia;Rj1pHGUpea|MCNrcm2%6F;>`Bn~;(lO%I2D0PEi9;hV_O|{aD zG1j=HZ0Bz@2u7Al4yhUFui#VCE=icjV$D@;{Qkf@_DBwYjSE z@S!s+2@6-AIdr(Qs<<)W9Xp22I@sW81Nda{lRBinMQvcmvc4D} zLItj=PwpZ>n%0P559kRR$zm|JUk0@#-)zO#%47#`7_zwdl2=Xt!c9Pe*D}}|AjerQ zSP+{a>434-Yiz}?7I-fQ38W)|0rEo`T{eJzko;$_w15_n{Aa|Ner3bK;auwcn7 zxeVbVCyG*_N#y3{=jP@k*ikeVv6rAH&cn8{Xj_C90qGUeiw7c17z>i|lF2F>$|NGG zFl^?G=caFSZhrNtCbr30Jnv@h&bMy;*x_A!?!5cO^i{?EZD*nOm1baR{Lbv5ag7`~ zoA1lsvs+u;qCND-)US|#M873|N!As}KR)pK63>MEvy5i~s2TlB_7w8{(;Aj&1IcNN zAM~-r$Nn{PC0fHWl|TF5vZ0hKf0u0d-g2pwEq|L_`u^ogj2cV2#AB?2SJ*2o0=ED* zL{5Nvli2|hJ;Dug8es@&;u^Geaw7soNFmp*NZ3jGRS(Qa0oVHAJ**PA7H>2(F}oq$ zOy-CoQ%U@a#>sm~*h2PD$fRlZM11<@b$u;XtI5A**Td^JeEhZzE|+R+?;gEHdq^0b z3Ki820dJ#Sa9chfO08aR_L^Y{2RpcEEkB)iT#W{No=m1waKkbWTZrM=(#$fcZch%=s7o$M7zP?Z2(a; zB$=R);Sl8umil$6&d!xy{U7 zTUQUS8Qxr6ke7R>^aAXYC7e;gu_0d=q+9}5vm3<^{F*cC(ti4K+YnD2cX6hz4P z!uKNNd&!H<2{pmgL?(!72E_9eo zSG~XB4RmEhJ~vdTc1F5Iz6)NG+)&>wj$`oJ3_5Pd}~f^(Nh*@hrj7 z1gjn9B;`XFAPDnS$e(eAGO&FCD06e{GT<^xUOjOsFK*CArCIO>xBjqf3eVHCV)IgC z)Cd(6FN(%!EKBsu49#*U_V2b0(dBldRNYQLU(#_1KMyUGDW*?jv_%{gXX~s6RWmv zu4+v?2YNR>)Xx2Z#@@bq#+n*kRaHjMTE^5$lUwb7HQaAh(-zfgc3OR~RF&doVs1y+ zYOwn~7HDPFBkNgnMPpjER{0JDeIo;&8ne5-(Gd%^RaRHkR(Sm;V`Y`On!E3*XtG(D zN%d5jDt&6Cd~JwZQ#_fJ-TjR0kx*c~A^yrF#gUQwv1DUFM*E(|dMFi}xyUNZGLT0Id4ixx*U!xSYmhON8Q9@Isb_MOI zQfk3JD!$fO=e3)Nzajpi%y{b(9$e{YDJi0EKIaBSdfpp=|29`w<6gMa%?EXb(p|hj z1d45PlmE8(mfL+nS0HtI1^h{XUeyu3f_MXOgizX{x1_`sI)|1btjHi?WVtC_kpmw- zwit{nag?!sX^y-0lUF8{0{=MR_U%(oxug#5u4*_^P~05cHzr zYmrc$uR`El99|uAB#`Sm5{0vh#o}=cSo9X ziN3x>U{y!QDt1I90Tl4u>VbjPC!RT>C)$dwE0VpvN%|ry;iJc6k^JP7G_m9uGYQ5i z42LNMx?n_*M~Dds3jtGw%WxJZM4&fb^Xc-Z&@90ZE#n}xH|H^K?F2PgiU8cPzG*X;t<{~s@Ewc#f%^JAcM5Di|8`8 zt)i0RFNzmsgatb-<1vb}%dhXOu5I)p%B$7pyVM&>MF{e|PB~fa2F@KDSj3l;*s{#GqTM7HF%D=1OirTVkeS`pN&nEGQGf zH<%OJD%}g%OE8$*N;K~M+ek?Ek@QZ=K{797A#g_8M^L@QFL6qlBUVX~c4TH2DRftS z1b-$Ond~tXaYJ&gcXf4ltPN6Z17uhyqG1h+MJQWB&(EN5FpJ-r7h+IAP&slo!ADEf z^Tt`kgNZ7TUv8XYs6w97>53j_Vr6P8kqpd!*b?5bt9S~%0;F7}5P?W(7@-wX9l%d=znfr%CJ4UDvf z0&J@Ey?1+whJ!}P_Nt|w7QO*-LIrHK39dq6`Js5_95n~<#OEk<95W@!_{x=n7RMK2 zd8s`CD?jlZ8z-IvKWGYV0Z@q$6U`BC@J7k43WpDZLn-k5GBQOQAcsyg#4r*Ipio9c zP+$$N7F9%~gOi2PZd0A$HRN;fm=U9+Z&pMvM508voY3C|NIgC}UlXe^X}0PW9j;EB zW;EY2{`hNb&z+~i*UqTH*B;-s)r8xfu8tMeHqBsd#}mbSPv42dG;f?)T7UHI6#fpc zOW2-;t-#I^I0!>aiG{+{EbLCg0>xx-lp4&R%$|PWU@&Owy#L-OvL|mAf~roRAr4^Y z_z~mXO}wZx+En9mn8_apw4m8}L#<#dTp$Ta(Oj@2*=@;o21_yny8b=XdlV?<*`^&veDfVWp&KJeGyLt_=znKkl`P~Kc#4@ z499g_ddY_YQ55{%%4XPZk^pu>Y4Mg>6C}e||^>sa*Z2KnZ52N|HnG0$F z`G&|dLRS0Ictm~a3n*_t;UX(CV)#q#-_~f>Ap_1oY%e$hAj8a(^$`M0)JOvzCB)@7lNe+IIY1- zo=lq;gL3r412BA%8V3g(5H3WXE?B&%CiB@X!h+g;(Ew(SARSWTIs%W~6~~^P9c+)^ z^_Yjx8wT4Ah*(CPG7k;>8HMV^Nv9KvU;N;6)priIw-4S~{oKL04BsKRE&4jp z09c=gfI(1c!91En)k2qA3?+ukYH6&bZ%DawSqSkJ5R`@I5i5=O1kY9(I9#+r45iUP zB*og3@Clru@mxKxR$w12o=IT3g<2?Bpk~bJyY$?eRc&v4^tnq<^7&P3p1b5b@#LlF zKKcgmhVVezd;C~u8|f(wVMmD+h#?X>0T}j1$-^FId&mw4vM2uWBWPghg3?lZ0&fCn z&neo2W=)zNoR=wsdFjG6WPs_B;xzpA#sBsDdd}d?wo2 zxy~oXeDy!@moVoT`iN2=iZp{$KdYD@q7d+772=l>3u#7Jq#sw@4>KUdK*s*)*};K< zD=qs*TPD`sYBt+z%vTy%Ah5Hscqz^j$umjo(RKH4{n;~HnGa{`Ag*0*8Qs@1xo!{K z>rTr*H*RZ0%vka7lBW~Nr0s*K`pnO^GN+^oa?hy3My}H&3Nk`qUpOUBgK5&b3{E6+ z1b$sN1C6!8lia9u5RHvA)p}i3A|8Yh5rQ&ArxZ2i&@$Pmg~)GS)XhrwQ{d@{8!^!554>LAvO5K>rXuKdhv6bW;n7<)3zPK z9EB}PoDri~XFAj55uweCwy3afX9&4U5x#ErIu1m|-LNbCo{*2!V9DHo01S3noRFa4 zmL)qd+1Y()yBa6JRO!b-=tdf_B0aA;%39@dFt(?zrud^7*7o2FuRZ?ZY33~M`@4&2 zoCQ&fM_Bv5JKe87^!RJrnDehLUF^7Ty>8dJ`m~_0!iPw9on>ct#GZDUqb^B=WcclE zLQ5i36wFmZR>(p~#lDuOb@Vej1qc+vdV-@T(1@19Uc_KX*q1^@T3xM+_Gpm*MLTjc z2(jGH%jq^$TTovd-6P$T4r}T*LK2IFu@GcS@Ed6>R7H$mjpV0v3QWbukrt99M3;=z zIfCS4%8*R`;85Eh$RNqC)}hGI=xfEdUIQvYJY~w}rcL+JVc)@h;ik<^eW%ABf9X5yRtP?g%n=#HJ^ukG6EmyxUY=0CxJ|y&w}&`CR3b!1<_R2-3!m}wu(y%k+T+m zZY>n7tj>zrP}_RkjV>F=*m{c3SoFD4e1=87T0&n67J{Z=6Q)_163G85zB0H_ z(Au8}+P-+khxyz%%_9z{L=g$8nz%U7zo^<6@lATSdmFMx z=dG$^7oYz?@vE($YK=UsHGF;dO)NW7{HKxJpJ>gdK2|UKk!QvFLEoBmTqB7Jhkz08 z;EiX7I1r9d8V5om&}x$?k_S_^Uem`#Y=r0kg^X z3srSmOE<*@&%MXpYait~Q35z~@=dZ|1J0yBSuS+P9D>(@7K@?U4HT;ads=450zws` zlRP+siGytb_CG(cX0WrP*tznTr1iQwGKO|lpKDWheV}UV-mO)E z`u?^Qh11sQ;s<08&r4-__E|l6m~NEfcoSQzI+C`&Rjc}J%>y@!_+c9fCBocXAf``O z((HmO!?LTgy-zes*t$ul2_w{1@^hTkF~i86N+8%3NGkltgNSp$Vf?4QZ1NQfwcWwz zoJS=im`4^#ef% z$Fjp-9N{ieN`jAgn#Q)oYbum#!N+`Vd!;zz=!zSB)!2%>C5-TE3Nu5Bt$3ET|L`M) zXNrIO?CUI2`11W@$1sSG{IK|=v(GZmGg|S@*YE$bb_|;Hk{nP0nn*DTz};Yj-$Q{( zz+HFTK<#&Pvt}$20%^zDIukuy*M=p+L9mCer!h%P-&e-=Dcd zd-&&%Ja*|rBpHlgj|u+pQLG^Fgs0ZF-fP0 zO@ev6y&&wQSBe*fbS*A;q+Og71>FE3$v#kx^PGr*cUK6y0jdBVRWixKEt3ur`eK8^ zZLsMlAoyCWsW{XWi*bq`Tz|LI_4ZRB*-*~!M`06>G@)GEH8S_T(q2FxHq1xZ-*MKR z+Dd|UN{^ZLE``^G0$t{$BoUA^*&jm(}czG*v{jdvpQ*XlUZ*!1?F zZ|g~=dbWN0t)|8!3%Btt_g#2mV@s1UYkEa`}7TW_;u$D?h#yiIX# zP2f=Z$+;+Ci{KMi885SW&_!riG61xao5WJRr(K1GuPAc@k!@df< z3%=;Jt5;-`y)a9{Dk)=z;fpSFUJ1>r6c=1l4NAn|+VawM=|20g5UYPIez{8|#h;6i zC25S&gR~dEU0y?0N4N?VZVr2W9e@7{jA2)adP41?rJgqjDNB!`AOM`^3=%+y;A7fL%L+^HAY0{O1?gW7mBC+sS zg;MolS0cwW+7k1NNA#tF?!UXJZYP>`?JAVE^eRRW-GGoGzksjj8MI7=*yAdty{o?6`3 z+}LcNSuA^;WQ5+|)84wapH#SqzEiC_i_dx- zjS+`+ZbKP<$(S&knbTN=Jsm2i;1j}%F5-)EDifq!+RugY{F<|e4p2bM$0=euDO_O5 zUY1OQ1=9XaVGS2k!Z^$YvIkILEwt;w&k1)u2#!Yf1CmC_a7MOz8LYwfET&k2()xj4 z5=L7tc&c$;P_VkiJ_u1FDHR+_y#E5?T72IV*dGgPN!2A0hgj9vF$yy;*F&)9Dj_9? zF(>TxNK2r`h0P-Ps8n!ivxM}6<&-y;<;mYghm~Kn@=1{te=HN>_rXc)Vk1s5{}cf@ zGA)oMOnNY!AB6u)JW|pdk|;Z&6@f?g#G)-t4RtzCq4VYRZU-o97>h_T4w({DhDe6_ zrx5eBEUma;E$}J)6yKsBF{%Pa3qokUP$7RY%2)6j6?`@8ZYb@VMptxJ9x2AC(?r0D z-dRC!odBFd4PGZ10{|y7UErMqh!>&}EQeJ&+(-^8dK4Ji1iVaXO0NhL$H6hxHaHA#NfZiL> z0@~PuBecS%LHj)lr5vv)0Zo9xI!q@FGDCDoBSNoIAmYF_4-Y>~azSfk>LVYSQkx@n zHEVY6TvJn58|vr`*3ukF2(GC8qc_ghS~ZjFu20P^kE00*-yN+t;&?1_ zAL@M@ukB`etEERI*cM*gv-V3slWmsB; z*hOEK8nYN!M5Px6s4QY&04kWm!Y=nVt96?jFEJqLh)Ba?`@hECw1N}Yp?$x*s-k4u z6PkN8U5%Hfkq#gA>FyeK{EaWB9{u`P9!q^OcWF8`x_jrw^b5KcbkErC-DCF@FAnYO z>Dl?qlKvxLr;?wGBIPU>8ta5DgI>qxO$ZW7=0lSEVL>Kafuc(iJQ{RN7ADmv_I30Y z-)_h?1h8-1PZVDgasV_c+(bmm88%cvxwm2AvEJ{#OL$FRY15;&?SiL5a(5$gS(n{$yiNQiv|mJiq2XmbB6LtV%ZnFb z>e8>l6tQsyO~HCE`Z%MYC3qJ>TO<6Ou-m=2pHm1lh?%FL47`gAx(K)w!rD>^;rFx{ z_bvK84O?!7-}5`fZ*JRQcd04CA_RuK_IPd^Vor1)=su$*hNlmJHLdVl)RFQ1-KbT< znX)lb3|hy(c8qiw_kD~_gd31|_P38LE#Gy(YM<(?_)+Q($BO@@R07lRS@wQUc^A=0St)(r{b2RV>%P}q%j>+K{O@Y# zy~au9*WJSyMVX%7unzF6{JHXc`FO$4m(BOR>Xko3d7L#{_8gVH-)FCF>;L36jbRzA z%hwZm{o{l8$){wMTa^>algc-hpTqZfGn-lxVE@EzyqRbDX0Gx3_$T>`U}Med z4)vH?P=9H#8Fm>SFnrPQKMn61W5yxl9^=!-ADV)uoav`#pE+m#l=)}o%NCQR#?oOq zVVSeMX!*Y7rqtF@l3^cDs7b=m7|sWD<7`BVym{@Y&&Rs z#&)sFR5elcVAa!A->UitdyD;;{fzwu`w#6!N7}L3vDfi2$1{$-f2db8eJy$^Z|K7%jf zyV-Zx_oT1jd)MFWf3n6`^JL8%wQaR4YA0$xTKmP?AJi7>R@CjU`)b|y>)xunTyLvy zsb5jQqh70jp#JIlUo|KVS#Zz?8_qWr19br{@QJ`nfxm5RZd~1XTjQr1Uv2zlQ*+a? zrf&v^f+vD!gD(ev82nYJF?3t#Oz2yopElPu4>wOVpKAVU^Sj}i@agcY;h(nHTQ;`L zwmjYPot7)D$=3T?pKg6KVu-AdJQ?}xNHIDTor<1_J|F#WZ8dG{+h*HdZKuFn;+sEJ z_9GI3K3x2g4>MhPx5z87i~Y$W9UfL5*7FRWr~j(wDGKBN)$^*-!Ups_PD8RIdfuqm z*=O`T-k!r=g*3$sBoz}z$vlGv;=ky54r|8$t>;x`RQZ*jHz?KY4n1#F8rc1M-lX{0 z7nKp^Fy8h&sT{?xrUaEK)H#6sar_>|%!4>ja|q=}MS2+T z2Ae@y9QAvVwxPyR{LLx@uvPUad-b}M%DUak5tMeLg&EX?GCp#6X7cEa7M%J}aBKI* z?%4w(UQ9batSpXD>?kQfc>*z1;_Aj-rj5 zlxfismg1)ALkE!@&`T&)4xsD+(%&}n0gQg9m>13SZUK=#lu>z~(gnL)7iQUud=d>U z8`wZ_=fR@~j@~_^^#uoleO;NZcyAwSUEiFtSW!`Sp^L)+#sM*M>ZDu$261!d@R0+D z4hH+W@rUa}fanZH*R_0Nhh}FEc9mu)u~E7D5XO0<&reZ^Q^1Tfl^O6xCll;d7Q8X8 zf>kPOm34s524K!j%*Lufn;guEXr*fAW*+8cKG=b3SS_n#^$Y>PA9Iw!Sf-uimhgA*f1Mm zYuP%so^4>G>?XDmFD$;9-NH7rEo>{>#>Uuowu9|tyVwU{IODvpM#M>`C?% z`!xFudz$?R_F48h_6++Yc9wmfJUnc=!^5d1n*1oz7+3E^S%u4%ksW{ z-Z#nnrg+~p@6&kS4DZ{^$5T9>=J5=VXL-Dz$0vDwipQsUT;uT> z9^cCoy*$weuQE?0cp}LYDV|94M207_Jkie+lRPoS6Vp7Q@x%;I?B&T`p6uhvI8P>c zGRc!E1YPlDh9|Q;+0T=cJUPXa(>$s1f@<6PbJ`~=BX4XgXW~4Q;F%=PqgQ9Fd}@kMP4g*@PtEYDy?nZtPxtZZ zIG;}N=_H>{@#!?5&hY6hpYG?=lYDxLPfzn{jZe?;>AhU*w`~4l|1WJN*uYz)E%B3gjC&tIe>+`I0d_0_2w&rHW$Gh@sEVwS1 zH?&S-K*o`+xx6tvoHvDsG5qm7o9N0LVquIcsGT!T4F~Ct>^xsFl2<0y<<*W5N=JgH zf~U~(xn5)IscpH5t@V>*@|#un=G|;W9iN26)56 zlXFPd2MoSSKc1O1cJf5ZDb?O3z_inc)p6R#&A`I ztFF8Q%{T=}f`Gs@hMl*MOaxC&1oL(Ptt;=0ZQ7ALXVBJ;x8$p4!Y8`&uGpq+xlP+; zVSNbYZc$zxJEu5CcIM7G93y!)Ih=QN5`qG4htJvQrwTuL=EF*;ty^>F2x|eX;Zs;# z>b4^k#$%;?y}VD40PpGUIA*c|aRt$vF2nIrF6a%5O4FjRHJr-Oc@Vq02`8y|qBUpq9 zTC_=|`F298&RD*qGv9&j5(B1g07~6(zl0~VVWLyNwFdB|E8n%a2F#a_b>x}1S3tSD z94gCi^~8cHG0tApVe78nuAl-p92S);zOM>eyLKp?J=ep$m`NYzje*|qkqKb!WVS0G zk9GT3bmbGjt12*T8r73n3dPqN><(_Aoe2=$bn4WG@CHzV9OyOZ9ky$NAyN|kr$9n{ zz<&ITDtYTj=gg_@a4@*y6xvEJ-41rkHu46viCV$@1a0Qk+j3vwK{Z(a6}%9?P=mY~HN@&3D2JDSMB;$3hqQyx(+$sivU$77&VM~1hOELt5AbK}O zbQpwJ05n-qoVQ^227~Lv8>ll{t$qPAnt%>bWk;?%xB^U%Mywa2u_ch3T5)v~ZY{D^ zxlq?5*F;!f8H}+jKcJ6bq_i{>#CNX+Txlr>W8q*oL2W&#?uzm5bDhkCjkjX47^}Hd zymGNv)Gj@`tjPYLas1& zMK?By9OD`g3lQiEz|xCYmQXO-Y| zQ;g6tKMJsJjGb4MHOOp2hEe9`*m)*OZb3$rY^FNHxV44qP-ZLDq0Ba_LzywEGla}` zszaF_REIJ3CWBKf2?R|71YVQ|0s(nD@ zsOp`ueE(wAyXZnxy<6m{>OCSyRS(AU1B+D;(S@iwD{@rzgCa*&568X&|7J-t8t%+n zX7Xyw))T~Px)cc5g)s;q?2{nMQly?erx=GJFm%Y&vMl`uxQA7g=s8tcd#;5&vJJxG tBe`>`w)R|vu3oY{2>a6NN2Vb$p$g>T@pFo;#)kMsZl diff --git a/Oqtane.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff b/Oqtane.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff deleted file mode 100644 index f9309988aeab3868040d3b322658902098eba27f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14984 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 diff --git a/Oqtane.Client/wwwroot/css/quill/quill1.3.6.bubble.css b/Oqtane.Client/wwwroot/css/quill/quill1.3.6.bubble.css deleted file mode 100644 index 4336aa88..00000000 --- a/Oqtane.Client/wwwroot/css/quill/quill1.3.6.bubble.css +++ /dev/null @@ -1,952 +0,0 @@ -/*! - * Quill Editor v1.3.6 - * https://quilljs.com/ - * Copyright (c) 2014, Jason Chen - * Copyright (c) 2013, salesforce.com - */ -.ql-container { - box-sizing: border-box; - font-family: Helvetica, Arial, sans-serif; - font-size: 13px; - height: 100%; - margin: 0px; - position: relative; -} -.ql-container.ql-disabled .ql-tooltip { - visibility: hidden; -} -.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before { - pointer-events: none; -} -.ql-clipboard { - left: -100000px; - height: 1px; - overflow-y: hidden; - position: absolute; - top: 50%; -} -.ql-clipboard p { - margin: 0; - padding: 0; -} -.ql-editor { - box-sizing: border-box; - line-height: 1.42; - height: 100%; - outline: none; - overflow-y: auto; - padding: 12px 15px; - tab-size: 4; - -moz-tab-size: 4; - text-align: left; - white-space: pre-wrap; - word-wrap: break-word; -} -.ql-editor > * { - cursor: text; -} -.ql-editor p, -.ql-editor ol, -.ql-editor ul, -.ql-editor pre, -.ql-editor blockquote, -.ql-editor h1, -.ql-editor h2, -.ql-editor h3, -.ql-editor h4, -.ql-editor h5, -.ql-editor h6 { - margin: 0; - padding: 0; - counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; -} -.ql-editor ol, -.ql-editor ul { - padding-left: 1.5em; -} -.ql-editor ol > li, -.ql-editor ul > li { - list-style-type: none; -} -.ql-editor ul > li::before { - content: '\2022'; -} -.ql-editor ul[data-checked=true], -.ql-editor ul[data-checked=false] { - pointer-events: none; -} -.ql-editor ul[data-checked=true] > li *, -.ql-editor ul[data-checked=false] > li * { - pointer-events: all; -} -.ql-editor ul[data-checked=true] > li::before, -.ql-editor ul[data-checked=false] > li::before { - color: #777; - cursor: pointer; - pointer-events: all; -} -.ql-editor ul[data-checked=true] > li::before { - content: '\2611'; -} -.ql-editor ul[data-checked=false] > li::before { - content: '\2610'; -} -.ql-editor li::before { - display: inline-block; - white-space: nowrap; - width: 1.2em; -} -.ql-editor li:not(.ql-direction-rtl)::before { - margin-left: -1.5em; - margin-right: 0.3em; - text-align: right; -} -.ql-editor li.ql-direction-rtl::before { - margin-left: 0.3em; - margin-right: -1.5em; -} -.ql-editor ol li:not(.ql-direction-rtl), -.ql-editor ul li:not(.ql-direction-rtl) { - padding-left: 1.5em; -} -.ql-editor ol li.ql-direction-rtl, -.ql-editor ul li.ql-direction-rtl { - padding-right: 1.5em; -} -.ql-editor ol li { - counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; - counter-increment: list-0; -} -.ql-editor ol li:before { - content: counter(list-0, decimal) '. '; -} -.ql-editor ol li.ql-indent-1 { - counter-increment: list-1; -} -.ql-editor ol li.ql-indent-1:before { - content: counter(list-1, lower-alpha) '. '; -} -.ql-editor ol li.ql-indent-1 { - counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-2 { - counter-increment: list-2; -} -.ql-editor ol li.ql-indent-2:before { - content: counter(list-2, lower-roman) '. '; -} -.ql-editor ol li.ql-indent-2 { - counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-3 { - counter-increment: list-3; -} -.ql-editor ol li.ql-indent-3:before { - content: counter(list-3, decimal) '. '; -} -.ql-editor ol li.ql-indent-3 { - counter-reset: list-4 list-5 list-6 list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-4 { - counter-increment: list-4; -} -.ql-editor ol li.ql-indent-4:before { - content: counter(list-4, lower-alpha) '. '; -} -.ql-editor ol li.ql-indent-4 { - counter-reset: list-5 list-6 list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-5 { - counter-increment: list-5; -} -.ql-editor ol li.ql-indent-5:before { - content: counter(list-5, lower-roman) '. '; -} -.ql-editor ol li.ql-indent-5 { - counter-reset: list-6 list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-6 { - counter-increment: list-6; -} -.ql-editor ol li.ql-indent-6:before { - content: counter(list-6, decimal) '. '; -} -.ql-editor ol li.ql-indent-6 { - counter-reset: list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-7 { - counter-increment: list-7; -} -.ql-editor ol li.ql-indent-7:before { - content: counter(list-7, lower-alpha) '. '; -} -.ql-editor ol li.ql-indent-7 { - counter-reset: list-8 list-9; -} -.ql-editor ol li.ql-indent-8 { - counter-increment: list-8; -} -.ql-editor ol li.ql-indent-8:before { - content: counter(list-8, lower-roman) '. '; -} -.ql-editor ol li.ql-indent-8 { - counter-reset: list-9; -} -.ql-editor ol li.ql-indent-9 { - counter-increment: list-9; -} -.ql-editor ol li.ql-indent-9:before { - content: counter(list-9, decimal) '. '; -} -.ql-editor .ql-indent-1:not(.ql-direction-rtl) { - padding-left: 3em; -} -.ql-editor li.ql-indent-1:not(.ql-direction-rtl) { - padding-left: 4.5em; -} -.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right { - padding-right: 3em; -} -.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right { - padding-right: 4.5em; -} -.ql-editor .ql-indent-2:not(.ql-direction-rtl) { - padding-left: 6em; -} -.ql-editor li.ql-indent-2:not(.ql-direction-rtl) { - padding-left: 7.5em; -} -.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right { - padding-right: 6em; -} -.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right { - padding-right: 7.5em; -} -.ql-editor .ql-indent-3:not(.ql-direction-rtl) { - padding-left: 9em; -} -.ql-editor li.ql-indent-3:not(.ql-direction-rtl) { - padding-left: 10.5em; -} -.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right { - padding-right: 9em; -} -.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right { - padding-right: 10.5em; -} -.ql-editor .ql-indent-4:not(.ql-direction-rtl) { - padding-left: 12em; -} -.ql-editor li.ql-indent-4:not(.ql-direction-rtl) { - padding-left: 13.5em; -} -.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right { - padding-right: 12em; -} -.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right { - padding-right: 13.5em; -} -.ql-editor .ql-indent-5:not(.ql-direction-rtl) { - padding-left: 15em; -} -.ql-editor li.ql-indent-5:not(.ql-direction-rtl) { - padding-left: 16.5em; -} -.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right { - padding-right: 15em; -} -.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right { - padding-right: 16.5em; -} -.ql-editor .ql-indent-6:not(.ql-direction-rtl) { - padding-left: 18em; -} -.ql-editor li.ql-indent-6:not(.ql-direction-rtl) { - padding-left: 19.5em; -} -.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right { - padding-right: 18em; -} -.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right { - padding-right: 19.5em; -} -.ql-editor .ql-indent-7:not(.ql-direction-rtl) { - padding-left: 21em; -} -.ql-editor li.ql-indent-7:not(.ql-direction-rtl) { - padding-left: 22.5em; -} -.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right { - padding-right: 21em; -} -.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right { - padding-right: 22.5em; -} -.ql-editor .ql-indent-8:not(.ql-direction-rtl) { - padding-left: 24em; -} -.ql-editor li.ql-indent-8:not(.ql-direction-rtl) { - padding-left: 25.5em; -} -.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right { - padding-right: 24em; -} -.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right { - padding-right: 25.5em; -} -.ql-editor .ql-indent-9:not(.ql-direction-rtl) { - padding-left: 27em; -} -.ql-editor li.ql-indent-9:not(.ql-direction-rtl) { - padding-left: 28.5em; -} -.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right { - padding-right: 27em; -} -.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right { - padding-right: 28.5em; -} -.ql-editor .ql-video { - display: block; - max-width: 100%; -} -.ql-editor .ql-video.ql-align-center { - margin: 0 auto; -} -.ql-editor .ql-video.ql-align-right { - margin: 0 0 0 auto; -} -.ql-editor .ql-bg-black { - background-color: #000; -} -.ql-editor .ql-bg-red { - background-color: #e60000; -} -.ql-editor .ql-bg-orange { - background-color: #f90; -} -.ql-editor .ql-bg-yellow { - background-color: #ff0; -} -.ql-editor .ql-bg-green { - background-color: #008a00; -} -.ql-editor .ql-bg-blue { - background-color: #06c; -} -.ql-editor .ql-bg-purple { - background-color: #93f; -} -.ql-editor .ql-color-white { - color: #fff; -} -.ql-editor .ql-color-red { - color: #e60000; -} -.ql-editor .ql-color-orange { - color: #f90; -} -.ql-editor .ql-color-yellow { - color: #ff0; -} -.ql-editor .ql-color-green { - color: #008a00; -} -.ql-editor .ql-color-blue { - color: #06c; -} -.ql-editor .ql-color-purple { - color: #93f; -} -.ql-editor .ql-font-serif { - font-family: Georgia, Times New Roman, serif; -} -.ql-editor .ql-font-monospace { - font-family: Monaco, Courier New, monospace; -} -.ql-editor .ql-size-small { - font-size: 0.75em; -} -.ql-editor .ql-size-large { - font-size: 1.5em; -} -.ql-editor .ql-size-huge { - font-size: 2.5em; -} -.ql-editor .ql-direction-rtl { - direction: rtl; - text-align: inherit; -} -.ql-editor .ql-align-center { - text-align: center; -} -.ql-editor .ql-align-justify { - text-align: justify; -} -.ql-editor .ql-align-right { - text-align: right; -} -.ql-editor.ql-blank::before { - color: rgba(0,0,0,0.6); - content: attr(data-placeholder); - font-style: italic; - left: 15px; - pointer-events: none; - position: absolute; - right: 15px; -} -.ql-bubble.ql-toolbar:after, -.ql-bubble .ql-toolbar:after { - clear: both; - content: ''; - display: table; -} -.ql-bubble.ql-toolbar button, -.ql-bubble .ql-toolbar button { - background: none; - border: none; - cursor: pointer; - display: inline-block; - float: left; - height: 24px; - padding: 3px 5px; - width: 28px; -} -.ql-bubble.ql-toolbar button svg, -.ql-bubble .ql-toolbar button svg { - float: left; - height: 100%; -} -.ql-bubble.ql-toolbar button:active:hover, -.ql-bubble .ql-toolbar button:active:hover { - outline: none; -} -.ql-bubble.ql-toolbar input.ql-image[type=file], -.ql-bubble .ql-toolbar input.ql-image[type=file] { - display: none; -} -.ql-bubble.ql-toolbar button:hover, -.ql-bubble .ql-toolbar button:hover, -.ql-bubble.ql-toolbar button:focus, -.ql-bubble .ql-toolbar button:focus, -.ql-bubble.ql-toolbar button.ql-active, -.ql-bubble .ql-toolbar button.ql-active, -.ql-bubble.ql-toolbar .ql-picker-label:hover, -.ql-bubble .ql-toolbar .ql-picker-label:hover, -.ql-bubble.ql-toolbar .ql-picker-label.ql-active, -.ql-bubble .ql-toolbar .ql-picker-label.ql-active, -.ql-bubble.ql-toolbar .ql-picker-item:hover, -.ql-bubble .ql-toolbar .ql-picker-item:hover, -.ql-bubble.ql-toolbar .ql-picker-item.ql-selected, -.ql-bubble .ql-toolbar .ql-picker-item.ql-selected { - color: #fff; -} -.ql-bubble.ql-toolbar button:hover .ql-fill, -.ql-bubble .ql-toolbar button:hover .ql-fill, -.ql-bubble.ql-toolbar button:focus .ql-fill, -.ql-bubble .ql-toolbar button:focus .ql-fill, -.ql-bubble.ql-toolbar button.ql-active .ql-fill, -.ql-bubble .ql-toolbar button.ql-active .ql-fill, -.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-fill, -.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-fill, -.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-fill, -.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-fill, -.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-fill, -.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-fill, -.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-fill, -.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-fill, -.ql-bubble.ql-toolbar button:hover .ql-stroke.ql-fill, -.ql-bubble .ql-toolbar button:hover .ql-stroke.ql-fill, -.ql-bubble.ql-toolbar button:focus .ql-stroke.ql-fill, -.ql-bubble .ql-toolbar button:focus .ql-stroke.ql-fill, -.ql-bubble.ql-toolbar button.ql-active .ql-stroke.ql-fill, -.ql-bubble .ql-toolbar button.ql-active .ql-stroke.ql-fill, -.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill, -.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill, -.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill, -.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill, -.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill, -.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill, -.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill, -.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill { - fill: #fff; -} -.ql-bubble.ql-toolbar button:hover .ql-stroke, -.ql-bubble .ql-toolbar button:hover .ql-stroke, -.ql-bubble.ql-toolbar button:focus .ql-stroke, -.ql-bubble .ql-toolbar button:focus .ql-stroke, -.ql-bubble.ql-toolbar button.ql-active .ql-stroke, -.ql-bubble .ql-toolbar button.ql-active .ql-stroke, -.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke, -.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke, -.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke, -.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke, -.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke, -.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke, -.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke, -.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke, -.ql-bubble.ql-toolbar button:hover .ql-stroke-miter, -.ql-bubble .ql-toolbar button:hover .ql-stroke-miter, -.ql-bubble.ql-toolbar button:focus .ql-stroke-miter, -.ql-bubble .ql-toolbar button:focus .ql-stroke-miter, -.ql-bubble.ql-toolbar button.ql-active .ql-stroke-miter, -.ql-bubble .ql-toolbar button.ql-active .ql-stroke-miter, -.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke-miter, -.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke-miter, -.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter, -.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter, -.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke-miter, -.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke-miter, -.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter, -.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter { - stroke: #fff; -} -@media (pointer: coarse) { - .ql-bubble.ql-toolbar button:hover:not(.ql-active), - .ql-bubble .ql-toolbar button:hover:not(.ql-active) { - color: #ccc; - } - .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-fill, - .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-fill, - .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill, - .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill { - fill: #ccc; - } - .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke, - .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke, - .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter, - .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter { - stroke: #ccc; - } -} -.ql-bubble { - box-sizing: border-box; -} -.ql-bubble * { - box-sizing: border-box; -} -.ql-bubble .ql-hidden { - display: none; -} -.ql-bubble .ql-out-bottom, -.ql-bubble .ql-out-top { - visibility: hidden; -} -.ql-bubble .ql-tooltip { - position: absolute; - transform: translateY(10px); -} -.ql-bubble .ql-tooltip a { - cursor: pointer; - text-decoration: none; -} -.ql-bubble .ql-tooltip.ql-flip { - transform: translateY(-10px); -} -.ql-bubble .ql-formats { - display: inline-block; - vertical-align: middle; -} -.ql-bubble .ql-formats:after { - clear: both; - content: ''; - display: table; -} -.ql-bubble .ql-stroke { - fill: none; - stroke: #ccc; - stroke-linecap: round; - stroke-linejoin: round; - stroke-width: 2; -} -.ql-bubble .ql-stroke-miter { - fill: none; - stroke: #ccc; - stroke-miterlimit: 10; - stroke-width: 2; -} -.ql-bubble .ql-fill, -.ql-bubble .ql-stroke.ql-fill { - fill: #ccc; -} -.ql-bubble .ql-empty { - fill: none; -} -.ql-bubble .ql-even { - fill-rule: evenodd; -} -.ql-bubble .ql-thin, -.ql-bubble .ql-stroke.ql-thin { - stroke-width: 1; -} -.ql-bubble .ql-transparent { - opacity: 0.4; -} -.ql-bubble .ql-direction svg:last-child { - display: none; -} -.ql-bubble .ql-direction.ql-active svg:last-child { - display: inline; -} -.ql-bubble .ql-direction.ql-active svg:first-child { - display: none; -} -.ql-bubble .ql-editor h1 { - font-size: 2em; -} -.ql-bubble .ql-editor h2 { - font-size: 1.5em; -} -.ql-bubble .ql-editor h3 { - font-size: 1.17em; -} -.ql-bubble .ql-editor h4 { - font-size: 1em; -} -.ql-bubble .ql-editor h5 { - font-size: 0.83em; -} -.ql-bubble .ql-editor h6 { - font-size: 0.67em; -} -.ql-bubble .ql-editor a { - text-decoration: underline; -} -.ql-bubble .ql-editor blockquote { - border-left: 4px solid #ccc; - margin-bottom: 5px; - margin-top: 5px; - padding-left: 16px; -} -.ql-bubble .ql-editor code, -.ql-bubble .ql-editor pre { - background-color: #f0f0f0; - border-radius: 3px; -} -.ql-bubble .ql-editor pre { - white-space: pre-wrap; - margin-bottom: 5px; - margin-top: 5px; - padding: 5px 10px; -} -.ql-bubble .ql-editor code { - font-size: 85%; - padding: 2px 4px; -} -.ql-bubble .ql-editor pre.ql-syntax { - background-color: #23241f; - color: #f8f8f2; - overflow: visible; -} -.ql-bubble .ql-editor img { - max-width: 100%; -} -.ql-bubble .ql-picker { - color: #ccc; - display: inline-block; - float: left; - font-size: 14px; - font-weight: 500; - height: 24px; - position: relative; - vertical-align: middle; -} -.ql-bubble .ql-picker-label { - cursor: pointer; - display: inline-block; - height: 100%; - padding-left: 8px; - padding-right: 2px; - position: relative; - width: 100%; -} -.ql-bubble .ql-picker-label::before { - display: inline-block; - line-height: 22px; -} -.ql-bubble .ql-picker-options { - background-color: #444; - display: none; - min-width: 100%; - padding: 4px 8px; - position: absolute; - white-space: nowrap; -} -.ql-bubble .ql-picker-options .ql-picker-item { - cursor: pointer; - display: block; - padding-bottom: 5px; - padding-top: 5px; -} -.ql-bubble .ql-picker.ql-expanded .ql-picker-label { - color: #777; - z-index: 2; -} -.ql-bubble .ql-picker.ql-expanded .ql-picker-label .ql-fill { - fill: #777; -} -.ql-bubble .ql-picker.ql-expanded .ql-picker-label .ql-stroke { - stroke: #777; -} -.ql-bubble .ql-picker.ql-expanded .ql-picker-options { - display: block; - margin-top: -1px; - top: 100%; - z-index: 1; -} -.ql-bubble .ql-color-picker, -.ql-bubble .ql-icon-picker { - width: 28px; -} -.ql-bubble .ql-color-picker .ql-picker-label, -.ql-bubble .ql-icon-picker .ql-picker-label { - padding: 2px 4px; -} -.ql-bubble .ql-color-picker .ql-picker-label svg, -.ql-bubble .ql-icon-picker .ql-picker-label svg { - right: 4px; -} -.ql-bubble .ql-icon-picker .ql-picker-options { - padding: 4px 0px; -} -.ql-bubble .ql-icon-picker .ql-picker-item { - height: 24px; - width: 24px; - padding: 2px 4px; -} -.ql-bubble .ql-color-picker .ql-picker-options { - padding: 3px 5px; - width: 152px; -} -.ql-bubble .ql-color-picker .ql-picker-item { - border: 1px solid transparent; - float: left; - height: 16px; - margin: 2px; - padding: 0px; - width: 16px; -} -.ql-bubble .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg { - position: absolute; - margin-top: -9px; - right: 0; - top: 50%; - width: 18px; -} -.ql-bubble .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before, -.ql-bubble .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before, -.ql-bubble .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before, -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before, -.ql-bubble .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before, -.ql-bubble .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before { - content: attr(data-label); -} -.ql-bubble .ql-picker.ql-header { - width: 98px; -} -.ql-bubble .ql-picker.ql-header .ql-picker-label::before, -.ql-bubble .ql-picker.ql-header .ql-picker-item::before { - content: 'Normal'; -} -.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { - content: 'Heading 1'; -} -.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { - content: 'Heading 2'; -} -.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { - content: 'Heading 3'; -} -.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { - content: 'Heading 4'; -} -.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { - content: 'Heading 5'; -} -.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { - content: 'Heading 6'; -} -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { - font-size: 2em; -} -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { - font-size: 1.5em; -} -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { - font-size: 1.17em; -} -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { - font-size: 1em; -} -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { - font-size: 0.83em; -} -.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { - font-size: 0.67em; -} -.ql-bubble .ql-picker.ql-font { - width: 108px; -} -.ql-bubble .ql-picker.ql-font .ql-picker-label::before, -.ql-bubble .ql-picker.ql-font .ql-picker-item::before { - content: 'Sans Serif'; -} -.ql-bubble .ql-picker.ql-font .ql-picker-label[data-value=serif]::before, -.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=serif]::before { - content: 'Serif'; -} -.ql-bubble .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before, -.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before { - content: 'Monospace'; -} -.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=serif]::before { - font-family: Georgia, Times New Roman, serif; -} -.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before { - font-family: Monaco, Courier New, monospace; -} -.ql-bubble .ql-picker.ql-size { - width: 98px; -} -.ql-bubble .ql-picker.ql-size .ql-picker-label::before, -.ql-bubble .ql-picker.ql-size .ql-picker-item::before { - content: 'Normal'; -} -.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=small]::before, -.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=small]::before { - content: 'Small'; -} -.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=large]::before, -.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=large]::before { - content: 'Large'; -} -.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=huge]::before, -.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=huge]::before { - content: 'Huge'; -} -.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=small]::before { - font-size: 10px; -} -.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=large]::before { - font-size: 18px; -} -.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=huge]::before { - font-size: 32px; -} -.ql-bubble .ql-color-picker.ql-background .ql-picker-item { - background-color: #fff; -} -.ql-bubble .ql-color-picker.ql-color .ql-picker-item { - background-color: #000; -} -.ql-bubble .ql-toolbar .ql-formats { - margin: 8px 12px 8px 0px; -} -.ql-bubble .ql-toolbar .ql-formats:first-child { - margin-left: 12px; -} -.ql-bubble .ql-color-picker svg { - margin: 1px; -} -.ql-bubble .ql-color-picker .ql-picker-item.ql-selected, -.ql-bubble .ql-color-picker .ql-picker-item:hover { - border-color: #fff; -} -.ql-bubble .ql-tooltip { - background-color: #444; - border-radius: 25px; - color: #fff; -} -.ql-bubble .ql-tooltip-arrow { - border-left: 6px solid transparent; - border-right: 6px solid transparent; - content: " "; - display: block; - left: 50%; - margin-left: -6px; - position: absolute; -} -.ql-bubble .ql-tooltip:not(.ql-flip) .ql-tooltip-arrow { - border-bottom: 6px solid #444; - top: -6px; -} -.ql-bubble .ql-tooltip.ql-flip .ql-tooltip-arrow { - border-top: 6px solid #444; - bottom: -6px; -} -.ql-bubble .ql-tooltip.ql-editing .ql-tooltip-editor { - display: block; -} -.ql-bubble .ql-tooltip.ql-editing .ql-formats { - visibility: hidden; -} -.ql-bubble .ql-tooltip-editor { - display: none; -} -.ql-bubble .ql-tooltip-editor input[type=text] { - background: transparent; - border: none; - color: #fff; - font-size: 13px; - height: 100%; - outline: none; - padding: 10px 20px; - position: absolute; - width: 100%; -} -.ql-bubble .ql-tooltip-editor a { - top: 10px; - position: absolute; - right: 20px; -} -.ql-bubble .ql-tooltip-editor a:before { - color: #ccc; - content: "\D7"; - font-size: 16px; - font-weight: bold; -} -.ql-container.ql-bubble:not(.ql-disabled) a { - position: relative; - white-space: nowrap; -} -.ql-container.ql-bubble:not(.ql-disabled) a::before { - background-color: #444; - border-radius: 15px; - top: -5px; - font-size: 12px; - color: #fff; - content: attr(href); - font-weight: normal; - overflow: hidden; - padding: 5px 15px; - text-decoration: none; - z-index: 1; -} -.ql-container.ql-bubble:not(.ql-disabled) a::after { - border-top: 6px solid #444; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - top: 0; - content: " "; - height: 0; - width: 0; -} -.ql-container.ql-bubble:not(.ql-disabled) a::before, -.ql-container.ql-bubble:not(.ql-disabled) a::after { - left: 0; - margin-left: 50%; - position: absolute; - transform: translate(-50%, -100%); - transition: visibility 0s ease 200ms; - visibility: hidden; -} -.ql-container.ql-bubble:not(.ql-disabled) a:hover::before, -.ql-container.ql-bubble:not(.ql-disabled) a:hover::after { - visibility: visible; -} \ No newline at end of file diff --git a/Oqtane.Client/wwwroot/css/quill/quill1.3.6.snow.css b/Oqtane.Client/wwwroot/css/quill/quill1.3.6.snow.css deleted file mode 100644 index 8094fc96..00000000 --- a/Oqtane.Client/wwwroot/css/quill/quill1.3.6.snow.css +++ /dev/null @@ -1,945 +0,0 @@ -/*! - * Quill Editor v1.3.6 - * https://quilljs.com/ - * Copyright (c) 2014, Jason Chen - * Copyright (c) 2013, salesforce.com - */ -.ql-container { - box-sizing: border-box; - font-family: Helvetica, Arial, sans-serif; - font-size: 13px; - height: 100%; - margin: 0px; - position: relative; -} -.ql-container.ql-disabled .ql-tooltip { - visibility: hidden; -} -.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before { - pointer-events: none; -} -.ql-clipboard { - left: -100000px; - height: 1px; - overflow-y: hidden; - position: absolute; - top: 50%; -} -.ql-clipboard p { - margin: 0; - padding: 0; -} -.ql-editor { - box-sizing: border-box; - line-height: 1.42; - height: 100%; - outline: none; - overflow-y: auto; - padding: 12px 15px; - tab-size: 4; - -moz-tab-size: 4; - text-align: left; - white-space: pre-wrap; - word-wrap: break-word; -} -.ql-editor > * { - cursor: text; -} -.ql-editor p, -.ql-editor ol, -.ql-editor ul, -.ql-editor pre, -.ql-editor blockquote, -.ql-editor h1, -.ql-editor h2, -.ql-editor h3, -.ql-editor h4, -.ql-editor h5, -.ql-editor h6 { - margin: 0; - padding: 0; - counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; -} -.ql-editor ol, -.ql-editor ul { - padding-left: 1.5em; -} -.ql-editor ol > li, -.ql-editor ul > li { - list-style-type: none; -} -.ql-editor ul > li::before { - content: '\2022'; -} -.ql-editor ul[data-checked=true], -.ql-editor ul[data-checked=false] { - pointer-events: none; -} -.ql-editor ul[data-checked=true] > li *, -.ql-editor ul[data-checked=false] > li * { - pointer-events: all; -} -.ql-editor ul[data-checked=true] > li::before, -.ql-editor ul[data-checked=false] > li::before { - color: #777; - cursor: pointer; - pointer-events: all; -} -.ql-editor ul[data-checked=true] > li::before { - content: '\2611'; -} -.ql-editor ul[data-checked=false] > li::before { - content: '\2610'; -} -.ql-editor li::before { - display: inline-block; - white-space: nowrap; - width: 1.2em; -} -.ql-editor li:not(.ql-direction-rtl)::before { - margin-left: -1.5em; - margin-right: 0.3em; - text-align: right; -} -.ql-editor li.ql-direction-rtl::before { - margin-left: 0.3em; - margin-right: -1.5em; -} -.ql-editor ol li:not(.ql-direction-rtl), -.ql-editor ul li:not(.ql-direction-rtl) { - padding-left: 1.5em; -} -.ql-editor ol li.ql-direction-rtl, -.ql-editor ul li.ql-direction-rtl { - padding-right: 1.5em; -} -.ql-editor ol li { - counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; - counter-increment: list-0; -} -.ql-editor ol li:before { - content: counter(list-0, decimal) '. '; -} -.ql-editor ol li.ql-indent-1 { - counter-increment: list-1; -} -.ql-editor ol li.ql-indent-1:before { - content: counter(list-1, lower-alpha) '. '; -} -.ql-editor ol li.ql-indent-1 { - counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-2 { - counter-increment: list-2; -} -.ql-editor ol li.ql-indent-2:before { - content: counter(list-2, lower-roman) '. '; -} -.ql-editor ol li.ql-indent-2 { - counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-3 { - counter-increment: list-3; -} -.ql-editor ol li.ql-indent-3:before { - content: counter(list-3, decimal) '. '; -} -.ql-editor ol li.ql-indent-3 { - counter-reset: list-4 list-5 list-6 list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-4 { - counter-increment: list-4; -} -.ql-editor ol li.ql-indent-4:before { - content: counter(list-4, lower-alpha) '. '; -} -.ql-editor ol li.ql-indent-4 { - counter-reset: list-5 list-6 list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-5 { - counter-increment: list-5; -} -.ql-editor ol li.ql-indent-5:before { - content: counter(list-5, lower-roman) '. '; -} -.ql-editor ol li.ql-indent-5 { - counter-reset: list-6 list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-6 { - counter-increment: list-6; -} -.ql-editor ol li.ql-indent-6:before { - content: counter(list-6, decimal) '. '; -} -.ql-editor ol li.ql-indent-6 { - counter-reset: list-7 list-8 list-9; -} -.ql-editor ol li.ql-indent-7 { - counter-increment: list-7; -} -.ql-editor ol li.ql-indent-7:before { - content: counter(list-7, lower-alpha) '. '; -} -.ql-editor ol li.ql-indent-7 { - counter-reset: list-8 list-9; -} -.ql-editor ol li.ql-indent-8 { - counter-increment: list-8; -} -.ql-editor ol li.ql-indent-8:before { - content: counter(list-8, lower-roman) '. '; -} -.ql-editor ol li.ql-indent-8 { - counter-reset: list-9; -} -.ql-editor ol li.ql-indent-9 { - counter-increment: list-9; -} -.ql-editor ol li.ql-indent-9:before { - content: counter(list-9, decimal) '. '; -} -.ql-editor .ql-indent-1:not(.ql-direction-rtl) { - padding-left: 3em; -} -.ql-editor li.ql-indent-1:not(.ql-direction-rtl) { - padding-left: 4.5em; -} -.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right { - padding-right: 3em; -} -.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right { - padding-right: 4.5em; -} -.ql-editor .ql-indent-2:not(.ql-direction-rtl) { - padding-left: 6em; -} -.ql-editor li.ql-indent-2:not(.ql-direction-rtl) { - padding-left: 7.5em; -} -.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right { - padding-right: 6em; -} -.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right { - padding-right: 7.5em; -} -.ql-editor .ql-indent-3:not(.ql-direction-rtl) { - padding-left: 9em; -} -.ql-editor li.ql-indent-3:not(.ql-direction-rtl) { - padding-left: 10.5em; -} -.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right { - padding-right: 9em; -} -.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right { - padding-right: 10.5em; -} -.ql-editor .ql-indent-4:not(.ql-direction-rtl) { - padding-left: 12em; -} -.ql-editor li.ql-indent-4:not(.ql-direction-rtl) { - padding-left: 13.5em; -} -.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right { - padding-right: 12em; -} -.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right { - padding-right: 13.5em; -} -.ql-editor .ql-indent-5:not(.ql-direction-rtl) { - padding-left: 15em; -} -.ql-editor li.ql-indent-5:not(.ql-direction-rtl) { - padding-left: 16.5em; -} -.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right { - padding-right: 15em; -} -.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right { - padding-right: 16.5em; -} -.ql-editor .ql-indent-6:not(.ql-direction-rtl) { - padding-left: 18em; -} -.ql-editor li.ql-indent-6:not(.ql-direction-rtl) { - padding-left: 19.5em; -} -.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right { - padding-right: 18em; -} -.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right { - padding-right: 19.5em; -} -.ql-editor .ql-indent-7:not(.ql-direction-rtl) { - padding-left: 21em; -} -.ql-editor li.ql-indent-7:not(.ql-direction-rtl) { - padding-left: 22.5em; -} -.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right { - padding-right: 21em; -} -.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right { - padding-right: 22.5em; -} -.ql-editor .ql-indent-8:not(.ql-direction-rtl) { - padding-left: 24em; -} -.ql-editor li.ql-indent-8:not(.ql-direction-rtl) { - padding-left: 25.5em; -} -.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right { - padding-right: 24em; -} -.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right { - padding-right: 25.5em; -} -.ql-editor .ql-indent-9:not(.ql-direction-rtl) { - padding-left: 27em; -} -.ql-editor li.ql-indent-9:not(.ql-direction-rtl) { - padding-left: 28.5em; -} -.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right { - padding-right: 27em; -} -.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right { - padding-right: 28.5em; -} -.ql-editor .ql-video { - display: block; - max-width: 100%; -} -.ql-editor .ql-video.ql-align-center { - margin: 0 auto; -} -.ql-editor .ql-video.ql-align-right { - margin: 0 0 0 auto; -} -.ql-editor .ql-bg-black { - background-color: #000; -} -.ql-editor .ql-bg-red { - background-color: #e60000; -} -.ql-editor .ql-bg-orange { - background-color: #f90; -} -.ql-editor .ql-bg-yellow { - background-color: #ff0; -} -.ql-editor .ql-bg-green { - background-color: #008a00; -} -.ql-editor .ql-bg-blue { - background-color: #06c; -} -.ql-editor .ql-bg-purple { - background-color: #93f; -} -.ql-editor .ql-color-white { - color: #fff; -} -.ql-editor .ql-color-red { - color: #e60000; -} -.ql-editor .ql-color-orange { - color: #f90; -} -.ql-editor .ql-color-yellow { - color: #ff0; -} -.ql-editor .ql-color-green { - color: #008a00; -} -.ql-editor .ql-color-blue { - color: #06c; -} -.ql-editor .ql-color-purple { - color: #93f; -} -.ql-editor .ql-font-serif { - font-family: Georgia, Times New Roman, serif; -} -.ql-editor .ql-font-monospace { - font-family: Monaco, Courier New, monospace; -} -.ql-editor .ql-size-small { - font-size: 0.75em; -} -.ql-editor .ql-size-large { - font-size: 1.5em; -} -.ql-editor .ql-size-huge { - font-size: 2.5em; -} -.ql-editor .ql-direction-rtl { - direction: rtl; - text-align: inherit; -} -.ql-editor .ql-align-center { - text-align: center; -} -.ql-editor .ql-align-justify { - text-align: justify; -} -.ql-editor .ql-align-right { - text-align: right; -} -.ql-editor.ql-blank::before { - color: rgba(0,0,0,0.6); - content: attr(data-placeholder); - font-style: italic; - left: 15px; - pointer-events: none; - position: absolute; - right: 15px; -} -.ql-snow.ql-toolbar:after, -.ql-snow .ql-toolbar:after { - clear: both; - content: ''; - display: table; -} -.ql-snow.ql-toolbar button, -.ql-snow .ql-toolbar button { - background: none; - border: none; - cursor: pointer; - display: inline-block; - float: left; - height: 24px; - padding: 3px 5px; - width: 28px; -} -.ql-snow.ql-toolbar button svg, -.ql-snow .ql-toolbar button svg { - float: left; - height: 100%; -} -.ql-snow.ql-toolbar button:active:hover, -.ql-snow .ql-toolbar button:active:hover { - outline: none; -} -.ql-snow.ql-toolbar input.ql-image[type=file], -.ql-snow .ql-toolbar input.ql-image[type=file] { - display: none; -} -.ql-snow.ql-toolbar button:hover, -.ql-snow .ql-toolbar button:hover, -.ql-snow.ql-toolbar button:focus, -.ql-snow .ql-toolbar button:focus, -.ql-snow.ql-toolbar button.ql-active, -.ql-snow .ql-toolbar button.ql-active, -.ql-snow.ql-toolbar .ql-picker-label:hover, -.ql-snow .ql-toolbar .ql-picker-label:hover, -.ql-snow.ql-toolbar .ql-picker-label.ql-active, -.ql-snow .ql-toolbar .ql-picker-label.ql-active, -.ql-snow.ql-toolbar .ql-picker-item:hover, -.ql-snow .ql-toolbar .ql-picker-item:hover, -.ql-snow.ql-toolbar .ql-picker-item.ql-selected, -.ql-snow .ql-toolbar .ql-picker-item.ql-selected { - color: #06c; -} -.ql-snow.ql-toolbar button:hover .ql-fill, -.ql-snow .ql-toolbar button:hover .ql-fill, -.ql-snow.ql-toolbar button:focus .ql-fill, -.ql-snow .ql-toolbar button:focus .ql-fill, -.ql-snow.ql-toolbar button.ql-active .ql-fill, -.ql-snow .ql-toolbar button.ql-active .ql-fill, -.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill, -.ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill, -.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill, -.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill, -.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill, -.ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill, -.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill, -.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill, -.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill, -.ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill, -.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill, -.ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill, -.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill, -.ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill, -.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill, -.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill, -.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill, -.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill, -.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill, -.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill, -.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill, -.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill { - fill: #06c; -} -.ql-snow.ql-toolbar button:hover .ql-stroke, -.ql-snow .ql-toolbar button:hover .ql-stroke, -.ql-snow.ql-toolbar button:focus .ql-stroke, -.ql-snow .ql-toolbar button:focus .ql-stroke, -.ql-snow.ql-toolbar button.ql-active .ql-stroke, -.ql-snow .ql-toolbar button.ql-active .ql-stroke, -.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke, -.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke, -.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke, -.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke, -.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke, -.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke, -.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke, -.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke, -.ql-snow.ql-toolbar button:hover .ql-stroke-miter, -.ql-snow .ql-toolbar button:hover .ql-stroke-miter, -.ql-snow.ql-toolbar button:focus .ql-stroke-miter, -.ql-snow .ql-toolbar button:focus .ql-stroke-miter, -.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter, -.ql-snow .ql-toolbar button.ql-active .ql-stroke-miter, -.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter, -.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter, -.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter, -.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter, -.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter, -.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter, -.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter, -.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter { - stroke: #06c; -} -@media (pointer: coarse) { - .ql-snow.ql-toolbar button:hover:not(.ql-active), - .ql-snow .ql-toolbar button:hover:not(.ql-active) { - color: #444; - } - .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-fill, - .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-fill, - .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill, - .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill { - fill: #444; - } - .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke, - .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke, - .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter, - .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter { - stroke: #444; - } -} -.ql-snow { - box-sizing: border-box; -} -.ql-snow * { - box-sizing: border-box; -} -.ql-snow .ql-hidden { - display: none; -} -.ql-snow .ql-out-bottom, -.ql-snow .ql-out-top { - visibility: hidden; -} -.ql-snow .ql-tooltip { - position: absolute; - transform: translateY(10px); -} -.ql-snow .ql-tooltip a { - cursor: pointer; - text-decoration: none; -} -.ql-snow .ql-tooltip.ql-flip { - transform: translateY(-10px); -} -.ql-snow .ql-formats { - display: inline-block; - vertical-align: middle; -} -.ql-snow .ql-formats:after { - clear: both; - content: ''; - display: table; -} -.ql-snow .ql-stroke { - fill: none; - stroke: #444; - stroke-linecap: round; - stroke-linejoin: round; - stroke-width: 2; -} -.ql-snow .ql-stroke-miter { - fill: none; - stroke: #444; - stroke-miterlimit: 10; - stroke-width: 2; -} -.ql-snow .ql-fill, -.ql-snow .ql-stroke.ql-fill { - fill: #444; -} -.ql-snow .ql-empty { - fill: none; -} -.ql-snow .ql-even { - fill-rule: evenodd; -} -.ql-snow .ql-thin, -.ql-snow .ql-stroke.ql-thin { - stroke-width: 1; -} -.ql-snow .ql-transparent { - opacity: 0.4; -} -.ql-snow .ql-direction svg:last-child { - display: none; -} -.ql-snow .ql-direction.ql-active svg:last-child { - display: inline; -} -.ql-snow .ql-direction.ql-active svg:first-child { - display: none; -} -.ql-snow .ql-editor h1 { - font-size: 2em; -} -.ql-snow .ql-editor h2 { - font-size: 1.5em; -} -.ql-snow .ql-editor h3 { - font-size: 1.17em; -} -.ql-snow .ql-editor h4 { - font-size: 1em; -} -.ql-snow .ql-editor h5 { - font-size: 0.83em; -} -.ql-snow .ql-editor h6 { - font-size: 0.67em; -} -.ql-snow .ql-editor a { - text-decoration: underline; -} -.ql-snow .ql-editor blockquote { - border-left: 4px solid #ccc; - margin-bottom: 5px; - margin-top: 5px; - padding-left: 16px; -} -.ql-snow .ql-editor code, -.ql-snow .ql-editor pre { - background-color: #f0f0f0; - border-radius: 3px; -} -.ql-snow .ql-editor pre { - white-space: pre-wrap; - margin-bottom: 5px; - margin-top: 5px; - padding: 5px 10px; -} -.ql-snow .ql-editor code { - font-size: 85%; - padding: 2px 4px; -} -.ql-snow .ql-editor pre.ql-syntax { - background-color: #23241f; - color: #f8f8f2; - overflow: visible; -} -.ql-snow .ql-editor img { - max-width: 100%; -} -.ql-snow .ql-picker { - color: #444; - display: inline-block; - float: left; - font-size: 14px; - font-weight: 500; - height: 24px; - position: relative; - vertical-align: middle; -} -.ql-snow .ql-picker-label { - cursor: pointer; - display: inline-block; - height: 100%; - padding-left: 8px; - padding-right: 2px; - position: relative; - width: 100%; -} -.ql-snow .ql-picker-label::before { - display: inline-block; - line-height: 22px; -} -.ql-snow .ql-picker-options { - background-color: #fff; - display: none; - min-width: 100%; - padding: 4px 8px; - position: absolute; - white-space: nowrap; -} -.ql-snow .ql-picker-options .ql-picker-item { - cursor: pointer; - display: block; - padding-bottom: 5px; - padding-top: 5px; -} -.ql-snow .ql-picker.ql-expanded .ql-picker-label { - color: #ccc; - z-index: 2; -} -.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill { - fill: #ccc; -} -.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke { - stroke: #ccc; -} -.ql-snow .ql-picker.ql-expanded .ql-picker-options { - display: block; - margin-top: -1px; - top: 100%; - z-index: 1; -} -.ql-snow .ql-color-picker, -.ql-snow .ql-icon-picker { - width: 28px; -} -.ql-snow .ql-color-picker .ql-picker-label, -.ql-snow .ql-icon-picker .ql-picker-label { - padding: 2px 4px; -} -.ql-snow .ql-color-picker .ql-picker-label svg, -.ql-snow .ql-icon-picker .ql-picker-label svg { - right: 4px; -} -.ql-snow .ql-icon-picker .ql-picker-options { - padding: 4px 0px; -} -.ql-snow .ql-icon-picker .ql-picker-item { - height: 24px; - width: 24px; - padding: 2px 4px; -} -.ql-snow .ql-color-picker .ql-picker-options { - padding: 3px 5px; - width: 152px; -} -.ql-snow .ql-color-picker .ql-picker-item { - border: 1px solid transparent; - float: left; - height: 16px; - margin: 2px; - padding: 0px; - width: 16px; -} -.ql-snow .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg { - position: absolute; - margin-top: -9px; - right: 0; - top: 50%; - width: 18px; -} -.ql-snow .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before, -.ql-snow .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before, -.ql-snow .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before, -.ql-snow .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before, -.ql-snow .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before, -.ql-snow .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before { - content: attr(data-label); -} -.ql-snow .ql-picker.ql-header { - width: 98px; -} -.ql-snow .ql-picker.ql-header .ql-picker-label::before, -.ql-snow .ql-picker.ql-header .ql-picker-item::before { - content: 'Normal'; -} -.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { - content: 'Heading 1'; -} -.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { - content: 'Heading 2'; -} -.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { - content: 'Heading 3'; -} -.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { - content: 'Heading 4'; -} -.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { - content: 'Heading 5'; -} -.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { - content: 'Heading 6'; -} -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { - font-size: 2em; -} -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { - font-size: 1.5em; -} -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { - font-size: 1.17em; -} -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { - font-size: 1em; -} -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { - font-size: 0.83em; -} -.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { - font-size: 0.67em; -} -.ql-snow .ql-picker.ql-font { - width: 108px; -} -.ql-snow .ql-picker.ql-font .ql-picker-label::before, -.ql-snow .ql-picker.ql-font .ql-picker-item::before { - content: 'Sans Serif'; -} -.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before, -.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before { - content: 'Serif'; -} -.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before, -.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before { - content: 'Monospace'; -} -.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before { - font-family: Georgia, Times New Roman, serif; -} -.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before { - font-family: Monaco, Courier New, monospace; -} -.ql-snow .ql-picker.ql-size { - width: 98px; -} -.ql-snow .ql-picker.ql-size .ql-picker-label::before, -.ql-snow .ql-picker.ql-size .ql-picker-item::before { - content: 'Normal'; -} -.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before, -.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before { - content: 'Small'; -} -.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before, -.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before { - content: 'Large'; -} -.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before, -.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before { - content: 'Huge'; -} -.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before { - font-size: 10px; -} -.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before { - font-size: 18px; -} -.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before { - font-size: 32px; -} -.ql-snow .ql-color-picker.ql-background .ql-picker-item { - background-color: #fff; -} -.ql-snow .ql-color-picker.ql-color .ql-picker-item { - background-color: #000; -} -.ql-toolbar.ql-snow { - border: 1px solid #ccc; - box-sizing: border-box; - font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; - padding: 8px; -} -.ql-toolbar.ql-snow .ql-formats { - margin-right: 15px; -} -.ql-toolbar.ql-snow .ql-picker-label { - border: 1px solid transparent; -} -.ql-toolbar.ql-snow .ql-picker-options { - border: 1px solid transparent; - box-shadow: rgba(0,0,0,0.2) 0 2px 8px; -} -.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label { - border-color: #ccc; -} -.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options { - border-color: #ccc; -} -.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item.ql-selected, -.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item:hover { - border-color: #000; -} -.ql-toolbar.ql-snow + .ql-container.ql-snow { - border-top: 0px; -} -.ql-snow .ql-tooltip { - background-color: #fff; - border: 1px solid #ccc; - box-shadow: 0px 0px 5px #ddd; - color: #444; - padding: 5px 12px; - white-space: nowrap; -} -.ql-snow .ql-tooltip::before { - content: "Visit URL:"; - line-height: 26px; - margin-right: 8px; -} -.ql-snow .ql-tooltip input[type=text] { - display: none; - border: 1px solid #ccc; - font-size: 13px; - height: 26px; - margin: 0px; - padding: 3px 5px; - width: 170px; -} -.ql-snow .ql-tooltip a.ql-preview { - display: inline-block; - max-width: 200px; - overflow-x: hidden; - text-overflow: ellipsis; - vertical-align: top; -} -.ql-snow .ql-tooltip a.ql-action::after { - border-right: 1px solid #ccc; - content: 'Edit'; - margin-left: 16px; - padding-right: 8px; -} -.ql-snow .ql-tooltip a.ql-remove::before { - content: 'Remove'; - margin-left: 8px; -} -.ql-snow .ql-tooltip a { - line-height: 26px; -} -.ql-snow .ql-tooltip.ql-editing a.ql-preview, -.ql-snow .ql-tooltip.ql-editing a.ql-remove { - display: none; -} -.ql-snow .ql-tooltip.ql-editing input[type=text] { - display: inline-block; -} -.ql-snow .ql-tooltip.ql-editing a.ql-action::after { - border-right: 0px; - content: 'Save'; - padding-right: 0px; -} -.ql-snow .ql-tooltip[data-mode=link]::before { - content: "Enter link:"; -} -.ql-snow .ql-tooltip[data-mode=formula]::before { - content: "Enter formula:"; -} -.ql-snow .ql-tooltip[data-mode=video]::before { - content: "Enter video:"; -} -.ql-snow a { - color: #06c; -} -.ql-container.ql-snow { - border: 1px solid #ccc; -} \ No newline at end of file diff --git a/Oqtane.Client/wwwroot/favicon.ico b/Oqtane.Client/wwwroot/favicon.ico deleted file mode 100644 index 0e569015cd5b8ef24cb0b2b28fe9e49e5fe34a87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5430 zcmc&&Ye-{96rRMG#28InV|*K*Q4vE3F^KqnA@Ps+0I5*-M`@)LtYK|oDar0q*HV@( zl%+okQYfXA{t#KDl+qt=TUzOlZ7F40N};x07Lig?yDTD-+wUY!xS8CWYusQ54l{G+ zyyl!U=gwTlbWG3ec80#5Eg2YdGsf!c6Z21izYSa)a2SFjfk$`dHa9nCsI06ETCLV= zg@uJTDk>_bCMPG2XA#iY*yuKyO!q-6$pN^Bb#-;#OfuTr+bcp!C*Mf&(A-W z%@5peca55i;^N{rIjzlRyE!^KY8@XRHv@k~r_;slDJUqoqsGte2F6PXsB_&hDPk>yytVELuH#Rn=r#QlAkNf)i%$aD_*5HQ__WzdJx)#2*wbj|u z(o&y^E*4?z$i-OH#@O%QG4`hn{{4Zmf8N3R2aKKk1^_M56G#M7qRx*dGT(|Z0 zb>raRptYx`#~KcY&*jH#Z9@ICAn%tP4#$_syT1`fI+GH{hrsP3pI$>fbYv^{EcDII z&Cj9MJOVAes}8Uy3fe2n%gctd$kn0=g+hAl`3|p>nRLt7P$M=G7mJpBt##;rTP`=7 zad~<9ryCm^dMzDl>-g>rqedzE8}*uM>0hWp2RO?oYIY$OOKS49@TH}t@meL9O>scI z{BU@9xNU1|E50|>-Ws%9u=PkzOJn52BhY3Yhd?0EuM~59t3zsb!>%CtMYI9m{>y4S zYWT9UviNsVE(>RDM~$B2LGzxRhsMBd^7XU!i0fyf9NaPHwfN!dfao{W>W5l<;yU8? zm8gGye%>$AiSy~{X{*IzNr?eu9f)*VbI3^3-__Nnsefi><`T|BihkU2veu8>{}?u= z$U*L1)v~*(scBl|!#Um6;uF_V%U==YU?2Oqf2r1Y+{X;an;%6!8U6SHI4kD*l;(eyIqRUC>On6Z$Obj1Bt!7t0eE#$0^)6;~v6c?9!__j-sAC%V_#|Y9rbB*b#*mf ze`{-N=`NP$clhMR^XL?LkoUKgR?6c`xR4PN#DdwIFUE(L75V8X7Vp_tV~P z6bIbF9BXT9M#MQ;FA%f+-rnA1e4a%&=k4t5^ee^Tv`(<@?(R0MKcRfb*^T>!^X96D zPw5<|#TobMS<->?7s6TpJXiWeIe5PmlW&1fv4PG=Fc@^MtgIODo~aP|ayiF6gK|1W zKh1I9Q>{z6)K70X+_!%bZNT^HaAah}oJ(D5vfzW~Mg7PPssR_$@P;&^-X-4xIETl0 zOIBTo@>3<%)zw!qjOPJz{-cZ3O}cQ;sKHzQn9jT3?;pHq{iF{y?=Euj*X`}?oV>NN z-2>{*G~V&5|826B_kYk!)Q((|v}cl}YdZRM5QkXaP9$cFMow(effLtJ3IIP4GesjY p(_Tc%+mWQ}*8%q=Y3FOmc?s~kBt-`ja!K|H0p>A+NCKop`wt!H9{>OV diff --git a/Oqtane.Client/wwwroot/images/checked.png b/Oqtane.Client/wwwroot/images/checked.png deleted file mode 100644 index a4100c70a27c17c9da8ae3001183f6c335a2bdda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 427 zcmV;c0aX5pP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^XO*0N*(00A3GL_t(IPh-&ZF--E^pZd%FP~v}5u-A^H zkJ>)QNem#(UZ+z3lY>Ee8NjAtkt7C#^pa$N&*}94-lx*we3A?RY25zm(ErXmv*2PR z88G4g!v9aczxW?^E*~yNv;kRH8vnol_33}%)pitdq6`Q=oBRLq_vim-JY0?;hhad> z`BIcX0%4H9kG?zi-xnBw$l@T3VZfzNH~;T?edK@OnQSn>|K0+i?Jxd^oXvx)LB<#c zM4m7DfA+)G|JOg?{a<@?!v9adzx*%0)`6l48Dkg#!l1Zb`+VpBU;lpppN_@f$QY{u zAPfqJl#A8>ebB-K9pf~BSPVA6?@;Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=KugC=K=Jss|El70=7v+K~y+TV`M-A zCDjcauU@|LU}R*h`S9_R@vmRMz~XFdtPFwz0%!mJ{oBIJ%d=zJgzg_;2_S$Q(A?Rp zcJ|zbn%}?w#QyvD4=RcZn3$Lt1O)|7OG!#3b+y!A0f~VP0BOE{|U_1sNIt z0~LPx@%_irZ{NSa7ZBk0|M>Bf=Kufy!RooWxt>c)OF2yJYQ4n>a`v4&cW3?l`70JA z$;!&gAR#W=J!?vT8Hf)9X*oqvkDol92xN+Y_(0bz77-RoV*>dZ2F- z$y^3Tp=E1*)mo7FnR6HHe*E|$2v!6HKyNTWJcI?Xv$Nln5ErvwvvmIZkmy8SU?A@P z@#E(lkS1in#LUbLWuk-sKiN4r99p~k1Azgv0vH1>Pzg9-VPRol5)u?V3+1B#V9e+} zc<^ZPeIWk+R#=>f)8 z3zM*r;0~Zkr=j9-08I1Gv(l12f-o>9c0q;VK!BhBw2X}O4(8J*j{OM?4ml1?6oJ4P z;f2aG{QU7#&euOsN?u8g@6FqH*+3xyhzukttE(!9v^Um1gJ-7tmJXe}_a4l9_x^(^ zdPu_n$k`y9C8Z>idRiM0nF$8sQ?ogM0p`KU!BGP=$QbBHusA5Rftl;<*RNk&6crVA f05wCiGXnzviHabpJT*b900000NkvXXu0mjfSXFW4 diff --git a/Oqtane.Client/wwwroot/images/null.png b/Oqtane.Client/wwwroot/images/null.png deleted file mode 100644 index d0f50939ed0601172010cd4a9fe24e40deaf2cfa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6;>1s;*b z3=BeG%-Ex}lN~51S>hT|5}cn_Ql40p%21G)nOCBhms+A=qGzCIXnEz@rWBweJx>?M z5DWk0AOHW`JJ;6!tuHe0(2p?Q$*`pSN<$#C8}EW?j1E#c9F{-@|9Ke7MVA=K*OvML PbuxIm`njxgN@xNACvh`; diff --git a/Oqtane.Client/wwwroot/images/unchecked.png b/Oqtane.Client/wwwroot/images/unchecked.png deleted file mode 100644 index 566e60a8e19044766052c5859586b5bfec830853..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 438 zcmV;n0ZIOeP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^XO*0N*(00AaRL_t(IPo0xJYr;SPhVyq?{3oKAh`MKR zS2w>F{YyHCIJtC-v<^a3D3W&YE3sgpO5f%^@n~`vFdd%ZKHleY?-9d%c(k2T>H8(0 zM;4w6g}}whAsCPGF&M}>MZoaI_u=(= za*i;3oXv1rEZ|lua$XS-hR?elylz*F-EtYn=@ci&!CS2s+p852hA;a)ynY|SWO5Bx ztIEDq0b%$j3=ytY+Av8K@UwX`!{^N=wOFk{6n49Fbh0S+aFCt+MG?rh6L%J!P?C5Z zMc|J{;;E8t6Y)CRE!K^OoKpm!!y(+1!pOSS!h5|g$BG~;SAl5;y2X2VjgfVE_OC07*qoM6N<$g1LygEC2ui diff --git a/Oqtane.Client/wwwroot/js/interop.js b/Oqtane.Client/wwwroot/js/interop.js deleted file mode 100644 index ec289bbe..00000000 --- a/Oqtane.Client/wwwroot/js/interop.js +++ /dev/null @@ -1,259 +0,0 @@ -window.interop = { - setCookie: function (name, value, days) { - var d = new Date(); - d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000)); - var expires = "expires=" + d.toUTCString(); - document.cookie = name + "=" + value + ";" + expires + ";path=/"; - }, - getCookie: function (name) { - name = name + "="; - var decodedCookie = decodeURIComponent(document.cookie); - var ca = decodedCookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0) === ' ') { - c = c.substring(1); - } - if (c.indexOf(name) === 0) { - return c.substring(name.length, c.length); - } - } - return ""; - }, - updateTitle: function (title) { - if (document.title !== title) { - document.title = title; - } - }, - includeMeta: function (id, attribute, name, content) { - var meta; - if (id !== "") { - meta = document.getElementById(id); - } - else { - meta = document.querySelector("meta[" + attribute + "=\"" + CSS.escape(name) + "\"]"); - } - if (meta === null) { - meta = document.createElement("meta"); - meta.setAttribute(attribute, name); - if (id !== "") { - meta.id = id; - } - meta.content = content; - document.head.appendChild(meta); - } - else { - if (meta.content !== content) { - meta.setAttribute("content", content); - } - } - }, - includeLink: function (id, rel, url, type) { - var link; - if (id !== "") { - link = document.getElementById(id); - } - else { - link = document.querySelector("link[href=\"" + CSS.escape(url) + "\"]"); - } - if (link === null) { - link = document.createElement("link"); - if (id !== "") { - link.id = id; - } - link.rel = rel; - link.href = url; - if (type !== "") { - link.type = type; - } - document.head.appendChild(link); - } - else { - if (link.rel !== rel) { - link.setAttribute('rel', rel); - } - if (link.href !== url) { - link.setAttribute('href', url); - } - if (type !== "" && link.type !== type) { - link.setAttribute('type', type); - } - } - }, - includeScript: function (id, src, content, location) { - var script; - if (id !== "") { - script = document.getElementById(id); - } - if (script === null) { - script = document.createElement("script"); - if (id !== "") { - script.id = id; - } - if (src !== "") { - script.src = src; - } - else { - script.innerHTML = content; - } - if (location === 'head') { - document.head.appendChild(script); - } - if (location === 'body') { - document.body.appendChild(script); - } - } - else { - if (src !== "") { - if (script.src !== src) { - script.src = src; - } - } - else { - if (script.innerHTML !== content) { - script.innerHTML = content; - } - } - } - }, - getElementByName: function (name) { - var elements = document.getElementsByName(name); - if (elements.length) { - return elements[0].value; - } else { - return ""; - } - }, - submitForm: function (path, fields) { - const form = document.createElement('form'); - form.method = 'post'; - form.action = path; - - for (const key in fields) { - if (fields.hasOwnProperty(key)) { - const hiddenField = document.createElement('input'); - hiddenField.type = 'hidden'; - hiddenField.name = key; - hiddenField.value = fields[key]; - form.appendChild(hiddenField); - } - } - - document.body.appendChild(form); - form.submit(); - }, - getFiles: function (id) { - var files = []; - var fileinput = document.getElementById(id); - if (fileinput !== null) { - for (var i = 0; i < fileinput.files.length; i++) { - files.push(fileinput.files[i].name); - } - } - return files; - }, - uploadFiles: function (posturl, folder, id) { - var files = document.getElementById(id + 'FileInput').files; - var progressinfo = document.getElementById(id + 'ProgressInfo'); - var progressbar = document.getElementById(id + 'ProgressBar'); - var filename = ''; - - for (var i = 0; i < files.length; i++) { - var FileChunk = []; - var file = files[i]; - var MaxFileSizeMB = 1; - var BufferChunkSize = MaxFileSizeMB * (1024 * 1024); - var FileStreamPos = 0; - var EndPos = BufferChunkSize; - var Size = file.size; - - progressbar.setAttribute("style", "visibility: visible;"); - - if (files.length > 1) { - filename = file.name; - } - - while (FileStreamPos < Size) { - FileChunk.push(file.slice(FileStreamPos, EndPos)); - FileStreamPos = EndPos; - EndPos = FileStreamPos + BufferChunkSize; - } - - var TotalParts = FileChunk.length; - var PartCount = 0; - - while (Chunk = FileChunk.shift()) { - PartCount++; - var FileName = file.name + ".part_" + PartCount + "_" + TotalParts; - - var data = new FormData(); - data.append('folder', folder); - data.append('file', Chunk, FileName); - var request = new XMLHttpRequest(); - request.open('POST', posturl, true); - request.upload.onloadstart = function (e) { - progressbar.value = 0; - progressinfo.innerHTML = filename + ' 0%'; - }; - request.upload.onprogress = function (e) { - var percent = Math.ceil((e.loaded / e.total) * 100); - progressbar.value = (percent / 100); - progressinfo.innerHTML = filename + '[' + PartCount + '] ' + percent + '%'; - }; - request.upload.onloadend = function (e) { - progressbar.value = 1; - progressinfo.innerHTML = filename + ' 100%'; - }; - request.send(data); - } - } - }, - createQuill: function ( - quillElement, toolBar, readOnly, - placeholder, theme, debugLevel) { - - Quill.register('modules/blotFormatter', QuillBlotFormatter.default); - - var options = { - debug: debugLevel, - modules: { - toolbar: toolBar, - blotFormatter: {} - }, - placeholder: placeholder, - readOnly: readOnly, - theme: theme - }; - - new Quill(quillElement, options); - }, - getQuillContent: function (editorElement) { - return JSON.stringify(editorElement.__quill.getContents()); - }, - getQuillText: function (editorElement) { - return editorElement.__quill.getText(); - }, - getQuillHTML: function (editorElement) { - return editorElement.__quill.root.innerHTML; - }, - loadQuillContent: function (editorElement, editorContent) { - return editorElement.__quill.root.innerHTML = editorContent; - }, - enableQuillEditor: function (editorElement, mode) { - editorElement.__quill.enable(mode); - }, - insertQuillImage: function (quillElement, imageURL) { - var Delta = Quill.import('delta'); - editorIndex = 0; - - if (quillElement.__quill.getSelection() !== null) { - editorIndex = quillElement.__quill.getSelection().index; - } - - return quillElement.__quill.updateContents( - new Delta() - .retain(editorIndex) - .insert({ image: imageURL }, - { alt: imageURL })); - } -}; diff --git a/Oqtane.Client/wwwroot/js/quill-blot-formatter.min.js b/Oqtane.Client/wwwroot/js/quill-blot-formatter.min.js deleted file mode 100644 index ba11f7a7..00000000 --- a/Oqtane.Client/wwwroot/js/quill-blot-formatter.min.js +++ /dev/null @@ -1 +0,0 @@ -!function (e, t) { "object" == typeof exports && "object" == typeof module ? module.exports = t(require("Quill")) : "function" == typeof define && define.amd ? define(["Quill"], t) : "object" == typeof exports ? exports.QuillBlotFormatter = t(require("Quill")) : e.QuillBlotFormatter = t(e.Quill) }(this, function (e) { return function (e) { function t(r) { if (n[r]) return n[r].exports; var o = n[r] = { i: r, l: !1, exports: {} }; return e[r].call(o.exports, o, o.exports, t), o.l = !0, o.exports } var n = {}; return t.m = e, t.c = n, t.d = function (e, n, r) { t.o(e, n) || Object.defineProperty(e, n, { configurable: !1, enumerable: !0, get: r }) }, t.n = function (e) { var n = e && e.__esModule ? function () { return e.default } : function () { return e }; return t.d(n, "a", n), n }, t.o = function (e, t) { return Object.prototype.hasOwnProperty.call(e, t) }, t.p = "", t(t.s = 14) }([function (e, t, n) { "use strict"; function r(e) { return e && e.__esModule ? e : { default: e } } function o(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } Object.defineProperty(t, "__esModule", { value: !0 }); var i = function () { function e(e, t) { for (var n = 0; n < t.length; n++) { var r = t[n]; r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) } } return function (t, n, r) { return n && e(t.prototype, n), r && e(t, r), t } }(), a = n(15), l = r(a), u = n(4), s = r(u), c = n(1), f = (r(c), n(2)), p = (r(f), function (e, t) { return t }), d = function () { function e(t) { var n = this, r = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}; o(this, e), this.onClick = function () { n.hide() }, this.quill = t, this.options = (0, l.default)(s.default, r, { arrayMerge: p }), this.currentSpec = null, this.actions = [], this.overlay = document.createElement("div"), this.overlay.classList.add(this.options.overlay.className), this.options.overlay.style && Object.assign(this.overlay.style, this.options.overlay.style), document.execCommand("enableObjectResizing", !1, "false"), this.quill.root.parentNode.style.position = this.quill.root.parentNode.style.position || "relative", this.quill.root.addEventListener("click", this.onClick), this.specs = this.options.specs.map(function (e) { return new e(n) }), this.specs.forEach(function (e) { return e.init() }) } return i(e, [{ key: "show", value: function (e) { this.currentSpec = e, this.currentSpec.setSelection(), this.setUserSelect("none"), this.quill.root.parentNode.appendChild(this.overlay), this.repositionOverlay(), this.createActions(e) } }, { key: "hide", value: function () { this.currentSpec && (this.currentSpec.onHide(), this.currentSpec = null, this.quill.root.parentNode.removeChild(this.overlay), this.overlay.style.setProperty("display", "none"), this.setUserSelect(""), this.destroyActions()) } }, { key: "update", value: function () { this.repositionOverlay(), this.actions.forEach(function (e) { return e.onUpdate() }) } }, { key: "createActions", value: function (e) { var t = this; this.actions = e.getActions().map(function (e) { var n = new e(t); return n.onCreate(), n }) } }, { key: "destroyActions", value: function () { this.actions.forEach(function (e) { return e.onDestroy() }), this.actions = [] } }, { key: "repositionOverlay", value: function () { if (this.currentSpec) { var e = this.currentSpec.getOverlayElement(); if (e) { var t = this.quill.root.parentNode, n = e.getBoundingClientRect(), r = t.getBoundingClientRect(); Object.assign(this.overlay.style, { display: "block", left: n.left - r.left - 1 + t.scrollLeft + "px", top: n.top - r.top + t.scrollTop + "px", width: n.width + "px", height: n.height + "px" }) } } } }, { key: "setUserSelect", value: function (e) { var t = this;["userSelect", "mozUserSelect", "webkitUserSelect", "msUserSelect"].forEach(function (n) { t.quill.root.style.setProperty(n, e), document.documentElement && document.documentElement.style.setProperty(n, e) }) } }]), e }(); t.default = d }, function (e, t, n) { "use strict"; function r(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } Object.defineProperty(t, "__esModule", { value: !0 }); var o = function () { function e(e, t) { for (var n = 0; n < t.length; n++) { var r = t[n]; r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) } } return function (t, n, r) { return n && e(t.prototype, n), r && e(t, r), t } }(), i = n(0), a = (function (e) { e && e.__esModule }(i), function () { function e(t) { r(this, e), this.formatter = t } return o(e, [{ key: "onCreate", value: function () { } }, { key: "onDestroy", value: function () { } }, { key: "onUpdate", value: function () { } }]), e }()); t.default = a }, function (e, t, n) { "use strict"; function r(e) { return e && e.__esModule ? e : { default: e } } function o(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } Object.defineProperty(t, "__esModule", { value: !0 }); var i = function () { function e(e, t) { for (var n = 0; n < t.length; n++) { var r = t[n]; r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) } } return function (t, n, r) { return n && e(t.prototype, n), r && e(t, r), t } }(), a = n(0), l = (r(a), n(1)), u = (r(l), n(5)), s = r(u), c = n(9), f = r(c), p = n(10), d = r(p), y = function () { function e(t) { o(this, e), this.formatter = t } return i(e, [{ key: "init", value: function () { } }, { key: "getActions", value: function () { return [s.default, f.default, d.default] } }, { key: "getTargetElement", value: function () { return null } }, { key: "getOverlayElement", value: function () { return this.getTargetElement() } }, { key: "setSelection", value: function () { this.formatter.quill.setSelection(null) } }, { key: "onHide", value: function () { } }]), e }(); t.default = y }, function (e, t, n) { "use strict" }, function (e, t, n) { "use strict"; function r(e) { return e && e.__esModule ? e : { default: e } } Object.defineProperty(t, "__esModule", { value: !0 }); var o = n(2), i = (r(o), n(11)), a = r(i), l = n(12), u = r(l), s = { specs: [a.default, u.default], overlay: { className: "blot-formatter__overlay", style: { position: "absolute", boxSizing: "border-box", border: "1px dashed #444" } }, align: { attribute: "data-align", aligner: { applyStyle: !0 }, icons: { left: '\n \n \n \n \n \n ', center: '\n \n \n \n \n \n ', right: '\n \n \n \n \n \n ' }, toolbar: { allowDeselect: !0, mainClassName: "blot-formatter__toolbar", mainStyle: { position: "absolute", top: "-12px", right: "0", left: "0", height: "0", minWidth: "100px", font: "12px/1.0 Arial, Helvetica, sans-serif", textAlign: "center", color: "#333", boxSizing: "border-box", cursor: "default", zIndex: "1" }, buttonClassName: "blot-formatter__toolbar-button", addButtonSelectStyle: !0, buttonStyle: { display: "inline-block", width: "24px", height: "24px", background: "white", border: "1px solid #999", verticalAlign: "middle" }, svgStyle: { display: "inline-block", width: "24px", height: "24px", background: "white", border: "1px solid #999", verticalAlign: "middle" } } }, resize: { handleClassName: "blot-formatter__resize-handle", handleStyle: { position: "absolute", height: "12px", width: "12px", backgroundColor: "white", border: "1px solid #777", boxSizing: "border-box", opacity: "0.80" } } }; t.default = s }, function (e, t, n) { "use strict"; function r(e) { return e && e.__esModule ? e : { default: e } } function o(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } function i(e, t) { if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return !t || "object" != typeof t && "function" != typeof t ? e : t } function a(e, t) { if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); e.prototype = Object.create(t && t.prototype, { constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 } }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) } Object.defineProperty(t, "__esModule", { value: !0 }); var l = function () { function e(e, t) { for (var n = 0; n < t.length; n++) { var r = t[n]; r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) } } return function (t, n, r) { return n && e(t.prototype, n), r && e(t, r), t } }(), u = n(1), s = r(u), c = n(0), f = (r(c), n(6)), p = r(f), d = (n(3), n(7), n(8)), y = r(d), h = function (e) { function t(e) { o(this, t); var n = i(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e)); return n.aligner = new p.default(e.options.align), n.toolbar = new y.default, n } return a(t, e), l(t, [{ key: "onCreate", value: function () { var e = this.toolbar.create(this.formatter, this.aligner); this.formatter.overlay.appendChild(e) } }, { key: "onDestroy", value: function () { var e = this.toolbar.getElement(); e && (this.formatter.overlay.removeChild(e), this.toolbar.destroy()) } }]), t }(s.default); t.default = h }, function (e, t, n) { "use strict"; function r(e, t, n) { return t in e ? Object.defineProperty(e, t, { value: n, enumerable: !0, configurable: !0, writable: !0 }) : e[t] = n, e } function o(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } Object.defineProperty(t, "__esModule", { value: !0 }); var i = function () { function e(e, t) { for (var n = 0; n < t.length; n++) { var r = t[n]; r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) } } return function (t, n, r) { return n && e(t.prototype, n), r && e(t, r), t } }(), a = (n(3), "left"), l = "center", u = "right", s = function () { function e(t) { var n, i = this; o(this, e), this.applyStyle = t.aligner.applyStyle, this.alignAttribute = t.attribute, this.alignments = (n = {}, r(n, a, { name: a, icon: t.icons.left, apply: function (e) { i.setAlignment(e, a), i.setStyle(e, "inline", "left", "0 1em 1em 0") } }), r(n, l, { name: l, icon: t.icons.center, apply: function (e) { i.setAlignment(e, l), i.setStyle(e, "block", null, "auto") } }), r(n, u, { name: u, icon: t.icons.right, apply: function (e) { i.setAlignment(e, u), i.setStyle(e, "inline", "right", "0 0 1em 1em") } }), n) } return i(e, [{ key: "getAlignments", value: function () { var e = this; return Object.keys(this.alignments).map(function (t) { return e.alignments[t] }) } }, { key: "clear", value: function (e) { e.removeAttribute(this.alignAttribute), this.setStyle(e, null, null, null) } }, { key: "isAligned", value: function (e, t) { return e.getAttribute(this.alignAttribute) === t.name } }, { key: "setAlignment", value: function (e, t) { e.setAttribute(this.alignAttribute, t) } }, { key: "setStyle", value: function (e, t, n, r) { this.applyStyle && (e.style.setProperty("display", t), e.style.setProperty("float", n), e.style.setProperty("margin", r)) } }]), e }(); t.default = s }, function (e, t, n) { "use strict"; var r = (n(3), n(0)); !function (e) { e && e.__esModule }(r) }, function (e, t, n) { "use strict"; function r(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } Object.defineProperty(t, "__esModule", { value: !0 }); var o = function () { function e(e, t) { for (var n = 0; n < t.length; n++) { var r = t[n]; r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) } } return function (t, n, r) { return n && e(t.prototype, n), r && e(t, r), t } }(), i = (n(7), n(3), n(0)), a = (function (e) { e && e.__esModule }(i), function () { function e() { r(this, e), this.toolbar = null, this.buttons = [] } return o(e, [{ key: "create", value: function (e, t) { var n = document.createElement("div"); return n.classList.add(e.options.align.toolbar.mainClassName), this.addToolbarStyle(e, n), this.addButtons(e, n, t), this.toolbar = n, this.toolbar } }, { key: "destroy", value: function () { this.toolbar = null, this.buttons = [] } }, { key: "getElement", value: function () { return this.toolbar } }, { key: "addToolbarStyle", value: function (e, t) { e.options.align.toolbar.mainStyle && Object.assign(t.style, e.options.align.toolbar.mainStyle) } }, { key: "addButtonStyle", value: function (e, t, n) { n.options.align.toolbar.buttonStyle && (Object.assign(e.style, n.options.align.toolbar.buttonStyle), t > 0 && (e.style.borderLeftWidth = "0")), n.options.align.toolbar.svgStyle && Object.assign(e.children[0].style, n.options.align.toolbar.svgStyle) } }, { key: "addButtons", value: function (e, t, n) { var r = this; n.getAlignments().forEach(function (o, i) { var a = document.createElement("span"); a.classList.add(e.options.align.toolbar.buttonClassName), a.innerHTML = o.icon, a.addEventListener("click", function () { r.onButtonClick(a, e, o, n) }), r.preselectButton(a, o, e, n), r.addButtonStyle(a, i, e), r.buttons.push(a), t.appendChild(a) }) } }, { key: "preselectButton", value: function (e, t, n, r) { if (n.currentSpec) { var o = n.currentSpec.getTargetElement(); o && r.isAligned(o, t) && this.selectButton(n, e) } } }, { key: "onButtonClick", value: function (e, t, n, r) { if (t.currentSpec) { var o = t.currentSpec.getTargetElement(); o && this.clickButton(e, o, t, n, r) } } }, { key: "clickButton", value: function (e, t, n, r, o) { var i = this; this.buttons.forEach(function (e) { i.deselectButton(n, e) }), o.isAligned(t, r) ? n.options.align.toolbar.allowDeselect ? o.clear(t) : this.selectButton(n, e) : (this.selectButton(n, e), r.apply(t)), n.update() } }, { key: "selectButton", value: function (e, t) { t.classList.add("is-selected"), e.options.align.toolbar.addButtonSelectStyle && t.style.setProperty("filter", "invert(20%)") } }, { key: "deselectButton", value: function (e, t) { t.classList.remove("is-selected"), e.options.align.toolbar.addButtonSelectStyle && t.style.removeProperty("filter") } }]), e }()); t.default = a }, function (e, t, n) { "use strict"; function r(e) { return e && e.__esModule ? e : { default: e } } function o(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } function i(e, t) { if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return !t || "object" != typeof t && "function" != typeof t ? e : t } function a(e, t) { if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); e.prototype = Object.create(t && t.prototype, { constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 } }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) } Object.defineProperty(t, "__esModule", { value: !0 }); var l = function () { function e(e, t) { for (var n = 0; n < t.length; n++) { var r = t[n]; r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) } } return function (t, n, r) { return n && e(t.prototype, n), r && e(t, r), t } }(), u = n(1), s = r(u), c = n(0), f = (r(c), function (e) { function t(e) { o(this, t); var n = i(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e)); return n.onMouseDown = function (e) { if (e.target instanceof HTMLElement && (n.dragHandle = e.target, n.setCursor(n.dragHandle.style.cursor), n.formatter.currentSpec)) { var t = n.formatter.currentSpec.getTargetElement(); if (t) { var r = t.getBoundingClientRect(); n.dragStartX = e.clientX, n.preDragWidth = r.width, n.targetRatio = r.height / r.width, document.addEventListener("mousemove", n.onDrag), document.addEventListener("mouseup", n.onMouseUp) } } }, n.onDrag = function (e) { if (n.formatter.currentSpec) { var t = n.formatter.currentSpec.getTargetElement(); if (t) { var r = e.clientX - n.dragStartX, o = 0; o = n.dragHandle === n.topLeftHandle || n.dragHandle === n.bottomLeftHandle ? Math.round(n.preDragWidth - r) : Math.round(n.preDragWidth + r); var i = n.targetRatio * o; t.setAttribute("width", "" + o), t.setAttribute("height", "" + i), n.formatter.update() } } }, n.onMouseUp = function () { n.setCursor(""), document.removeEventListener("mousemove", n.onDrag), document.removeEventListener("mouseup", n.onMouseUp) }, n.topLeftHandle = n.createHandle("top-left", "nwse-resize"), n.topRightHandle = n.createHandle("top-right", "nesw-resize"), n.bottomRightHandle = n.createHandle("bottom-right", "nwse-resize"), n.bottomLeftHandle = n.createHandle("bottom-left", "nesw-resize"), n.dragHandle = null, n.dragStartX = 0, n.preDragWidth = 0, n.targetRatio = 0, n } return a(t, e), l(t, [{ key: "onCreate", value: function () { this.formatter.overlay.appendChild(this.topLeftHandle), this.formatter.overlay.appendChild(this.topRightHandle), this.formatter.overlay.appendChild(this.bottomRightHandle), this.formatter.overlay.appendChild(this.bottomLeftHandle), this.repositionHandles(this.formatter.options.resize.handleStyle) } }, { key: "onDestroy", value: function () { this.setCursor(""), this.formatter.overlay.removeChild(this.topLeftHandle), this.formatter.overlay.removeChild(this.topRightHandle), this.formatter.overlay.removeChild(this.bottomRightHandle), this.formatter.overlay.removeChild(this.bottomLeftHandle) } }, { key: "createHandle", value: function (e, t) { var n = document.createElement("div"); return n.classList.add(this.formatter.options.resize.handleClassName), n.setAttribute("data-position", e), n.style.cursor = t, this.formatter.options.resize.handleStyle && Object.assign(n.style, this.formatter.options.resize.handleStyle), n.addEventListener("mousedown", this.onMouseDown), n } }, { key: "repositionHandles", value: function (e) { var t = "0px", n = "0px"; e && (e.width && (t = -parseFloat(e.width) / 2 + "px"), e.height && (n = -parseFloat(e.height) / 2 + "px")), Object.assign(this.topLeftHandle.style, { left: t, top: n }), Object.assign(this.topRightHandle.style, { right: t, top: n }), Object.assign(this.bottomRightHandle.style, { right: t, bottom: n }), Object.assign(this.bottomLeftHandle.style, { left: t, bottom: n }) } }, { key: "setCursor", value: function (e) { if (document.body && (document.body.style.cursor = e), this.formatter.currentSpec) { var t = this.formatter.currentSpec.getOverlayElement(); t && (t.style.cursor = e) } } }]), t }(s.default)); t.default = f }, function (e, t, n) { "use strict"; function r(e) { return e && e.__esModule ? e : { default: e } } function o(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } function i(e, t) { if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return !t || "object" != typeof t && "function" != typeof t ? e : t } function a(e, t) { if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); e.prototype = Object.create(t && t.prototype, { constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 } }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) } Object.defineProperty(t, "__esModule", { value: !0 }); var l = function () { function e(e, t) { for (var n = 0; n < t.length; n++) { var r = t[n]; r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) } } return function (t, n, r) { return n && e(t.prototype, n), r && e(t, r), t } }(), u = n(16), s = r(u), c = n(1), f = r(c), p = function (e) { function t() { var e, n, r, a; o(this, t); for (var l = arguments.length, u = Array(l), c = 0; c < l; c++)u[c] = arguments[c]; return n = r = i(this, (e = t.__proto__ || Object.getPrototypeOf(t)).call.apply(e, [this].concat(u))), r.onKeyUp = function (e) { if (r.formatter.currentSpec && (46 === e.keyCode || 8 === e.keyCode)) { var t = s.default.find(r.formatter.currentSpec.getTargetElement()); t && t.deleteAt(0), r.formatter.hide() } }, a = n, i(r, a) } return a(t, e), l(t, [{ key: "onCreate", value: function () { document.addEventListener("keyup", this.onKeyUp, !0), this.formatter.quill.root.addEventListener("input", this.onKeyUp, !0) } }, { key: "onDestroy", value: function () { document.removeEventListener("keyup", this.onKeyUp), this.formatter.quill.root.removeEventListener("input", this.onKeyUp) } }]), t }(f.default); t.default = p }, function (e, t, n) { "use strict"; function r(e) { return e && e.__esModule ? e : { default: e } } function o(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } function i(e, t) { if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return !t || "object" != typeof t && "function" != typeof t ? e : t } function a(e, t) { if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); e.prototype = Object.create(t && t.prototype, { constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 } }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) } Object.defineProperty(t, "__esModule", { value: !0 }); var l = function () { function e(e, t) { for (var n = 0; n < t.length; n++) { var r = t[n]; r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) } } return function (t, n, r) { return n && e(t.prototype, n), r && e(t, r), t } }(), u = n(2), s = r(u), c = n(0), f = (r(c), function (e) { function t(e) { o(this, t); var n = i(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e)); return n.onClick = function (e) { var t = e.target; t instanceof HTMLElement && "IMG" === t.tagName && (n.img = t, n.formatter.show(n)) }, n.img = null, n } return a(t, e), l(t, [{ key: "init", value: function () { this.formatter.quill.root.addEventListener("click", this.onClick) } }, { key: "getTargetElement", value: function () { return this.img } }, { key: "onHide", value: function () { this.img = null } }]), t }(s.default)); t.default = f }, function (e, t, n) { "use strict"; function r(e) { return e && e.__esModule ? e : { default: e } } function o(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } function i(e, t) { if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return !t || "object" != typeof t && "function" != typeof t ? e : t } function a(e, t) { if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); e.prototype = Object.create(t && t.prototype, { constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 } }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) } Object.defineProperty(t, "__esModule", { value: !0 }); var l = n(13), u = r(l), s = n(0), c = (r(s), function (e) { function t(e) { return o(this, t), i(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, "iframe.ql-video")) } return a(t, e), t }(u.default)); t.default = c }, function (e, t, n) { "use strict"; function r(e) { return e && e.__esModule ? e : { default: e } } function o(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } function i(e, t) { if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return !t || "object" != typeof t && "function" != typeof t ? e : t } function a(e, t) { if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); e.prototype = Object.create(t && t.prototype, { constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 } }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) } Object.defineProperty(t, "__esModule", { value: !0 }); var l = function () { function e(e, t) { for (var n = 0; n < t.length; n++) { var r = t[n]; r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) } } return function (t, n, r) { return n && e(t.prototype, n), r && e(t, r), t } }(), u = n(2), s = r(u), c = n(0), f = (r(c), "data-blot-formatter-unclickable-bound"), p = function (e) { function t(e, n) { o(this, t); var r = i(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e)); return r.onTextChange = function () { Array.from(document.querySelectorAll(r.selector + ":not([" + f + "])")).forEach(function (e) { e.setAttribute(f, "true"), e.addEventListener("mouseenter", r.onMouseEnter) }) }, r.onMouseEnter = function (e) { var t = e.target; t instanceof HTMLElement && (r.nextUnclickable = t, r.repositionProxyImage(r.nextUnclickable)) }, r.onProxyImageClick = function () { r.unclickable = r.nextUnclickable, r.nextUnclickable = null, r.formatter.show(r), r.hideProxyImage() }, r.selector = n, r.unclickable = null, r.nextUnclickable = null, r } return a(t, e), l(t, [{ key: "init", value: function () { document.body && document.body.appendChild(this.createProxyImage()), this.hideProxyImage(), this.proxyImage.addEventListener("click", this.onProxyImageClick), this.formatter.quill.on("text-change", this.onTextChange) } }, { key: "getTargetElement", value: function () { return this.unclickable } }, { key: "getOverlayElement", value: function () { return this.unclickable } }, { key: "onHide", value: function () { this.hideProxyImage(), this.nextUnclickable = null, this.unclickable = null } }, { key: "createProxyImage", value: function () { var e = document.createElement("canvas"), t = e.getContext("2d"); return t.globalAlpha = 0, t.fillRect(0, 0, 1, 1), this.proxyImage = document.createElement("img"), this.proxyImage.src = e.toDataURL("image/png"), this.proxyImage.classList.add("blot-formatter__proxy-image"), Object.assign(this.proxyImage.style, { position: "absolute", margin: "0" }), this.proxyImage } }, { key: "hideProxyImage", value: function () { Object.assign(this.proxyImage.style, { display: "none" }) } }, { key: "repositionProxyImage", value: function (e) { var t = e.getBoundingClientRect(); Object.assign(this.proxyImage.style, { display: "block", left: t.left + window.pageXOffset + "px", top: t.top + window.pageYOffset + "px", width: t.width + "px", height: t.height + "px" }) } }]), t }(s.default); t.default = p }, function (e, t, n) { "use strict"; function r(e) { return e && e.__esModule ? e : { default: e } } Object.defineProperty(t, "__esModule", { value: !0 }); var o = n(4); Object.defineProperty(t, "DefaultOptions", { enumerable: !0, get: function () { return r(o).default } }); var i = n(0); Object.defineProperty(t, "default", { enumerable: !0, get: function () { return r(i).default } }); var a = n(1); Object.defineProperty(t, "Action", { enumerable: !0, get: function () { return r(a).default } }); var l = n(5); Object.defineProperty(t, "AlignAction", { enumerable: !0, get: function () { return r(l).default } }); var u = n(6); Object.defineProperty(t, "DefaultAligner", { enumerable: !0, get: function () { return r(u).default } }); var s = n(8); Object.defineProperty(t, "DefaultToolbar", { enumerable: !0, get: function () { return r(s).default } }); var c = n(10); Object.defineProperty(t, "DeleteAction", { enumerable: !0, get: function () { return r(c).default } }); var f = n(9); Object.defineProperty(t, "ResizeAction", { enumerable: !0, get: function () { return r(f).default } }); var p = n(2); Object.defineProperty(t, "BlotSpec", { enumerable: !0, get: function () { return r(p).default } }); var d = n(11); Object.defineProperty(t, "ImageSpec", { enumerable: !0, get: function () { return r(d).default } }); var y = n(13); Object.defineProperty(t, "UnclickableBlotSpec", { enumerable: !0, get: function () { return r(y).default } }); var h = n(12); Object.defineProperty(t, "IframeVideoSpec", { enumerable: !0, get: function () { return r(h).default } }) }, function (e, t, n) { "use strict"; function r(e) { return !!e && "object" == typeof e } function o(e) { var t = Object.prototype.toString.call(e); return "[object RegExp]" === t || "[object Date]" === t || i(e) } function i(e) { return e.$$typeof === d } function a(e) { return Array.isArray(e) ? [] : {} } function l(e, t) { return t && !1 === t.clone || !f(e) ? e : c(a(e), e, t) } function u(e, t, n) { return e.concat(t).map(function (e) { return l(e, n) }) } function s(e, t, n) { var r = {}; return f(e) && Object.keys(e).forEach(function (t) { r[t] = l(e[t], n) }), Object.keys(t).forEach(function (o) { f(t[o]) && e[o] ? r[o] = c(e[o], t[o], n) : r[o] = l(t[o], n) }), r } function c(e, t, n) { var r = Array.isArray(t), o = Array.isArray(e), i = n || { arrayMerge: u }; if (r === o) return r ? (i.arrayMerge || u)(e, t, n) : s(e, t, n); return l(t, n) } Object.defineProperty(t, "__esModule", { value: !0 }); var f = function (e) { return r(e) && !o(e) }, p = "function" == typeof Symbol && Symbol.for, d = p ? Symbol.for("react.element") : 60103; c.all = function (e, t) { if (!Array.isArray(e)) throw new Error("first argument should be an array"); return e.reduce(function (e, n) { return c(e, n, t) }, {}) }; var y = c; t.default = y }, function (t, n) { t.exports = e }]) }); \ No newline at end of file diff --git a/Oqtane.Client/wwwroot/js/quill1.3.6.min.js b/Oqtane.Client/wwwroot/js/quill1.3.6.min.js deleted file mode 100644 index ff30fd90..00000000 --- a/Oqtane.Client/wwwroot/js/quill1.3.6.min.js +++ /dev/null @@ -1,8 +0,0 @@ -/*! - * Quill Editor v1.3.6 - * https://quilljs.com/ - * Copyright (c) 2014, Jason Chen - * Copyright (c) 2013, salesforce.com - */ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Quill=e():t.Quill=e()}("undefined"!=typeof self?self:this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=45)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(17),o=n(18),i=n(19),l=n(48),a=n(49),s=n(50),u=n(51),c=n(52),f=n(11),h=n(29),p=n(30),d=n(28),y=n(1),v={Scope:y.Scope,create:y.create,find:y.find,query:y.query,register:y.register,Container:r.default,Format:o.default,Leaf:i.default,Embed:u.default,Scroll:l.default,Block:s.default,Inline:a.default,Text:c.default,Attributor:{Attribute:f.default,Class:h.default,Style:p.default,Store:d.default}};e.default=v},function(t,e,n){"use strict";function r(t,e){var n=i(t);if(null==n)throw new s("Unable to create "+t+" blot");var r=n;return new r(t instanceof Node||t.nodeType===Node.TEXT_NODE?t:r.create(e),e)}function o(t,n){return void 0===n&&(n=!1),null==t?null:null!=t[e.DATA_KEY]?t[e.DATA_KEY].blot:n?o(t.parentNode,n):null}function i(t,e){void 0===e&&(e=p.ANY);var n;if("string"==typeof t)n=h[t]||u[t];else if(t instanceof Text||t.nodeType===Node.TEXT_NODE)n=h.text;else if("number"==typeof t)t&p.LEVEL&p.BLOCK?n=h.block:t&p.LEVEL&p.INLINE&&(n=h.inline);else if(t instanceof HTMLElement){var r=(t.getAttribute("class")||"").split(/\s+/);for(var o in r)if(n=c[r[o]])break;n=n||f[t.tagName]}return null==n?null:e&p.LEVEL&n.scope&&e&p.TYPE&n.scope?n:null}function l(){for(var t=[],e=0;e1)return t.map(function(t){return l(t)});var n=t[0];if("string"!=typeof n.blotName&&"string"!=typeof n.attrName)throw new s("Invalid definition");if("abstract"===n.blotName)throw new s("Cannot register abstract class");if(h[n.blotName||n.attrName]=n,"string"==typeof n.keyName)u[n.keyName]=n;else if(null!=n.className&&(c[n.className]=n),null!=n.tagName){Array.isArray(n.tagName)?n.tagName=n.tagName.map(function(t){return t.toUpperCase()}):n.tagName=n.tagName.toUpperCase();var r=Array.isArray(n.tagName)?n.tagName:[n.tagName];r.forEach(function(t){null!=f[t]&&null!=n.className||(f[t]=n)})}return n}var a=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var s=function(t){function e(e){var n=this;return e="[Parchment] "+e,n=t.call(this,e)||this,n.message=e,n.name=n.constructor.name,n}return a(e,t),e}(Error);e.ParchmentError=s;var u={},c={},f={},h={};e.DATA_KEY="__blot";var p;!function(t){t[t.TYPE=3]="TYPE",t[t.LEVEL=12]="LEVEL",t[t.ATTRIBUTE=13]="ATTRIBUTE",t[t.BLOT=14]="BLOT",t[t.INLINE=7]="INLINE",t[t.BLOCK=11]="BLOCK",t[t.BLOCK_BLOT=10]="BLOCK_BLOT",t[t.INLINE_BLOT=6]="INLINE_BLOT",t[t.BLOCK_ATTRIBUTE=9]="BLOCK_ATTRIBUTE",t[t.INLINE_ATTRIBUTE=5]="INLINE_ATTRIBUTE",t[t.ANY=15]="ANY"}(p=e.Scope||(e.Scope={})),e.create=r,e.find=o,e.query=i,e.register=l},function(t,e){"use strict";var n=Object.prototype.hasOwnProperty,r=Object.prototype.toString,o=function(t){return"function"==typeof Array.isArray?Array.isArray(t):"[object Array]"===r.call(t)},i=function(t){if(!t||"[object Object]"!==r.call(t))return!1;var e=n.call(t,"constructor"),o=t.constructor&&t.constructor.prototype&&n.call(t.constructor.prototype,"isPrototypeOf");if(t.constructor&&!e&&!o)return!1;var i;for(i in t);return void 0===i||n.call(t,i)};t.exports=function t(){var e,n,r,l,a,s,u=arguments[0],c=1,f=arguments.length,h=!1;for("boolean"==typeof u&&(h=u,u=arguments[1]||{},c=2),(null==u||"object"!=typeof u&&"function"!=typeof u)&&(u={});c1&&void 0!==arguments[1]?arguments[1]:{};return null==t?e:("function"==typeof t.formats&&(e=(0,f.default)(e,t.formats())),null==t.parent||"scroll"==t.parent.blotName||t.parent.statics.scope!==t.statics.scope?e:a(t.parent,e))}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.BlockEmbed=e.bubbleFormats=void 0;var s=function(){function t(t,e){for(var n=0;n0&&(t1&&void 0!==arguments[1]&&arguments[1];if(n&&(0===t||t>=this.length()-1)){var r=this.clone();return 0===t?(this.parent.insertBefore(r,this),this):(this.parent.insertBefore(r,this.next),r)}var o=u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"split",this).call(this,t,n);return this.cache={},o}}]),e}(y.default.Block);x.blotName="block",x.tagName="P",x.defaultChild="break",x.allowedChildren=[m.default,y.default.Embed,O.default],e.bubbleFormats=a,e.BlockEmbed=w,e.default=x},function(t,e,n){var r=n(54),o=n(12),i=n(2),l=n(20),a=String.fromCharCode(0),s=function(t){Array.isArray(t)?this.ops=t:null!=t&&Array.isArray(t.ops)?this.ops=t.ops:this.ops=[]};s.prototype.insert=function(t,e){var n={};return 0===t.length?this:(n.insert=t,null!=e&&"object"==typeof e&&Object.keys(e).length>0&&(n.attributes=e),this.push(n))},s.prototype.delete=function(t){return t<=0?this:this.push({delete:t})},s.prototype.retain=function(t,e){if(t<=0)return this;var n={retain:t};return null!=e&&"object"==typeof e&&Object.keys(e).length>0&&(n.attributes=e),this.push(n)},s.prototype.push=function(t){var e=this.ops.length,n=this.ops[e-1];if(t=i(!0,{},t),"object"==typeof n){if("number"==typeof t.delete&&"number"==typeof n.delete)return this.ops[e-1]={delete:n.delete+t.delete},this;if("number"==typeof n.delete&&null!=t.insert&&(e-=1,"object"!=typeof(n=this.ops[e-1])))return this.ops.unshift(t),this;if(o(t.attributes,n.attributes)){if("string"==typeof t.insert&&"string"==typeof n.insert)return this.ops[e-1]={insert:n.insert+t.insert},"object"==typeof t.attributes&&(this.ops[e-1].attributes=t.attributes),this;if("number"==typeof t.retain&&"number"==typeof n.retain)return this.ops[e-1]={retain:n.retain+t.retain},"object"==typeof t.attributes&&(this.ops[e-1].attributes=t.attributes),this}}return e===this.ops.length?this.ops.push(t):this.ops.splice(e,0,t),this},s.prototype.chop=function(){var t=this.ops[this.ops.length-1];return t&&t.retain&&!t.attributes&&this.ops.pop(),this},s.prototype.filter=function(t){return this.ops.filter(t)},s.prototype.forEach=function(t){this.ops.forEach(t)},s.prototype.map=function(t){return this.ops.map(t)},s.prototype.partition=function(t){var e=[],n=[];return this.forEach(function(r){(t(r)?e:n).push(r)}),[e,n]},s.prototype.reduce=function(t,e){return this.ops.reduce(t,e)},s.prototype.changeLength=function(){return this.reduce(function(t,e){return e.insert?t+l.length(e):e.delete?t-e.delete:t},0)},s.prototype.length=function(){return this.reduce(function(t,e){return t+l.length(e)},0)},s.prototype.slice=function(t,e){t=t||0,"number"!=typeof e&&(e=1/0);for(var n=[],r=l.iterator(this.ops),o=0;o0&&(e.push(t.ops[0]),e.ops=e.ops.concat(t.ops.slice(1))),e},s.prototype.diff=function(t,e){if(this.ops===t.ops)return new s;var n=[this,t].map(function(e){return e.map(function(n){if(null!=n.insert)return"string"==typeof n.insert?n.insert:a;var r=e===t?"on":"with";throw new Error("diff() called "+r+" non-document")}).join("")}),i=new s,u=r(n[0],n[1],e),c=l.iterator(this.ops),f=l.iterator(t.ops);return u.forEach(function(t){for(var e=t[1].length;e>0;){var n=0;switch(t[0]){case r.INSERT:n=Math.min(f.peekLength(),e),i.push(f.next(n));break;case r.DELETE:n=Math.min(e,c.peekLength()),c.next(n),i.delete(n);break;case r.EQUAL:n=Math.min(c.peekLength(),f.peekLength(),e);var a=c.next(n),s=f.next(n);o(a.insert,s.insert)?i.retain(n,l.attributes.diff(a.attributes,s.attributes)):i.push(s).delete(n)}e-=n}}),i.chop()},s.prototype.eachLine=function(t,e){e=e||"\n";for(var n=l.iterator(this.ops),r=new s,o=0;n.hasNext();){if("insert"!==n.peekType())return;var i=n.peek(),a=l.length(i)-n.peekLength(),u="string"==typeof i.insert?i.insert.indexOf(e,a)-a:-1;if(u<0)r.push(n.next());else if(u>0)r.push(n.next(u));else{if(!1===t(r,n.next(1).attributes||{},o))return;o+=1,r=new s}}r.length()>0&&t(r,{},o)},s.prototype.transform=function(t,e){if(e=!!e,"number"==typeof t)return this.transformPosition(t,e);for(var n=l.iterator(this.ops),r=l.iterator(t.ops),o=new s;n.hasNext()||r.hasNext();)if("insert"!==n.peekType()||!e&&"insert"===r.peekType())if("insert"===r.peekType())o.push(r.next());else{var i=Math.min(n.peekLength(),r.peekLength()),a=n.next(i),u=r.next(i);if(a.delete)continue;u.delete?o.push(u):o.retain(i,l.attributes.transform(a.attributes,u.attributes,e))}else o.retain(l.length(n.next()));return o.chop()},s.prototype.transformPosition=function(t,e){e=!!e;for(var n=l.iterator(this.ops),r=0;n.hasNext()&&r<=t;){var o=n.peekLength(),i=n.peekType();n.next(),"delete"!==i?("insert"===i&&(r0){var n=this.parent.isolate(this.offset(),this.length());this.moveChildren(n),n.wrap(this)}}}],[{key:"compare",value:function(t,n){var r=e.order.indexOf(t),o=e.order.indexOf(n);return r>=0||o>=0?r-o:t===n?0:t0){var a,s=[g.default.events.TEXT_CHANGE,l,i,e];if((a=this.emitter).emit.apply(a,[g.default.events.EDITOR_CHANGE].concat(s)),e!==g.default.sources.SILENT){var c;(c=this.emitter).emit.apply(c,s)}}return l}function s(t,e,n,r,o){var i={};return"number"==typeof t.index&&"number"==typeof t.length?"number"!=typeof e?(o=r,r=n,n=e,e=t.length,t=t.index):(e=t.length,t=t.index):"number"!=typeof e&&(o=r,r=n,n=e,e=0),"object"===(void 0===n?"undefined":c(n))?(i=n,o=r):"string"==typeof n&&(null!=r?i[n]=r:o=n),o=o||g.default.sources.API,[t,e,i,o]}function u(t,e,n,r){if(null==t)return null;var o=void 0,i=void 0;if(e instanceof d.default){var l=[t.index,t.index+t.length].map(function(t){return e.transformPosition(t,r!==g.default.sources.USER)}),a=f(l,2);o=a[0],i=a[1]}else{var s=[t.index,t.index+t.length].map(function(t){return t=0?t+n:Math.max(e,t+n)}),u=f(s,2);o=u[0],i=u[1]}return new x.Range(o,i-o)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.overload=e.expandConfig=void 0;var c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},f=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),h=function(){function t(t,e){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{};if(i(this,t),this.options=l(e,r),this.container=this.options.container,null==this.container)return P.error("Invalid Quill container",e);this.options.debug&&t.debug(this.options.debug);var o=this.container.innerHTML.trim();this.container.classList.add("ql-container"),this.container.innerHTML="",this.container.__quill=this,this.root=this.addContainer("ql-editor"),this.root.classList.add("ql-blank"),this.root.setAttribute("data-gramm",!1),this.scrollingContainer=this.options.scrollingContainer||this.root,this.emitter=new g.default,this.scroll=w.default.create(this.root,{emitter:this.emitter,whitelist:this.options.formats}),this.editor=new v.default(this.scroll),this.selection=new k.default(this.scroll,this.emitter),this.theme=new this.options.theme(this,this.options),this.keyboard=this.theme.addModule("keyboard"),this.clipboard=this.theme.addModule("clipboard"),this.history=this.theme.addModule("history"),this.theme.init(),this.emitter.on(g.default.events.EDITOR_CHANGE,function(t){t===g.default.events.TEXT_CHANGE&&n.root.classList.toggle("ql-blank",n.editor.isBlank())}),this.emitter.on(g.default.events.SCROLL_UPDATE,function(t,e){var r=n.selection.lastRange,o=r&&0===r.length?r.index:void 0;a.call(n,function(){return n.editor.update(null,e,o)},t)});var s=this.clipboard.convert("

    "+o+"


    ");this.setContents(s),this.history.clear(),this.options.placeholder&&this.root.setAttribute("data-placeholder",this.options.placeholder),this.options.readOnly&&this.disable()}return h(t,null,[{key:"debug",value:function(t){!0===t&&(t="log"),A.default.level(t)}},{key:"find",value:function(t){return t.__quill||w.default.find(t)}},{key:"import",value:function(t){return null==this.imports[t]&&P.error("Cannot import "+t+". Are you sure it was registered?"),this.imports[t]}},{key:"register",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if("string"!=typeof t){var o=t.attrName||t.blotName;"string"==typeof o?this.register("formats/"+o,t,e):Object.keys(t).forEach(function(r){n.register(r,t[r],e)})}else null==this.imports[t]||r||P.warn("Overwriting "+t+" with",e),this.imports[t]=e,(t.startsWith("blots/")||t.startsWith("formats/"))&&"abstract"!==e.blotName?w.default.register(e):t.startsWith("modules")&&"function"==typeof e.register&&e.register()}}]),h(t,[{key:"addContainer",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if("string"==typeof t){var n=t;t=document.createElement("div"),t.classList.add(n)}return this.container.insertBefore(t,e),t}},{key:"blur",value:function(){this.selection.setRange(null)}},{key:"deleteText",value:function(t,e,n){var r=this,o=s(t,e,n),i=f(o,4);return t=i[0],e=i[1],n=i[3],a.call(this,function(){return r.editor.deleteText(t,e)},n,t,-1*e)}},{key:"disable",value:function(){this.enable(!1)}},{key:"enable",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.scroll.enable(t),this.container.classList.toggle("ql-disabled",!t)}},{key:"focus",value:function(){var t=this.scrollingContainer.scrollTop;this.selection.focus(),this.scrollingContainer.scrollTop=t,this.scrollIntoView()}},{key:"format",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:g.default.sources.API;return a.call(this,function(){var r=n.getSelection(!0),i=new d.default;if(null==r)return i;if(w.default.query(t,w.default.Scope.BLOCK))i=n.editor.formatLine(r.index,r.length,o({},t,e));else{if(0===r.length)return n.selection.format(t,e),i;i=n.editor.formatText(r.index,r.length,o({},t,e))}return n.setSelection(r,g.default.sources.SILENT),i},r)}},{key:"formatLine",value:function(t,e,n,r,o){var i=this,l=void 0,u=s(t,e,n,r,o),c=f(u,4);return t=c[0],e=c[1],l=c[2],o=c[3],a.call(this,function(){return i.editor.formatLine(t,e,l)},o,t,0)}},{key:"formatText",value:function(t,e,n,r,o){var i=this,l=void 0,u=s(t,e,n,r,o),c=f(u,4);return t=c[0],e=c[1],l=c[2],o=c[3],a.call(this,function(){return i.editor.formatText(t,e,l)},o,t,0)}},{key:"getBounds",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=void 0;n="number"==typeof t?this.selection.getBounds(t,e):this.selection.getBounds(t.index,t.length);var r=this.container.getBoundingClientRect();return{bottom:n.bottom-r.top,height:n.height,left:n.left-r.left,right:n.right-r.left,top:n.top-r.top,width:n.width}}},{key:"getContents",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.getLength()-t,n=s(t,e),r=f(n,2);return t=r[0],e=r[1],this.editor.getContents(t,e)}},{key:"getFormat",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.getSelection(!0),e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return"number"==typeof t?this.editor.getFormat(t,e):this.editor.getFormat(t.index,t.length)}},{key:"getIndex",value:function(t){return t.offset(this.scroll)}},{key:"getLength",value:function(){return this.scroll.length()}},{key:"getLeaf",value:function(t){return this.scroll.leaf(t)}},{key:"getLine",value:function(t){return this.scroll.line(t)}},{key:"getLines",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Number.MAX_VALUE;return"number"!=typeof t?this.scroll.lines(t.index,t.length):this.scroll.lines(t,e)}},{key:"getModule",value:function(t){return this.theme.modules[t]}},{key:"getSelection",value:function(){return arguments.length>0&&void 0!==arguments[0]&&arguments[0]&&this.focus(),this.update(),this.selection.getRange()[0]}},{key:"getText",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.getLength()-t,n=s(t,e),r=f(n,2);return t=r[0],e=r[1],this.editor.getText(t,e)}},{key:"hasFocus",value:function(){return this.selection.hasFocus()}},{key:"insertEmbed",value:function(e,n,r){var o=this,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:t.sources.API;return a.call(this,function(){return o.editor.insertEmbed(e,n,r)},i,e)}},{key:"insertText",value:function(t,e,n,r,o){var i=this,l=void 0,u=s(t,0,n,r,o),c=f(u,4);return t=c[0],l=c[2],o=c[3],a.call(this,function(){return i.editor.insertText(t,e,l)},o,t,e.length)}},{key:"isEnabled",value:function(){return!this.container.classList.contains("ql-disabled")}},{key:"off",value:function(){return this.emitter.off.apply(this.emitter,arguments)}},{key:"on",value:function(){return this.emitter.on.apply(this.emitter,arguments)}},{key:"once",value:function(){return this.emitter.once.apply(this.emitter,arguments)}},{key:"pasteHTML",value:function(t,e,n){this.clipboard.dangerouslyPasteHTML(t,e,n)}},{key:"removeFormat",value:function(t,e,n){var r=this,o=s(t,e,n),i=f(o,4);return t=i[0],e=i[1],n=i[3],a.call(this,function(){return r.editor.removeFormat(t,e)},n,t)}},{key:"scrollIntoView",value:function(){this.selection.scrollIntoView(this.scrollingContainer)}},{key:"setContents",value:function(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:g.default.sources.API;return a.call(this,function(){t=new d.default(t);var n=e.getLength(),r=e.editor.deleteText(0,n),o=e.editor.applyDelta(t),i=o.ops[o.ops.length-1];return null!=i&&"string"==typeof i.insert&&"\n"===i.insert[i.insert.length-1]&&(e.editor.deleteText(e.getLength()-1,1),o.delete(1)),r.compose(o)},n)}},{key:"setSelection",value:function(e,n,r){if(null==e)this.selection.setRange(null,n||t.sources.API);else{var o=s(e,n,r),i=f(o,4);e=i[0],n=i[1],r=i[3],this.selection.setRange(new x.Range(e,n),r),r!==g.default.sources.SILENT&&this.selection.scrollIntoView(this.scrollingContainer)}}},{key:"setText",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:g.default.sources.API,n=(new d.default).insert(t);return this.setContents(n,e)}},{key:"update",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:g.default.sources.USER,e=this.scroll.update(t);return this.selection.update(t),e}},{key:"updateContents",value:function(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:g.default.sources.API;return a.call(this,function(){return t=new d.default(t),e.editor.applyDelta(t,n)},n,!0)}}]),t}();S.DEFAULTS={bounds:null,formats:null,modules:{},placeholder:"",readOnly:!1,scrollingContainer:null,strict:!0,theme:"default"},S.events=g.default.events,S.sources=g.default.sources,S.version="1.3.6",S.imports={delta:d.default,parchment:w.default,"core/module":_.default,"core/theme":T.default},e.expandConfig=l,e.overload=s,e.default=S},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var o=function t(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};r(this,t),this.quill=e,this.options=n};o.DEFAULTS={},e.default=o},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=n(0),a=function(t){return t&&t.__esModule?t:{default:t}}(l),s=function(t){function e(){return r(this,e),o(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return i(e,t),e}(a.default.Text);e.default=s},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var n=0;n1?e-1:0),r=1;r1?n-1:0),o=1;o-1:this.whitelist.indexOf(e)>-1))},t.prototype.remove=function(t){t.removeAttribute(this.keyName)},t.prototype.value=function(t){var e=t.getAttribute(this.keyName);return this.canAdd(t,e)&&e?e:""},t}();e.default=o},function(t,e,n){function r(t){return null===t||void 0===t}function o(t){return!(!t||"object"!=typeof t||"number"!=typeof t.length)&&("function"==typeof t.copy&&"function"==typeof t.slice&&!(t.length>0&&"number"!=typeof t[0]))}function i(t,e,n){var i,c;if(r(t)||r(e))return!1;if(t.prototype!==e.prototype)return!1;if(s(t))return!!s(e)&&(t=l.call(t),e=l.call(e),u(t,e,n));if(o(t)){if(!o(e))return!1;if(t.length!==e.length)return!1;for(i=0;i=0;i--)if(f[i]!=h[i])return!1;for(i=f.length-1;i>=0;i--)if(c=f[i],!u(t[c],e[c],n))return!1;return typeof t==typeof e}var l=Array.prototype.slice,a=n(55),s=n(56),u=t.exports=function(t,e,n){return n||(n={}),t===e||(t instanceof Date&&e instanceof Date?t.getTime()===e.getTime():!t||!e||"object"!=typeof t&&"object"!=typeof e?n.strict?t===e:t==e:i(t,e,n))}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.Code=void 0;var a=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),s=function(){function t(t,e){for(var n=0;n=t+n)){var l=this.newlineIndex(t,!0)+1,a=i-l+1,s=this.isolate(l,a),u=s.next;s.format(r,o),u instanceof e&&u.formatAt(0,t-l+n-a,r,o)}}}},{key:"insertAt",value:function(t,e,n){if(null==n){var r=this.descendant(m.default,t),o=a(r,2),i=o[0],l=o[1];i.insertAt(l,e)}}},{key:"length",value:function(){var t=this.domNode.textContent.length;return this.domNode.textContent.endsWith("\n")?t:t+1}},{key:"newlineIndex",value:function(t){if(arguments.length>1&&void 0!==arguments[1]&&arguments[1])return this.domNode.textContent.slice(0,t).lastIndexOf("\n");var e=this.domNode.textContent.slice(t).indexOf("\n");return e>-1?t+e:-1}},{key:"optimize",value:function(t){this.domNode.textContent.endsWith("\n")||this.appendChild(p.default.create("text","\n")),u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"optimize",this).call(this,t);var n=this.next;null!=n&&n.prev===this&&n.statics.blotName===this.statics.blotName&&this.statics.formats(this.domNode)===n.statics.formats(n.domNode)&&(n.optimize(t),n.moveChildren(this),n.remove())}},{key:"replace",value:function(t){u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"replace",this).call(this,t),[].slice.call(this.domNode.querySelectorAll("*")).forEach(function(t){var e=p.default.find(t);null==e?t.parentNode.removeChild(t):e instanceof p.default.Embed?e.remove():e.unwrap()})}}],[{key:"create",value:function(t){var n=u(e.__proto__||Object.getPrototypeOf(e),"create",this).call(this,t);return n.setAttribute("spellcheck",!1),n}},{key:"formats",value:function(){return!0}}]),e}(y.default);O.blotName="code-block",O.tagName="PRE",O.TAB=" ",e.Code=_,e.default=O},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;n-1}Object.defineProperty(e,"__esModule",{value:!0}),e.sanitize=e.default=void 0;var a=function(){function t(t,e){for(var n=0;n1&&void 0!==arguments[1]&&arguments[1],n=this.container.querySelector(".ql-selected");if(t!==n&&(null!=n&&n.classList.remove("ql-selected"),null!=t&&(t.classList.add("ql-selected"),this.select.selectedIndex=[].indexOf.call(t.parentNode.children,t),t.hasAttribute("data-value")?this.label.setAttribute("data-value",t.getAttribute("data-value")):this.label.removeAttribute("data-value"),t.hasAttribute("data-label")?this.label.setAttribute("data-label",t.getAttribute("data-label")):this.label.removeAttribute("data-label"),e))){if("function"==typeof Event)this.select.dispatchEvent(new Event("change"));else if("object"===("undefined"==typeof Event?"undefined":l(Event))){var r=document.createEvent("Event");r.initEvent("change",!0,!0),this.select.dispatchEvent(r)}this.close()}}},{key:"update",value:function(){var t=void 0;if(this.select.selectedIndex>-1){var e=this.container.querySelector(".ql-picker-options").children[this.select.selectedIndex];t=this.select.options[this.select.selectedIndex],this.selectItem(e)}else this.selectItem(null);var n=null!=t&&t!==this.select.querySelector("option[selected]");this.label.classList.toggle("ql-active",n)}}]),t}();e.default=p},function(t,e,n){"use strict";function r(t){var e=a.find(t);if(null==e)try{e=a.create(t)}catch(n){e=a.create(a.Scope.INLINE),[].slice.call(t.childNodes).forEach(function(t){e.domNode.appendChild(t)}),t.parentNode&&t.parentNode.replaceChild(e.domNode,t),e.attach()}return e}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(47),l=n(27),a=n(1),s=function(t){function e(e){var n=t.call(this,e)||this;return n.build(),n}return o(e,t),e.prototype.appendChild=function(t){this.insertBefore(t)},e.prototype.attach=function(){t.prototype.attach.call(this),this.children.forEach(function(t){t.attach()})},e.prototype.build=function(){var t=this;this.children=new i.default,[].slice.call(this.domNode.childNodes).reverse().forEach(function(e){try{var n=r(e);t.insertBefore(n,t.children.head||void 0)}catch(t){if(t instanceof a.ParchmentError)return;throw t}})},e.prototype.deleteAt=function(t,e){if(0===t&&e===this.length())return this.remove();this.children.forEachAt(t,e,function(t,e,n){t.deleteAt(e,n)})},e.prototype.descendant=function(t,n){var r=this.children.find(n),o=r[0],i=r[1];return null==t.blotName&&t(o)||null!=t.blotName&&o instanceof t?[o,i]:o instanceof e?o.descendant(t,i):[null,-1]},e.prototype.descendants=function(t,n,r){void 0===n&&(n=0),void 0===r&&(r=Number.MAX_VALUE);var o=[],i=r;return this.children.forEachAt(n,r,function(n,r,l){(null==t.blotName&&t(n)||null!=t.blotName&&n instanceof t)&&o.push(n),n instanceof e&&(o=o.concat(n.descendants(t,r,i))),i-=l}),o},e.prototype.detach=function(){this.children.forEach(function(t){t.detach()}),t.prototype.detach.call(this)},e.prototype.formatAt=function(t,e,n,r){this.children.forEachAt(t,e,function(t,e,o){t.formatAt(e,o,n,r)})},e.prototype.insertAt=function(t,e,n){var r=this.children.find(t),o=r[0],i=r[1];if(o)o.insertAt(i,e,n);else{var l=null==n?a.create("text",e):a.create(e,n);this.appendChild(l)}},e.prototype.insertBefore=function(t,e){if(null!=this.statics.allowedChildren&&!this.statics.allowedChildren.some(function(e){return t instanceof e}))throw new a.ParchmentError("Cannot insert "+t.statics.blotName+" into "+this.statics.blotName);t.insertInto(this,e)},e.prototype.length=function(){return this.children.reduce(function(t,e){return t+e.length()},0)},e.prototype.moveChildren=function(t,e){this.children.forEach(function(n){t.insertBefore(n,e)})},e.prototype.optimize=function(e){if(t.prototype.optimize.call(this,e),0===this.children.length)if(null!=this.statics.defaultChild){var n=a.create(this.statics.defaultChild);this.appendChild(n),n.optimize(e)}else this.remove()},e.prototype.path=function(t,n){void 0===n&&(n=!1);var r=this.children.find(t,n),o=r[0],i=r[1],l=[[this,t]];return o instanceof e?l.concat(o.path(i,n)):(null!=o&&l.push([o,i]),l)},e.prototype.removeChild=function(t){this.children.remove(t)},e.prototype.replace=function(n){n instanceof e&&n.moveChildren(this),t.prototype.replace.call(this,n)},e.prototype.split=function(t,e){if(void 0===e&&(e=!1),!e){if(0===t)return this;if(t===this.length())return this.next}var n=this.clone();return this.parent.insertBefore(n,this.next),this.children.forEachAt(t,this.length(),function(t,r,o){t=t.split(r,e),n.appendChild(t)}),n},e.prototype.unwrap=function(){this.moveChildren(this.parent,this.next),this.remove()},e.prototype.update=function(t,e){var n=this,o=[],i=[];t.forEach(function(t){t.target===n.domNode&&"childList"===t.type&&(o.push.apply(o,t.addedNodes),i.push.apply(i,t.removedNodes))}),i.forEach(function(t){if(!(null!=t.parentNode&&"IFRAME"!==t.tagName&&document.body.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_CONTAINED_BY)){var e=a.find(t);null!=e&&(null!=e.domNode.parentNode&&e.domNode.parentNode!==n.domNode||e.detach())}}),o.filter(function(t){return t.parentNode==n.domNode}).sort(function(t,e){return t===e?0:t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING?1:-1}).forEach(function(t){var e=null;null!=t.nextSibling&&(e=a.find(t.nextSibling));var o=r(t);o.next==e&&null!=o.next||(null!=o.parent&&o.parent.removeChild(n),n.insertBefore(o,e||void 0))})},e}(l.default);e.default=s},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(11),i=n(28),l=n(17),a=n(1),s=function(t){function e(e){var n=t.call(this,e)||this;return n.attributes=new i.default(n.domNode),n}return r(e,t),e.formats=function(t){return"string"==typeof this.tagName||(Array.isArray(this.tagName)?t.tagName.toLowerCase():void 0)},e.prototype.format=function(t,e){var n=a.query(t);n instanceof o.default?this.attributes.attribute(n,e):e&&(null==n||t===this.statics.blotName&&this.formats()[t]===e||this.replaceWith(t,e))},e.prototype.formats=function(){var t=this.attributes.values(),e=this.statics.formats(this.domNode);return null!=e&&(t[this.statics.blotName]=e),t},e.prototype.replaceWith=function(e,n){var r=t.prototype.replaceWith.call(this,e,n);return this.attributes.copy(r),r},e.prototype.update=function(e,n){var r=this;t.prototype.update.call(this,e,n),e.some(function(t){return t.target===r.domNode&&"attributes"===t.type})&&this.attributes.build()},e.prototype.wrap=function(n,r){var o=t.prototype.wrap.call(this,n,r);return o instanceof e&&o.statics.scope===this.statics.scope&&this.attributes.move(o),o},e}(l.default);e.default=s},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(27),i=n(1),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r(e,t),e.value=function(t){return!0},e.prototype.index=function(t,e){return this.domNode===t||this.domNode.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_CONTAINED_BY?Math.min(e,1):-1},e.prototype.position=function(t,e){var n=[].indexOf.call(this.parent.domNode.childNodes,this.domNode);return t>0&&(n+=1),[this.parent.domNode,n]},e.prototype.value=function(){return t={},t[this.statics.blotName]=this.statics.value(this.domNode)||!0,t;var t},e.scope=i.Scope.INLINE_BLOT,e}(o.default);e.default=l},function(t,e,n){function r(t){this.ops=t,this.index=0,this.offset=0}var o=n(12),i=n(2),l={attributes:{compose:function(t,e,n){"object"!=typeof t&&(t={}),"object"!=typeof e&&(e={});var r=i(!0,{},e);n||(r=Object.keys(r).reduce(function(t,e){return null!=r[e]&&(t[e]=r[e]),t},{}));for(var o in t)void 0!==t[o]&&void 0===e[o]&&(r[o]=t[o]);return Object.keys(r).length>0?r:void 0},diff:function(t,e){"object"!=typeof t&&(t={}),"object"!=typeof e&&(e={});var n=Object.keys(t).concat(Object.keys(e)).reduce(function(n,r){return o(t[r],e[r])||(n[r]=void 0===e[r]?null:e[r]),n},{});return Object.keys(n).length>0?n:void 0},transform:function(t,e,n){if("object"!=typeof t)return e;if("object"==typeof e){if(!n)return e;var r=Object.keys(e).reduce(function(n,r){return void 0===t[r]&&(n[r]=e[r]),n},{});return Object.keys(r).length>0?r:void 0}}},iterator:function(t){return new r(t)},length:function(t){return"number"==typeof t.delete?t.delete:"number"==typeof t.retain?t.retain:"string"==typeof t.insert?t.insert.length:1}};r.prototype.hasNext=function(){return this.peekLength()<1/0},r.prototype.next=function(t){t||(t=1/0);var e=this.ops[this.index];if(e){var n=this.offset,r=l.length(e);if(t>=r-n?(t=r-n,this.index+=1,this.offset=0):this.offset+=t,"number"==typeof e.delete)return{delete:t};var o={};return e.attributes&&(o.attributes=e.attributes),"number"==typeof e.retain?o.retain=t:"string"==typeof e.insert?o.insert=e.insert.substr(n,t):o.insert=e.insert,o}return{retain:1/0}},r.prototype.peek=function(){return this.ops[this.index]},r.prototype.peekLength=function(){return this.ops[this.index]?l.length(this.ops[this.index])-this.offset:1/0},r.prototype.peekType=function(){return this.ops[this.index]?"number"==typeof this.ops[this.index].delete?"delete":"number"==typeof this.ops[this.index].retain?"retain":"insert":"retain"},t.exports=l},function(t,e){var n=function(){"use strict";function t(t,e){return null!=e&&t instanceof e}function e(n,r,o,i,c){function f(n,o){if(null===n)return null;if(0===o)return n;var y,v;if("object"!=typeof n)return n;if(t(n,a))y=new a;else if(t(n,s))y=new s;else if(t(n,u))y=new u(function(t,e){n.then(function(e){t(f(e,o-1))},function(t){e(f(t,o-1))})});else if(e.__isArray(n))y=[];else if(e.__isRegExp(n))y=new RegExp(n.source,l(n)),n.lastIndex&&(y.lastIndex=n.lastIndex);else if(e.__isDate(n))y=new Date(n.getTime());else{if(d&&Buffer.isBuffer(n))return y=new Buffer(n.length),n.copy(y),y;t(n,Error)?y=Object.create(n):void 0===i?(v=Object.getPrototypeOf(n),y=Object.create(v)):(y=Object.create(i),v=i)}if(r){var b=h.indexOf(n);if(-1!=b)return p[b];h.push(n),p.push(y)}t(n,a)&&n.forEach(function(t,e){var n=f(e,o-1),r=f(t,o-1);y.set(n,r)}),t(n,s)&&n.forEach(function(t){var e=f(t,o-1);y.add(e)});for(var g in n){var m;v&&(m=Object.getOwnPropertyDescriptor(v,g)),m&&null==m.set||(y[g]=f(n[g],o-1))}if(Object.getOwnPropertySymbols)for(var _=Object.getOwnPropertySymbols(n),g=0;g<_.length;g++){var O=_[g],w=Object.getOwnPropertyDescriptor(n,O);(!w||w.enumerable||c)&&(y[O]=f(n[O],o-1),w.enumerable||Object.defineProperty(y,O,{enumerable:!1}))}if(c)for(var x=Object.getOwnPropertyNames(n),g=0;g1&&void 0!==arguments[1]?arguments[1]:0;i(this,t),this.index=e,this.length=n},O=function(){function t(e,n){var r=this;i(this,t),this.emitter=n,this.scroll=e,this.composing=!1,this.mouseDown=!1,this.root=this.scroll.domNode,this.cursor=c.default.create("cursor",this),this.lastRange=this.savedRange=new _(0,0),this.handleComposition(),this.handleDragging(),this.emitter.listenDOM("selectionchange",document,function(){r.mouseDown||setTimeout(r.update.bind(r,v.default.sources.USER),1)}),this.emitter.on(v.default.events.EDITOR_CHANGE,function(t,e){t===v.default.events.TEXT_CHANGE&&e.length()>0&&r.update(v.default.sources.SILENT)}),this.emitter.on(v.default.events.SCROLL_BEFORE_UPDATE,function(){if(r.hasFocus()){var t=r.getNativeRange();null!=t&&t.start.node!==r.cursor.textNode&&r.emitter.once(v.default.events.SCROLL_UPDATE,function(){try{r.setNativeRange(t.start.node,t.start.offset,t.end.node,t.end.offset)}catch(t){}})}}),this.emitter.on(v.default.events.SCROLL_OPTIMIZE,function(t,e){if(e.range){var n=e.range,o=n.startNode,i=n.startOffset,l=n.endNode,a=n.endOffset;r.setNativeRange(o,i,l,a)}}),this.update(v.default.sources.SILENT)}return s(t,[{key:"handleComposition",value:function(){var t=this;this.root.addEventListener("compositionstart",function(){t.composing=!0}),this.root.addEventListener("compositionend",function(){if(t.composing=!1,t.cursor.parent){var e=t.cursor.restore();if(!e)return;setTimeout(function(){t.setNativeRange(e.startNode,e.startOffset,e.endNode,e.endOffset)},1)}})}},{key:"handleDragging",value:function(){var t=this;this.emitter.listenDOM("mousedown",document.body,function(){t.mouseDown=!0}),this.emitter.listenDOM("mouseup",document.body,function(){t.mouseDown=!1,t.update(v.default.sources.USER)})}},{key:"focus",value:function(){this.hasFocus()||(this.root.focus(),this.setRange(this.savedRange))}},{key:"format",value:function(t,e){if(null==this.scroll.whitelist||this.scroll.whitelist[t]){this.scroll.update();var n=this.getNativeRange();if(null!=n&&n.native.collapsed&&!c.default.query(t,c.default.Scope.BLOCK)){if(n.start.node!==this.cursor.textNode){var r=c.default.find(n.start.node,!1);if(null==r)return;if(r instanceof c.default.Leaf){var o=r.split(n.start.offset);r.parent.insertBefore(this.cursor,o)}else r.insertBefore(this.cursor,n.start.node);this.cursor.attach()}this.cursor.format(t,e),this.scroll.optimize(),this.setNativeRange(this.cursor.textNode,this.cursor.textNode.data.length),this.update()}}}},{key:"getBounds",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=this.scroll.length();t=Math.min(t,n-1),e=Math.min(t+e,n-1)-t;var r=void 0,o=this.scroll.leaf(t),i=a(o,2),l=i[0],s=i[1];if(null==l)return null;var u=l.position(s,!0),c=a(u,2);r=c[0],s=c[1];var f=document.createRange();if(e>0){f.setStart(r,s);var h=this.scroll.leaf(t+e),p=a(h,2);if(l=p[0],s=p[1],null==l)return null;var d=l.position(s,!0),y=a(d,2);return r=y[0],s=y[1],f.setEnd(r,s),f.getBoundingClientRect()}var v="left",b=void 0;return r instanceof Text?(s0&&(v="right")),{bottom:b.top+b.height,height:b.height,left:b[v],right:b[v],top:b.top,width:0}}},{key:"getNativeRange",value:function(){var t=document.getSelection();if(null==t||t.rangeCount<=0)return null;var e=t.getRangeAt(0);if(null==e)return null;var n=this.normalizeNative(e);return m.info("getNativeRange",n),n}},{key:"getRange",value:function(){var t=this.getNativeRange();return null==t?[null,null]:[this.normalizedToRange(t),t]}},{key:"hasFocus",value:function(){return document.activeElement===this.root}},{key:"normalizedToRange",value:function(t){var e=this,n=[[t.start.node,t.start.offset]];t.native.collapsed||n.push([t.end.node,t.end.offset]);var r=n.map(function(t){var n=a(t,2),r=n[0],o=n[1],i=c.default.find(r,!0),l=i.offset(e.scroll);return 0===o?l:i instanceof c.default.Container?l+i.length():l+i.index(r,o)}),i=Math.min(Math.max.apply(Math,o(r)),this.scroll.length()-1),l=Math.min.apply(Math,[i].concat(o(r)));return new _(l,i-l)}},{key:"normalizeNative",value:function(t){if(!l(this.root,t.startContainer)||!t.collapsed&&!l(this.root,t.endContainer))return null;var e={start:{node:t.startContainer,offset:t.startOffset},end:{node:t.endContainer,offset:t.endOffset},native:t};return[e.start,e.end].forEach(function(t){for(var e=t.node,n=t.offset;!(e instanceof Text)&&e.childNodes.length>0;)if(e.childNodes.length>n)e=e.childNodes[n],n=0;else{if(e.childNodes.length!==n)break;e=e.lastChild,n=e instanceof Text?e.data.length:e.childNodes.length+1}t.node=e,t.offset=n}),e}},{key:"rangeToNative",value:function(t){var e=this,n=t.collapsed?[t.index]:[t.index,t.index+t.length],r=[],o=this.scroll.length();return n.forEach(function(t,n){t=Math.min(o-1,t);var i=void 0,l=e.scroll.leaf(t),s=a(l,2),u=s[0],c=s[1],f=u.position(c,0!==n),h=a(f,2);i=h[0],c=h[1],r.push(i,c)}),r.length<2&&(r=r.concat(r)),r}},{key:"scrollIntoView",value:function(t){var e=this.lastRange;if(null!=e){var n=this.getBounds(e.index,e.length);if(null!=n){var r=this.scroll.length()-1,o=this.scroll.line(Math.min(e.index,r)),i=a(o,1),l=i[0],s=l;if(e.length>0){var u=this.scroll.line(Math.min(e.index+e.length,r));s=a(u,1)[0]}if(null!=l&&null!=s){var c=t.getBoundingClientRect();n.topc.bottom&&(t.scrollTop+=n.bottom-c.bottom)}}}}},{key:"setNativeRange",value:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:t,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:e,o=arguments.length>4&&void 0!==arguments[4]&&arguments[4];if(m.info("setNativeRange",t,e,n,r),null==t||null!=this.root.parentNode&&null!=t.parentNode&&null!=n.parentNode){var i=document.getSelection();if(null!=i)if(null!=t){this.hasFocus()||this.root.focus();var l=(this.getNativeRange()||{}).native;if(null==l||o||t!==l.startContainer||e!==l.startOffset||n!==l.endContainer||r!==l.endOffset){"BR"==t.tagName&&(e=[].indexOf.call(t.parentNode.childNodes,t),t=t.parentNode),"BR"==n.tagName&&(r=[].indexOf.call(n.parentNode.childNodes,n),n=n.parentNode);var a=document.createRange();a.setStart(t,e),a.setEnd(n,r),i.removeAllRanges(),i.addRange(a)}}else i.removeAllRanges(),this.root.blur(),document.body.focus()}}},{key:"setRange",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:v.default.sources.API;if("string"==typeof e&&(n=e,e=!1),m.info("setRange",t),null!=t){var r=this.rangeToNative(t);this.setNativeRange.apply(this,o(r).concat([e]))}else this.setNativeRange(null);this.update(n)}},{key:"update",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:v.default.sources.USER,e=this.lastRange,n=this.getRange(),r=a(n,2),o=r[0],i=r[1];if(this.lastRange=o,null!=this.lastRange&&(this.savedRange=this.lastRange),!(0,d.default)(e,this.lastRange)){var l;!this.composing&&null!=i&&i.native.collapsed&&i.start.node!==this.cursor.textNode&&this.cursor.restore();var s=[v.default.events.SELECTION_CHANGE,(0,h.default)(this.lastRange),(0,h.default)(e),t];if((l=this.emitter).emit.apply(l,[v.default.events.EDITOR_CHANGE].concat(s)),t!==v.default.sources.SILENT){var u;(u=this.emitter).emit.apply(u,s)}}}}]),t}();e.Range=_,e.default=O},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=n(0),s=r(a),u=n(3),c=r(u),f=function(t){function e(){return o(this,e),i(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return l(e,t),e}(s.default.Container);f.allowedChildren=[c.default,u.BlockEmbed,f],e.default=f},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.ColorStyle=e.ColorClass=e.ColorAttributor=void 0;var l=function(){function t(t,e){for(var n=0;n1){var u=o.formats(),c=this.quill.getFormat(t.index-1,1);i=A.default.attributes.diff(u,c)||{}}}var f=/[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test(e.prefix)?2:1;this.quill.deleteText(t.index-f,f,S.default.sources.USER),Object.keys(i).length>0&&this.quill.formatLine(t.index-f,f,i,S.default.sources.USER),this.quill.focus()}}function c(t,e){var n=/^[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(e.suffix)?2:1;if(!(t.index>=this.quill.getLength()-n)){var r={},o=0,i=this.quill.getLine(t.index),l=b(i,1),a=l[0];if(e.offset>=a.length()-1){var s=this.quill.getLine(t.index+1),u=b(s,1),c=u[0];if(c){var f=a.formats(),h=this.quill.getFormat(t.index,1);r=A.default.attributes.diff(f,h)||{},o=c.length()}}this.quill.deleteText(t.index,n,S.default.sources.USER),Object.keys(r).length>0&&this.quill.formatLine(t.index+o-1,n,r,S.default.sources.USER)}}function f(t){var e=this.quill.getLines(t),n={};if(e.length>1){var r=e[0].formats(),o=e[e.length-1].formats();n=A.default.attributes.diff(o,r)||{}}this.quill.deleteText(t,S.default.sources.USER),Object.keys(n).length>0&&this.quill.formatLine(t.index,1,n,S.default.sources.USER),this.quill.setSelection(t.index,S.default.sources.SILENT),this.quill.focus()}function h(t,e){var n=this;t.length>0&&this.quill.scroll.deleteAt(t.index,t.length);var r=Object.keys(e.format).reduce(function(t,n){return T.default.query(n,T.default.Scope.BLOCK)&&!Array.isArray(e.format[n])&&(t[n]=e.format[n]),t},{});this.quill.insertText(t.index,"\n",r,S.default.sources.USER),this.quill.setSelection(t.index+1,S.default.sources.SILENT),this.quill.focus(),Object.keys(e.format).forEach(function(t){null==r[t]&&(Array.isArray(e.format[t])||"link"!==t&&n.quill.format(t,e.format[t],S.default.sources.USER))})}function p(t){return{key:D.keys.TAB,shiftKey:!t,format:{"code-block":!0},handler:function(e){var n=T.default.query("code-block"),r=e.index,o=e.length,i=this.quill.scroll.descendant(n,r),l=b(i,2),a=l[0],s=l[1];if(null!=a){var u=this.quill.getIndex(a),c=a.newlineIndex(s,!0)+1,f=a.newlineIndex(u+s+o),h=a.domNode.textContent.slice(c,f).split("\n");s=0,h.forEach(function(e,i){t?(a.insertAt(c+s,n.TAB),s+=n.TAB.length,0===i?r+=n.TAB.length:o+=n.TAB.length):e.startsWith(n.TAB)&&(a.deleteAt(c+s,n.TAB.length),s-=n.TAB.length,0===i?r-=n.TAB.length:o-=n.TAB.length),s+=e.length+1}),this.quill.update(S.default.sources.USER),this.quill.setSelection(r,o,S.default.sources.SILENT)}}}}function d(t){return{key:t[0].toUpperCase(),shortKey:!0,handler:function(e,n){this.quill.format(t,!n.format[t],S.default.sources.USER)}}}function y(t){if("string"==typeof t||"number"==typeof t)return y({key:t});if("object"===(void 0===t?"undefined":v(t))&&(t=(0,_.default)(t,!1)),"string"==typeof t.key)if(null!=D.keys[t.key.toUpperCase()])t.key=D.keys[t.key.toUpperCase()];else{if(1!==t.key.length)return null;t.key=t.key.toUpperCase().charCodeAt(0)}return t.shortKey&&(t[B]=t.shortKey,delete t.shortKey),t}Object.defineProperty(e,"__esModule",{value:!0}),e.SHORTKEY=e.default=void 0;var v="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},b=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),g=function(){function t(t,e){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=y(t);if(null==r||null==r.key)return I.warn("Attempted to add invalid keyboard binding",r);"function"==typeof e&&(e={handler:e}),"function"==typeof n&&(n={handler:n}),r=(0,k.default)(r,e,n),this.bindings[r.key]=this.bindings[r.key]||[],this.bindings[r.key].push(r)}},{key:"listen",value:function(){var t=this;this.quill.root.addEventListener("keydown",function(n){if(!n.defaultPrevented){var r=n.which||n.keyCode,o=(t.bindings[r]||[]).filter(function(t){return e.match(n,t)});if(0!==o.length){var i=t.quill.getSelection();if(null!=i&&t.quill.hasFocus()){var l=t.quill.getLine(i.index),a=b(l,2),s=a[0],u=a[1],c=t.quill.getLeaf(i.index),f=b(c,2),h=f[0],p=f[1],d=0===i.length?[h,p]:t.quill.getLeaf(i.index+i.length),y=b(d,2),g=y[0],m=y[1],_=h instanceof T.default.Text?h.value().slice(0,p):"",O=g instanceof T.default.Text?g.value().slice(m):"",x={collapsed:0===i.length,empty:0===i.length&&s.length()<=1,format:t.quill.getFormat(i),offset:u,prefix:_,suffix:O};o.some(function(e){if(null!=e.collapsed&&e.collapsed!==x.collapsed)return!1;if(null!=e.empty&&e.empty!==x.empty)return!1;if(null!=e.offset&&e.offset!==x.offset)return!1;if(Array.isArray(e.format)){if(e.format.every(function(t){return null==x.format[t]}))return!1}else if("object"===v(e.format)&&!Object.keys(e.format).every(function(t){return!0===e.format[t]?null!=x.format[t]:!1===e.format[t]?null==x.format[t]:(0,w.default)(e.format[t],x.format[t])}))return!1;return!(null!=e.prefix&&!e.prefix.test(x.prefix))&&(!(null!=e.suffix&&!e.suffix.test(x.suffix))&&!0!==e.handler.call(t,i,x))})&&n.preventDefault()}}}})}}]),e}(R.default);D.keys={BACKSPACE:8,TAB:9,ENTER:13,ESCAPE:27,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46},D.DEFAULTS={bindings:{bold:d("bold"),italic:d("italic"),underline:d("underline"),indent:{key:D.keys.TAB,format:["blockquote","indent","list"],handler:function(t,e){if(e.collapsed&&0!==e.offset)return!0;this.quill.format("indent","+1",S.default.sources.USER)}},outdent:{key:D.keys.TAB,shiftKey:!0,format:["blockquote","indent","list"],handler:function(t,e){if(e.collapsed&&0!==e.offset)return!0;this.quill.format("indent","-1",S.default.sources.USER)}},"outdent backspace":{key:D.keys.BACKSPACE,collapsed:!0,shiftKey:null,metaKey:null,ctrlKey:null,altKey:null,format:["indent","list"],offset:0,handler:function(t,e){null!=e.format.indent?this.quill.format("indent","-1",S.default.sources.USER):null!=e.format.list&&this.quill.format("list",!1,S.default.sources.USER)}},"indent code-block":p(!0),"outdent code-block":p(!1),"remove tab":{key:D.keys.TAB,shiftKey:!0,collapsed:!0,prefix:/\t$/,handler:function(t){this.quill.deleteText(t.index-1,1,S.default.sources.USER)}},tab:{key:D.keys.TAB,handler:function(t){this.quill.history.cutoff();var e=(new N.default).retain(t.index).delete(t.length).insert("\t");this.quill.updateContents(e,S.default.sources.USER),this.quill.history.cutoff(),this.quill.setSelection(t.index+1,S.default.sources.SILENT)}},"list empty enter":{key:D.keys.ENTER,collapsed:!0,format:["list"],empty:!0,handler:function(t,e){this.quill.format("list",!1,S.default.sources.USER),e.format.indent&&this.quill.format("indent",!1,S.default.sources.USER)}},"checklist enter":{key:D.keys.ENTER,collapsed:!0,format:{list:"checked"},handler:function(t){var e=this.quill.getLine(t.index),n=b(e,2),r=n[0],o=n[1],i=(0,k.default)({},r.formats(),{list:"checked"}),l=(new N.default).retain(t.index).insert("\n",i).retain(r.length()-o-1).retain(1,{list:"unchecked"});this.quill.updateContents(l,S.default.sources.USER),this.quill.setSelection(t.index+1,S.default.sources.SILENT),this.quill.scrollIntoView()}},"header enter":{key:D.keys.ENTER,collapsed:!0,format:["header"],suffix:/^$/,handler:function(t,e){var n=this.quill.getLine(t.index),r=b(n,2),o=r[0],i=r[1],l=(new N.default).retain(t.index).insert("\n",e.format).retain(o.length()-i-1).retain(1,{header:null});this.quill.updateContents(l,S.default.sources.USER),this.quill.setSelection(t.index+1,S.default.sources.SILENT),this.quill.scrollIntoView()}},"list autofill":{key:" ",collapsed:!0,format:{list:!1},prefix:/^\s*?(\d+\.|-|\*|\[ ?\]|\[x\])$/,handler:function(t,e){var n=e.prefix.length,r=this.quill.getLine(t.index),o=b(r,2),i=o[0],l=o[1];if(l>n)return!0;var a=void 0;switch(e.prefix.trim()){case"[]":case"[ ]":a="unchecked";break;case"[x]":a="checked";break;case"-":case"*":a="bullet";break;default:a="ordered"}this.quill.insertText(t.index," ",S.default.sources.USER),this.quill.history.cutoff();var s=(new N.default).retain(t.index-l).delete(n+1).retain(i.length()-2-l).retain(1,{list:a});this.quill.updateContents(s,S.default.sources.USER),this.quill.history.cutoff(),this.quill.setSelection(t.index-n,S.default.sources.SILENT)}},"code exit":{key:D.keys.ENTER,collapsed:!0,format:["code-block"],prefix:/\n\n$/,suffix:/^\s+$/,handler:function(t){var e=this.quill.getLine(t.index),n=b(e,2),r=n[0],o=n[1],i=(new N.default).retain(t.index+r.length()-o-2).retain(1,{"code-block":null}).delete(1);this.quill.updateContents(i,S.default.sources.USER)}},"embed left":s(D.keys.LEFT,!1),"embed left shift":s(D.keys.LEFT,!0),"embed right":s(D.keys.RIGHT,!1),"embed right shift":s(D.keys.RIGHT,!0)}},e.default=D,e.SHORTKEY=B},function(t,e,n){"use strict";t.exports={align:{"":n(75),center:n(76),right:n(77),justify:n(78)},background:n(79),blockquote:n(80),bold:n(81),clean:n(82),code:n(40),"code-block":n(40),color:n(83),direction:{"":n(84),rtl:n(85)},float:{center:n(86),full:n(87),left:n(88),right:n(89)},formula:n(90),header:{1:n(91),2:n(92)},italic:n(93),image:n(94),indent:{"+1":n(95),"-1":n(96)},link:n(97),list:{ordered:n(98),bullet:n(99),check:n(100)},script:{sub:n(101),super:n(102)},strike:n(103),underline:n(104),video:n(105)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1),o=function(){function t(t){this.domNode=t,this.domNode[r.DATA_KEY]={blot:this}}return Object.defineProperty(t.prototype,"statics",{get:function(){return this.constructor},enumerable:!0,configurable:!0}),t.create=function(t){if(null==this.tagName)throw new r.ParchmentError("Blot definition missing tagName");var e;return Array.isArray(this.tagName)?("string"==typeof t&&(t=t.toUpperCase(),parseInt(t).toString()===t&&(t=parseInt(t))),e="number"==typeof t?document.createElement(this.tagName[t-1]):this.tagName.indexOf(t)>-1?document.createElement(t):document.createElement(this.tagName[0])):e=document.createElement(this.tagName),this.className&&e.classList.add(this.className),e},t.prototype.attach=function(){null!=this.parent&&(this.scroll=this.parent.scroll)},t.prototype.clone=function(){var t=this.domNode.cloneNode(!1);return r.create(t)},t.prototype.detach=function(){null!=this.parent&&this.parent.removeChild(this),delete this.domNode[r.DATA_KEY]},t.prototype.deleteAt=function(t,e){this.isolate(t,e).remove()},t.prototype.formatAt=function(t,e,n,o){var i=this.isolate(t,e);if(null!=r.query(n,r.Scope.BLOT)&&o)i.wrap(n,o);else if(null!=r.query(n,r.Scope.ATTRIBUTE)){var l=r.create(this.statics.scope);i.wrap(l),l.format(n,o)}},t.prototype.insertAt=function(t,e,n){var o=null==n?r.create("text",e):r.create(e,n),i=this.split(t);this.parent.insertBefore(o,i)},t.prototype.insertInto=function(t,e){void 0===e&&(e=null),null!=this.parent&&this.parent.children.remove(this);var n=null;t.children.insertBefore(this,e),null!=e&&(n=e.domNode),this.domNode.parentNode==t.domNode&&this.domNode.nextSibling==n||t.domNode.insertBefore(this.domNode,n),this.parent=t,this.attach()},t.prototype.isolate=function(t,e){var n=this.split(t);return n.split(e),n},t.prototype.length=function(){return 1},t.prototype.offset=function(t){return void 0===t&&(t=this.parent),null==this.parent||this==t?0:this.parent.children.offset(this)+this.parent.offset(t)},t.prototype.optimize=function(t){null!=this.domNode[r.DATA_KEY]&&delete this.domNode[r.DATA_KEY].mutations},t.prototype.remove=function(){null!=this.domNode.parentNode&&this.domNode.parentNode.removeChild(this.domNode),this.detach()},t.prototype.replace=function(t){null!=t.parent&&(t.parent.insertBefore(this,t.next),t.remove())},t.prototype.replaceWith=function(t,e){var n="string"==typeof t?r.create(t,e):t;return n.replace(this),n},t.prototype.split=function(t,e){return 0===t?this:this.next},t.prototype.update=function(t,e){},t.prototype.wrap=function(t,e){var n="string"==typeof t?r.create(t,e):t;return null!=this.parent&&this.parent.insertBefore(n,this.next),n.appendChild(this),n},t.blotName="abstract",t}();e.default=o},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(11),o=n(29),i=n(30),l=n(1),a=function(){function t(t){this.attributes={},this.domNode=t,this.build()}return t.prototype.attribute=function(t,e){e?t.add(this.domNode,e)&&(null!=t.value(this.domNode)?this.attributes[t.attrName]=t:delete this.attributes[t.attrName]):(t.remove(this.domNode),delete this.attributes[t.attrName])},t.prototype.build=function(){var t=this;this.attributes={};var e=r.default.keys(this.domNode),n=o.default.keys(this.domNode),a=i.default.keys(this.domNode);e.concat(n).concat(a).forEach(function(e){var n=l.query(e,l.Scope.ATTRIBUTE);n instanceof r.default&&(t.attributes[n.attrName]=n)})},t.prototype.copy=function(t){var e=this;Object.keys(this.attributes).forEach(function(n){var r=e.attributes[n].value(e.domNode);t.format(n,r)})},t.prototype.move=function(t){var e=this;this.copy(t),Object.keys(this.attributes).forEach(function(t){e.attributes[t].remove(e.domNode)}),this.attributes={}},t.prototype.values=function(){var t=this;return Object.keys(this.attributes).reduce(function(e,n){return e[n]=t.attributes[n].value(t.domNode),e},{})},t}();e.default=a},function(t,e,n){"use strict";function r(t,e){return(t.getAttribute("class")||"").split(/\s+/).filter(function(t){return 0===t.indexOf(e+"-")})}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(11),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return o(e,t),e.keys=function(t){return(t.getAttribute("class")||"").split(/\s+/).map(function(t){return t.split("-").slice(0,-1).join("-")})},e.prototype.add=function(t,e){return!!this.canAdd(t,e)&&(this.remove(t),t.classList.add(this.keyName+"-"+e),!0)},e.prototype.remove=function(t){r(t,this.keyName).forEach(function(e){t.classList.remove(e)}),0===t.classList.length&&t.removeAttribute("class")},e.prototype.value=function(t){var e=r(t,this.keyName)[0]||"",n=e.slice(this.keyName.length+1);return this.canAdd(t,n)?n:""},e}(i.default);e.default=l},function(t,e,n){"use strict";function r(t){var e=t.split("-"),n=e.slice(1).map(function(t){return t[0].toUpperCase()+t.slice(1)}).join("");return e[0]+n}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(11),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return o(e,t),e.keys=function(t){return(t.getAttribute("style")||"").split(";").map(function(t){return t.split(":")[0].trim()})},e.prototype.add=function(t,e){return!!this.canAdd(t,e)&&(t.style[r(this.keyName)]=e,!0)},e.prototype.remove=function(t){t.style[r(this.keyName)]="",t.getAttribute("style")||t.removeAttribute("style")},e.prototype.value=function(t){var e=t.style[r(this.keyName)];return this.canAdd(t,e)?e:""},e}(i.default);e.default=l},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),s=function t(e,n,r){null===e&&(e=Function.prototype);var o=Object.getOwnPropertyDescriptor(e,n);if(void 0===o){var i=Object.getPrototypeOf(e);return null===i?void 0:t(i,n,r)}if("value"in o)return o.value;var l=o.get;if(void 0!==l)return l.call(r)},u=function(){function t(t,e){for(var n=0;n '},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;nr.right&&(i=r.right-o.right,this.root.style.left=e+i+"px"),o.leftr.bottom){var l=o.bottom-o.top,a=t.bottom-t.top+l;this.root.style.top=n-a+"px",this.root.classList.add("ql-flip")}return i}},{key:"show",value:function(){this.root.classList.remove("ql-editing"),this.root.classList.remove("ql-hidden")}}]),t}();e.default=i},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function a(t){var e=t.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtube\.com\/watch.*v=([a-zA-Z0-9_-]+)/)||t.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtu\.be\/([a-zA-Z0-9_-]+)/);return e?(e[1]||"https")+"://www.youtube.com/embed/"+e[2]+"?showinfo=0":(e=t.match(/^(?:(https?):\/\/)?(?:www\.)?vimeo\.com\/(\d+)/))?(e[1]||"https")+"://player.vimeo.com/video/"+e[2]+"/":t}function s(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];e.forEach(function(e){var r=document.createElement("option");e===n?r.setAttribute("selected","selected"):r.setAttribute("value",e),t.appendChild(r)})}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.BaseTooltip=void 0;var u=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:"link",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;this.root.classList.remove("ql-hidden"),this.root.classList.add("ql-editing"),null!=e?this.textbox.value=e:t!==this.root.getAttribute("data-mode")&&(this.textbox.value=""),this.position(this.quill.getBounds(this.quill.selection.savedRange)),this.textbox.select(),this.textbox.setAttribute("placeholder",this.textbox.getAttribute("data-"+t)||""),this.root.setAttribute("data-mode",t)}},{key:"restoreFocus",value:function(){var t=this.quill.scrollingContainer.scrollTop;this.quill.focus(),this.quill.scrollingContainer.scrollTop=t}},{key:"save",value:function(){var t=this.textbox.value;switch(this.root.getAttribute("data-mode")){case"link":var e=this.quill.root.scrollTop;this.linkRange?(this.quill.formatText(this.linkRange,"link",t,v.default.sources.USER),delete this.linkRange):(this.restoreFocus(),this.quill.format("link",t,v.default.sources.USER)),this.quill.root.scrollTop=e;break;case"video":t=a(t);case"formula":if(!t)break;var n=this.quill.getSelection(!0);if(null!=n){var r=n.index+n.length;this.quill.insertEmbed(r,this.root.getAttribute("data-mode"),t,v.default.sources.USER),"formula"===this.root.getAttribute("data-mode")&&this.quill.insertText(r+1," ",v.default.sources.USER),this.quill.setSelection(r+2,v.default.sources.USER)}}this.textbox.value="",this.hide()}}]),e}(A.default);e.BaseTooltip=M,e.default=L},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(46),i=r(o),l=n(34),a=n(36),s=n(62),u=n(63),c=r(u),f=n(64),h=r(f),p=n(65),d=r(p),y=n(35),v=n(24),b=n(37),g=n(38),m=n(39),_=r(m),O=n(66),w=r(O),x=n(15),k=r(x),E=n(67),N=r(E),j=n(68),A=r(j),q=n(69),T=r(q),P=n(70),S=r(P),C=n(71),L=r(C),M=n(13),R=r(M),I=n(72),B=r(I),D=n(73),U=r(D),F=n(74),H=r(F),K=n(26),z=r(K),Z=n(16),V=r(Z),W=n(41),G=r(W),Y=n(42),X=r(Y),$=n(43),Q=r($),J=n(107),tt=r(J),et=n(108),nt=r(et);i.default.register({"attributors/attribute/direction":a.DirectionAttribute,"attributors/class/align":l.AlignClass,"attributors/class/background":y.BackgroundClass,"attributors/class/color":v.ColorClass,"attributors/class/direction":a.DirectionClass,"attributors/class/font":b.FontClass,"attributors/class/size":g.SizeClass,"attributors/style/align":l.AlignStyle,"attributors/style/background":y.BackgroundStyle,"attributors/style/color":v.ColorStyle,"attributors/style/direction":a.DirectionStyle,"attributors/style/font":b.FontStyle,"attributors/style/size":g.SizeStyle},!0),i.default.register({"formats/align":l.AlignClass,"formats/direction":a.DirectionClass,"formats/indent":s.IndentClass,"formats/background":y.BackgroundStyle,"formats/color":v.ColorStyle,"formats/font":b.FontClass,"formats/size":g.SizeClass,"formats/blockquote":c.default,"formats/code-block":R.default,"formats/header":h.default,"formats/list":d.default,"formats/bold":_.default,"formats/code":M.Code,"formats/italic":w.default,"formats/link":k.default,"formats/script":N.default,"formats/strike":A.default,"formats/underline":T.default,"formats/image":S.default,"formats/video":L.default,"formats/list/item":p.ListItem,"modules/formula":B.default,"modules/syntax":U.default,"modules/toolbar":H.default,"themes/bubble":tt.default,"themes/snow":nt.default,"ui/icons":z.default,"ui/picker":V.default,"ui/icon-picker":X.default,"ui/color-picker":G.default,"ui/tooltip":Q.default},!0),e.default=i.default},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(0),i=r(o),l=n(6),a=r(l),s=n(3),u=r(s),c=n(14),f=r(c),h=n(23),p=r(h),d=n(31),y=r(d),v=n(33),b=r(v),g=n(5),m=r(g),_=n(59),O=r(_),w=n(8),x=r(w),k=n(60),E=r(k),N=n(61),j=r(N),A=n(25),q=r(A);a.default.register({"blots/block":u.default,"blots/block/embed":s.BlockEmbed,"blots/break":f.default,"blots/container":p.default,"blots/cursor":y.default,"blots/embed":b.default,"blots/inline":m.default,"blots/scroll":O.default,"blots/text":x.default,"modules/clipboard":E.default,"modules/history":j.default,"modules/keyboard":q.default}),i.default.register(u.default,f.default,y.default,m.default,O.default,x.default),e.default=a.default},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(){this.head=this.tail=null,this.length=0}return t.prototype.append=function(){for(var t=[],e=0;e1&&this.append.apply(this,t.slice(1))},t.prototype.contains=function(t){for(var e,n=this.iterator();e=n();)if(e===t)return!0;return!1},t.prototype.insertBefore=function(t,e){t&&(t.next=e,null!=e?(t.prev=e.prev,null!=e.prev&&(e.prev.next=t),e.prev=t,e===this.head&&(this.head=t)):null!=this.tail?(this.tail.next=t,t.prev=this.tail,this.tail=t):(t.prev=null,this.head=this.tail=t),this.length+=1)},t.prototype.offset=function(t){for(var e=0,n=this.head;null!=n;){if(n===t)return e;e+=n.length(),n=n.next}return-1},t.prototype.remove=function(t){this.contains(t)&&(null!=t.prev&&(t.prev.next=t.next),null!=t.next&&(t.next.prev=t.prev),t===this.head&&(this.head=t.next),t===this.tail&&(this.tail=t.prev),this.length-=1)},t.prototype.iterator=function(t){return void 0===t&&(t=this.head),function(){var e=t;return null!=t&&(t=t.next),e}},t.prototype.find=function(t,e){void 0===e&&(e=!1);for(var n,r=this.iterator();n=r();){var o=n.length();if(ta?n(r,t-a,Math.min(e,a+u-t)):n(r,0,Math.min(u,t+e-a)),a+=u}},t.prototype.map=function(t){return this.reduce(function(e,n){return e.push(t(n)),e},[])},t.prototype.reduce=function(t,e){for(var n,r=this.iterator();n=r();)e=t(e,n);return e},t}();e.default=r},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(17),i=n(1),l={attributes:!0,characterData:!0,characterDataOldValue:!0,childList:!0,subtree:!0},a=function(t){function e(e){var n=t.call(this,e)||this;return n.scroll=n,n.observer=new MutationObserver(function(t){n.update(t)}),n.observer.observe(n.domNode,l),n.attach(),n}return r(e,t),e.prototype.detach=function(){t.prototype.detach.call(this),this.observer.disconnect()},e.prototype.deleteAt=function(e,n){this.update(),0===e&&n===this.length()?this.children.forEach(function(t){t.remove()}):t.prototype.deleteAt.call(this,e,n)},e.prototype.formatAt=function(e,n,r,o){this.update(),t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.insertAt=function(e,n,r){this.update(),t.prototype.insertAt.call(this,e,n,r)},e.prototype.optimize=function(e,n){var r=this;void 0===e&&(e=[]),void 0===n&&(n={}),t.prototype.optimize.call(this,n);for(var l=[].slice.call(this.observer.takeRecords());l.length>0;)e.push(l.pop());for(var a=function(t,e){void 0===e&&(e=!0),null!=t&&t!==r&&null!=t.domNode.parentNode&&(null==t.domNode[i.DATA_KEY].mutations&&(t.domNode[i.DATA_KEY].mutations=[]),e&&a(t.parent))},s=function(t){null!=t.domNode[i.DATA_KEY]&&null!=t.domNode[i.DATA_KEY].mutations&&(t instanceof o.default&&t.children.forEach(s),t.optimize(n))},u=e,c=0;u.length>0;c+=1){if(c>=100)throw new Error("[Parchment] Maximum optimize iterations reached");for(u.forEach(function(t){var e=i.find(t.target,!0);null!=e&&(e.domNode===t.target&&("childList"===t.type?(a(i.find(t.previousSibling,!1)),[].forEach.call(t.addedNodes,function(t){var e=i.find(t,!1);a(e,!1),e instanceof o.default&&e.children.forEach(function(t){a(t,!1)})})):"attributes"===t.type&&a(e.prev)),a(e))}),this.children.forEach(s),u=[].slice.call(this.observer.takeRecords()),l=u.slice();l.length>0;)e.push(l.pop())}},e.prototype.update=function(e,n){var r=this;void 0===n&&(n={}),e=e||this.observer.takeRecords(),e.map(function(t){var e=i.find(t.target,!0);return null==e?null:null==e.domNode[i.DATA_KEY].mutations?(e.domNode[i.DATA_KEY].mutations=[t],e):(e.domNode[i.DATA_KEY].mutations.push(t),null)}).forEach(function(t){null!=t&&t!==r&&null!=t.domNode[i.DATA_KEY]&&t.update(t.domNode[i.DATA_KEY].mutations||[],n)}),null!=this.domNode[i.DATA_KEY].mutations&&t.prototype.update.call(this,this.domNode[i.DATA_KEY].mutations,n),this.optimize(e,n)},e.blotName="scroll",e.defaultChild="block",e.scope=i.Scope.BLOCK_BLOT,e.tagName="DIV",e}(o.default);e.default=a},function(t,e,n){"use strict";function r(t,e){if(Object.keys(t).length!==Object.keys(e).length)return!1;for(var n in t)if(t[n]!==e[n])return!1;return!0}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(18),l=n(1),a=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return o(e,t),e.formats=function(n){if(n.tagName!==e.tagName)return t.formats.call(this,n)},e.prototype.format=function(n,r){var o=this;n!==this.statics.blotName||r?t.prototype.format.call(this,n,r):(this.children.forEach(function(t){t instanceof i.default||(t=t.wrap(e.blotName,!0)),o.attributes.copy(t)}),this.unwrap())},e.prototype.formatAt=function(e,n,r,o){if(null!=this.formats()[r]||l.query(r,l.Scope.ATTRIBUTE)){this.isolate(e,n).format(r,o)}else t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.optimize=function(n){t.prototype.optimize.call(this,n);var o=this.formats();if(0===Object.keys(o).length)return this.unwrap();var i=this.next;i instanceof e&&i.prev===this&&r(o,i.formats())&&(i.moveChildren(this),i.remove())},e.blotName="inline",e.scope=l.Scope.INLINE_BLOT,e.tagName="SPAN",e}(i.default);e.default=a},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(18),i=n(1),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r(e,t),e.formats=function(n){var r=i.query(e.blotName).tagName;if(n.tagName!==r)return t.formats.call(this,n)},e.prototype.format=function(n,r){null!=i.query(n,i.Scope.BLOCK)&&(n!==this.statics.blotName||r?t.prototype.format.call(this,n,r):this.replaceWith(e.blotName))},e.prototype.formatAt=function(e,n,r,o){null!=i.query(r,i.Scope.BLOCK)?this.format(r,o):t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.insertAt=function(e,n,r){if(null==r||null!=i.query(n,i.Scope.INLINE))t.prototype.insertAt.call(this,e,n,r);else{var o=this.split(e),l=i.create(n,r);o.parent.insertBefore(l,o)}},e.prototype.update=function(e,n){navigator.userAgent.match(/Trident/)?this.build():t.prototype.update.call(this,e,n)},e.blotName="block",e.scope=i.Scope.BLOCK_BLOT,e.tagName="P",e}(o.default);e.default=l},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(19),i=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r(e,t),e.formats=function(t){},e.prototype.format=function(e,n){t.prototype.formatAt.call(this,0,this.length(),e,n)},e.prototype.formatAt=function(e,n,r,o){0===e&&n===this.length()?this.format(r,o):t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.formats=function(){return this.statics.formats(this.domNode)},e}(o.default);e.default=i},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(19),i=n(1),l=function(t){function e(e){var n=t.call(this,e)||this;return n.text=n.statics.value(n.domNode),n}return r(e,t),e.create=function(t){return document.createTextNode(t)},e.value=function(t){var e=t.data;return e.normalize&&(e=e.normalize()),e},e.prototype.deleteAt=function(t,e){this.domNode.data=this.text=this.text.slice(0,t)+this.text.slice(t+e)},e.prototype.index=function(t,e){return this.domNode===t?e:-1},e.prototype.insertAt=function(e,n,r){null==r?(this.text=this.text.slice(0,e)+n+this.text.slice(e),this.domNode.data=this.text):t.prototype.insertAt.call(this,e,n,r)},e.prototype.length=function(){return this.text.length},e.prototype.optimize=function(n){t.prototype.optimize.call(this,n),this.text=this.statics.value(this.domNode),0===this.text.length?this.remove():this.next instanceof e&&this.next.prev===this&&(this.insertAt(this.length(),this.next.value()),this.next.remove())},e.prototype.position=function(t,e){return void 0===e&&(e=!1),[this.domNode,t]},e.prototype.split=function(t,e){if(void 0===e&&(e=!1),!e){if(0===t)return this;if(t===this.length())return this.next}var n=i.create(this.domNode.splitText(t));return this.parent.insertBefore(n,this.next),this.text=this.statics.value(this.domNode),n},e.prototype.update=function(t,e){var n=this;t.some(function(t){return"characterData"===t.type&&t.target===n.domNode})&&(this.text=this.statics.value(this.domNode))},e.prototype.value=function(){return this.text},e.blotName="text",e.scope=i.Scope.INLINE_BLOT,e}(o.default);e.default=l},function(t,e,n){"use strict";var r=document.createElement("div");if(r.classList.toggle("test-class",!1),r.classList.contains("test-class")){var o=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(t,e){return arguments.length>1&&!this.contains(t)==!e?e:o.call(this,t)}}String.prototype.startsWith||(String.prototype.startsWith=function(t,e){return e=e||0,this.substr(e,t.length)===t}),String.prototype.endsWith||(String.prototype.endsWith=function(t,e){var n=this.toString();("number"!=typeof e||!isFinite(e)||Math.floor(e)!==e||e>n.length)&&(e=n.length),e-=t.length;var r=n.indexOf(t,e);return-1!==r&&r===e}),Array.prototype.find||Object.defineProperty(Array.prototype,"find",{value:function(t){if(null===this)throw new TypeError("Array.prototype.find called on null or undefined");if("function"!=typeof t)throw new TypeError("predicate must be a function");for(var e,n=Object(this),r=n.length>>>0,o=arguments[1],i=0;ie.length?t:e,l=t.length>e.length?e:t,a=i.indexOf(l);if(-1!=a)return r=[[y,i.substring(0,a)],[v,l],[y,i.substring(a+l.length)]],t.length>e.length&&(r[0][0]=r[2][0]=d),r;if(1==l.length)return[[d,t],[y,e]];var u=s(t,e);if(u){var c=u[0],f=u[1],h=u[2],p=u[3],b=u[4],g=n(c,h),m=n(f,p);return g.concat([[v,b]],m)}return o(t,e)}function o(t,e){for(var n=t.length,r=e.length,o=Math.ceil((n+r)/2),l=o,a=2*o,s=new Array(a),u=new Array(a),c=0;cn)v+=2;else if(x>r)p+=2;else if(h){var k=l+f-_;if(k>=0&&k=E)return i(t,e,O,x)}}}for(var N=-m+b;N<=m-g;N+=2){var E,k=l+N;E=N==-m||N!=m&&u[k-1]n)g+=2;else if(j>r)b+=2;else if(!h){var w=l+f-N;if(w>=0&&w=E)return i(t,e,O,x)}}}}return[[d,t],[y,e]]}function i(t,e,r,o){var i=t.substring(0,r),l=e.substring(0,o),a=t.substring(r),s=e.substring(o),u=n(i,l),c=n(a,s);return u.concat(c)}function l(t,e){if(!t||!e||t.charAt(0)!=e.charAt(0))return 0;for(var n=0,r=Math.min(t.length,e.length),o=r,i=0;n=t.length?[r,o,i,s,f]:null}var r=t.length>e.length?t:e,o=t.length>e.length?e:t;if(r.length<4||2*o.lengthu[4].length?s:u:s;var c,f,h,p;return t.length>e.length?(c=i[0],f=i[1],h=i[2],p=i[3]):(h=i[0],p=i[1],c=i[2],f=i[3]),[c,f,h,p,i[4]]}function u(t){t.push([v,""]);for(var e,n=0,r=0,o=0,i="",s="";n1?(0!==r&&0!==o&&(e=l(s,i),0!==e&&(n-r-o>0&&t[n-r-o-1][0]==v?t[n-r-o-1][1]+=s.substring(0,e):(t.splice(0,0,[v,s.substring(0,e)]),n++),s=s.substring(e),i=i.substring(e)),0!==(e=a(s,i))&&(t[n][1]=s.substring(s.length-e)+t[n][1],s=s.substring(0,s.length-e),i=i.substring(0,i.length-e))),0===r?t.splice(n-o,r+o,[y,s]):0===o?t.splice(n-r,r+o,[d,i]):t.splice(n-r-o,r+o,[d,i],[y,s]),n=n-r-o+(r?1:0)+(o?1:0)+1):0!==n&&t[n-1][0]==v?(t[n-1][1]+=t[n][1],t.splice(n,1)):n++,o=0,r=0,i="",s=""}""===t[t.length-1][1]&&t.pop();var c=!1;for(n=1;n0&&r.splice(o+2,0,[l[0],a]),p(r,o,3)}return t}function h(t){for(var e=!1,n=function(t){return t.charCodeAt(0)>=56320&&t.charCodeAt(0)<=57343},r=2;r=55296&&t.charCodeAt(t.length-1)<=56319}(t[r-2][1])&&t[r-1][0]===d&&n(t[r-1][1])&&t[r][0]===y&&n(t[r][1])&&(e=!0,t[r-1][1]=t[r-2][1].slice(-1)+t[r-1][1],t[r][1]=t[r-2][1].slice(-1)+t[r][1],t[r-2][1]=t[r-2][1].slice(0,-1));if(!e)return t;for(var o=[],r=0;r0&&o.push(t[r]);return o}function p(t,e,n){for(var r=e+n-1;r>=0&&r>=e-1;r--)if(r+1=r&&!a.endsWith("\n")&&(n=!0),e.scroll.insertAt(t,a);var c=e.scroll.line(t),f=u(c,2),h=f[0],p=f[1],y=(0,T.default)({},(0,O.bubbleFormats)(h));if(h instanceof w.default){var b=h.descendant(v.default.Leaf,p),g=u(b,1),m=g[0];y=(0,T.default)(y,(0,O.bubbleFormats)(m))}l=d.default.attributes.diff(y,l)||{}}else if("object"===s(o.insert)){var _=Object.keys(o.insert)[0];if(null==_)return t;e.scroll.insertAt(t,_,o.insert[_])}r+=i}return Object.keys(l).forEach(function(n){e.scroll.formatAt(t,i,n,l[n])}),t+i},0),t.reduce(function(t,n){return"number"==typeof n.delete?(e.scroll.deleteAt(t,n.delete),t):t+(n.retain||n.insert.length||1)},0),this.scroll.batchEnd(),this.update(t)}},{key:"deleteText",value:function(t,e){return this.scroll.deleteAt(t,e),this.update((new h.default).retain(t).delete(e))}},{key:"formatLine",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return this.scroll.update(),Object.keys(r).forEach(function(o){if(null==n.scroll.whitelist||n.scroll.whitelist[o]){var i=n.scroll.lines(t,Math.max(e,1)),l=e;i.forEach(function(e){var i=e.length();if(e instanceof g.default){var a=t-e.offset(n.scroll),s=e.newlineIndex(a+l)-a+1;e.formatAt(a,s,o,r[o])}else e.format(o,r[o]);l-=i})}}),this.scroll.optimize(),this.update((new h.default).retain(t).retain(e,(0,N.default)(r)))}},{key:"formatText",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return Object.keys(r).forEach(function(o){n.scroll.formatAt(t,e,o,r[o])}),this.update((new h.default).retain(t).retain(e,(0,N.default)(r)))}},{key:"getContents",value:function(t,e){return this.delta.slice(t,t+e)}},{key:"getDelta",value:function(){return this.scroll.lines().reduce(function(t,e){return t.concat(e.delta())},new h.default)}},{key:"getFormat",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=[],r=[];0===e?this.scroll.path(t).forEach(function(t){var e=u(t,1),o=e[0];o instanceof w.default?n.push(o):o instanceof v.default.Leaf&&r.push(o)}):(n=this.scroll.lines(t,e),r=this.scroll.descendants(v.default.Leaf,t,e));var o=[n,r].map(function(t){if(0===t.length)return{};for(var e=(0,O.bubbleFormats)(t.shift());Object.keys(e).length>0;){var n=t.shift();if(null==n)return e;e=l((0,O.bubbleFormats)(n),e)}return e});return T.default.apply(T.default,o)}},{key:"getText",value:function(t,e){return this.getContents(t,e).filter(function(t){return"string"==typeof t.insert}).map(function(t){return t.insert}).join("")}},{key:"insertEmbed",value:function(t,e,n){return this.scroll.insertAt(t,e,n),this.update((new h.default).retain(t).insert(o({},e,n)))}},{key:"insertText",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return e=e.replace(/\r\n/g,"\n").replace(/\r/g,"\n"),this.scroll.insertAt(t,e),Object.keys(r).forEach(function(o){n.scroll.formatAt(t,e.length,o,r[o])}),this.update((new h.default).retain(t).insert(e,(0,N.default)(r)))}},{key:"isBlank",value:function(){if(0==this.scroll.children.length)return!0;if(this.scroll.children.length>1)return!1;var t=this.scroll.children.head;return t.statics.blotName===w.default.blotName&&(!(t.children.length>1)&&t.children.head instanceof k.default)}},{key:"removeFormat",value:function(t,e){var n=this.getText(t,e),r=this.scroll.line(t+e),o=u(r,2),i=o[0],l=o[1],a=0,s=new h.default;null!=i&&(a=i instanceof g.default?i.newlineIndex(l)-l+1:i.length()-l,s=i.delta().slice(l,l+a-1).insert("\n"));var c=this.getContents(t,e+a),f=c.diff((new h.default).insert(n).concat(s)),p=(new h.default).retain(t).concat(f);return this.applyDelta(p)}},{key:"update",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,r=this.delta;if(1===e.length&&"characterData"===e[0].type&&e[0].target.data.match(P)&&v.default.find(e[0].target)){var o=v.default.find(e[0].target),i=(0,O.bubbleFormats)(o),l=o.offset(this.scroll),a=e[0].oldValue.replace(_.default.CONTENTS,""),s=(new h.default).insert(a),u=(new h.default).insert(o.value());t=(new h.default).retain(l).concat(s.diff(u,n)).reduce(function(t,e){return e.insert?t.insert(e.insert,i):t.push(e)},new h.default),this.delta=r.compose(t)}else this.delta=this.getDelta(),t&&(0,A.default)(r.compose(t),this.delta)||(t=r.diff(this.delta,n));return t}}]),t}();e.default=S},function(t,e){"use strict";function n(){}function r(t,e,n){this.fn=t,this.context=e,this.once=n||!1}function o(){this._events=new n,this._eventsCount=0}var i=Object.prototype.hasOwnProperty,l="~";Object.create&&(n.prototype=Object.create(null),(new n).__proto__||(l=!1)),o.prototype.eventNames=function(){var t,e,n=[];if(0===this._eventsCount)return n;for(e in t=this._events)i.call(t,e)&&n.push(l?e.slice(1):e);return Object.getOwnPropertySymbols?n.concat(Object.getOwnPropertySymbols(t)):n},o.prototype.listeners=function(t,e){var n=l?l+t:t,r=this._events[n];if(e)return!!r;if(!r)return[];if(r.fn)return[r.fn];for(var o=0,i=r.length,a=new Array(i);o0){if(i instanceof y.BlockEmbed||f instanceof y.BlockEmbed)return void this.optimize();if(i instanceof _.default){var h=i.newlineIndex(i.length(),!0);if(h>-1&&(i=i.split(h+1))===f)return void this.optimize()}else if(f instanceof _.default){var p=f.newlineIndex(0);p>-1&&f.split(p+1)}var d=f.children.head instanceof g.default?null:f.children.head;i.moveChildren(f,d),i.remove()}this.optimize()}},{key:"enable",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.domNode.setAttribute("contenteditable",t)}},{key:"formatAt",value:function(t,n,r,o){(null==this.whitelist||this.whitelist[r])&&(c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"formatAt",this).call(this,t,n,r,o),this.optimize())}},{key:"insertAt",value:function(t,n,r){if(null==r||null==this.whitelist||this.whitelist[n]){if(t>=this.length())if(null==r||null==h.default.query(n,h.default.Scope.BLOCK)){var o=h.default.create(this.statics.defaultChild);this.appendChild(o),null==r&&n.endsWith("\n")&&(n=n.slice(0,-1)),o.insertAt(0,n,r)}else{var i=h.default.create(n,r);this.appendChild(i)}else c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"insertAt",this).call(this,t,n,r);this.optimize()}}},{key:"insertBefore",value:function(t,n){if(t.statics.scope===h.default.Scope.INLINE_BLOT){var r=h.default.create(this.statics.defaultChild);r.appendChild(t),t=r}c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"insertBefore",this).call(this,t,n)}},{key:"leaf",value:function(t){return this.path(t).pop()||[null,-1]}},{key:"line",value:function(t){return t===this.length()?this.line(t-1):this.descendant(a,t)}},{key:"lines",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Number.MAX_VALUE;return function t(e,n,r){var o=[],i=r;return e.children.forEachAt(n,r,function(e,n,r){a(e)?o.push(e):e instanceof h.default.Container&&(o=o.concat(t(e,n,i))),i-=r}),o}(this,t,e)}},{key:"optimize",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};!0!==this.batch&&(c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"optimize",this).call(this,t,n),t.length>0&&this.emitter.emit(d.default.events.SCROLL_OPTIMIZE,t,n))}},{key:"path",value:function(t){return c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"path",this).call(this,t).slice(1)}},{key:"update",value:function(t){if(!0!==this.batch){var n=d.default.sources.USER;"string"==typeof t&&(n=t),Array.isArray(t)||(t=this.observer.takeRecords()),t.length>0&&this.emitter.emit(d.default.events.SCROLL_BEFORE_UPDATE,n,t),c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"update",this).call(this,t.concat([])),t.length>0&&this.emitter.emit(d.default.events.SCROLL_UPDATE,n,t)}}}]),e}(h.default.Scroll);x.blotName="scroll",x.className="ql-editor",x.tagName="DIV",x.defaultChild="block",x.allowedChildren=[v.default,y.BlockEmbed,w.default],e.default=x},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function l(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function s(t,e,n){return"object"===(void 0===e?"undefined":x(e))?Object.keys(e).reduce(function(t,n){return s(t,n,e[n])},t):t.reduce(function(t,r){return r.attributes&&r.attributes[e]?t.push(r):t.insert(r.insert,(0,j.default)({},o({},e,n),r.attributes))},new q.default)}function u(t){if(t.nodeType!==Node.ELEMENT_NODE)return{};return t["__ql-computed-style"]||(t["__ql-computed-style"]=window.getComputedStyle(t))}function c(t,e){for(var n="",r=t.ops.length-1;r>=0&&n.length-1}function h(t,e,n){return t.nodeType===t.TEXT_NODE?n.reduce(function(e,n){return n(t,e)},new q.default):t.nodeType===t.ELEMENT_NODE?[].reduce.call(t.childNodes||[],function(r,o){var i=h(o,e,n);return o.nodeType===t.ELEMENT_NODE&&(i=e.reduce(function(t,e){return e(o,t)},i),i=(o[W]||[]).reduce(function(t,e){return e(o,t)},i)),r.concat(i)},new q.default):new q.default}function p(t,e,n){return s(n,t,!0)}function d(t,e){var n=P.default.Attributor.Attribute.keys(t),r=P.default.Attributor.Class.keys(t),o=P.default.Attributor.Style.keys(t),i={};return n.concat(r).concat(o).forEach(function(e){var n=P.default.query(e,P.default.Scope.ATTRIBUTE);null!=n&&(i[n.attrName]=n.value(t),i[n.attrName])||(n=Y[e],null==n||n.attrName!==e&&n.keyName!==e||(i[n.attrName]=n.value(t)||void 0),null==(n=X[e])||n.attrName!==e&&n.keyName!==e||(n=X[e],i[n.attrName]=n.value(t)||void 0))}),Object.keys(i).length>0&&(e=s(e,i)),e}function y(t,e){var n=P.default.query(t);if(null==n)return e;if(n.prototype instanceof P.default.Embed){var r={},o=n.value(t);null!=o&&(r[n.blotName]=o,e=(new q.default).insert(r,n.formats(t)))}else"function"==typeof n.formats&&(e=s(e,n.blotName,n.formats(t)));return e}function v(t,e){return c(e,"\n")||e.insert("\n"),e}function b(){return new q.default}function g(t,e){var n=P.default.query(t);if(null==n||"list-item"!==n.blotName||!c(e,"\n"))return e;for(var r=-1,o=t.parentNode;!o.classList.contains("ql-clipboard");)"list"===(P.default.query(o)||{}).blotName&&(r+=1),o=o.parentNode;return r<=0?e:e.compose((new q.default).retain(e.length()-1).retain(1,{indent:r}))}function m(t,e){return c(e,"\n")||(f(t)||e.length()>0&&t.nextSibling&&f(t.nextSibling))&&e.insert("\n"),e}function _(t,e){if(f(t)&&null!=t.nextElementSibling&&!c(e,"\n\n")){var n=t.offsetHeight+parseFloat(u(t).marginTop)+parseFloat(u(t).marginBottom);t.nextElementSibling.offsetTop>t.offsetTop+1.5*n&&e.insert("\n")}return e}function O(t,e){var n={},r=t.style||{};return r.fontStyle&&"italic"===u(t).fontStyle&&(n.italic=!0),r.fontWeight&&(u(t).fontWeight.startsWith("bold")||parseInt(u(t).fontWeight)>=700)&&(n.bold=!0),Object.keys(n).length>0&&(e=s(e,n)),parseFloat(r.textIndent||0)>0&&(e=(new q.default).insert("\t").concat(e)),e}function w(t,e){var n=t.data;if("O:P"===t.parentNode.tagName)return e.insert(n.trim());if(0===n.trim().length&&t.parentNode.classList.contains("ql-clipboard"))return e;if(!u(t.parentNode).whiteSpace.startsWith("pre")){var r=function(t,e){return e=e.replace(/[^\u00a0]/g,""),e.length<1&&t?" ":e};n=n.replace(/\r\n/g," ").replace(/\n/g," "),n=n.replace(/\s\s+/g,r.bind(r,!0)),(null==t.previousSibling&&f(t.parentNode)||null!=t.previousSibling&&f(t.previousSibling))&&(n=n.replace(/^\s+/,r.bind(r,!1))),(null==t.nextSibling&&f(t.parentNode)||null!=t.nextSibling&&f(t.nextSibling))&&(n=n.replace(/\s+$/,r.bind(r,!1)))}return e.insert(n)}Object.defineProperty(e,"__esModule",{value:!0}),e.matchText=e.matchSpacing=e.matchNewline=e.matchBlot=e.matchAttributor=e.default=void 0;var x="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},k=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),E=function(){function t(t,e){for(var n=0;n\r?\n +\<"),this.convert();var e=this.quill.getFormat(this.quill.selection.savedRange.index);if(e[F.default.blotName]){var n=this.container.innerText;return this.container.innerHTML="",(new q.default).insert(n,o({},F.default.blotName,e[F.default.blotName]))}var r=this.prepareMatching(),i=k(r,2),l=i[0],a=i[1],s=h(this.container,l,a);return c(s,"\n")&&null==s.ops[s.ops.length-1].attributes&&(s=s.compose((new q.default).retain(s.length()-1).delete(1))),V.log("convert",this.container.innerHTML,s),this.container.innerHTML="",s}},{key:"dangerouslyPasteHTML",value:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:C.default.sources.API;if("string"==typeof t)this.quill.setContents(this.convert(t),e),this.quill.setSelection(0,C.default.sources.SILENT);else{var r=this.convert(e);this.quill.updateContents((new q.default).retain(t).concat(r),n),this.quill.setSelection(t+r.length(),C.default.sources.SILENT)}}},{key:"onPaste",value:function(t){var e=this;if(!t.defaultPrevented&&this.quill.isEnabled()){var n=this.quill.getSelection(),r=(new q.default).retain(n.index),o=this.quill.scrollingContainer.scrollTop;this.container.focus(),this.quill.selection.update(C.default.sources.SILENT),setTimeout(function(){r=r.concat(e.convert()).delete(n.length),e.quill.updateContents(r,C.default.sources.USER),e.quill.setSelection(r.length()-n.length,C.default.sources.SILENT),e.quill.scrollingContainer.scrollTop=o,e.quill.focus()},1)}}},{key:"prepareMatching",value:function(){var t=this,e=[],n=[];return this.matchers.forEach(function(r){var o=k(r,2),i=o[0],l=o[1];switch(i){case Node.TEXT_NODE:n.push(l);break;case Node.ELEMENT_NODE:e.push(l);break;default:[].forEach.call(t.container.querySelectorAll(i),function(t){t[W]=t[W]||[],t[W].push(l)})}}),[e,n]}}]),e}(I.default);$.DEFAULTS={matchers:[],matchVisual:!0},e.default=$,e.matchAttributor=d,e.matchBlot=y,e.matchNewline=m,e.matchSpacing=_,e.matchText=w},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function a(t){var e=t.ops[t.ops.length-1];return null!=e&&(null!=e.insert?"string"==typeof e.insert&&e.insert.endsWith("\n"):null!=e.attributes&&Object.keys(e.attributes).some(function(t){return null!=f.default.query(t,f.default.Scope.BLOCK)}))}function s(t){var e=t.reduce(function(t,e){return t+=e.delete||0},0),n=t.length()-e;return a(t)&&(n-=1),n}Object.defineProperty(e,"__esModule",{value:!0}),e.getLastChangeIndex=e.default=void 0;var u=function(){function t(t,e){for(var n=0;nr&&this.stack.undo.length>0){var o=this.stack.undo.pop();n=n.compose(o.undo),t=o.redo.compose(t)}else this.lastRecorded=r;this.stack.undo.push({redo:t,undo:n}),this.stack.undo.length>this.options.maxStack&&this.stack.undo.shift()}}},{key:"redo",value:function(){this.change("redo","undo")}},{key:"transform",value:function(t){this.stack.undo.forEach(function(e){e.undo=t.transform(e.undo,!0),e.redo=t.transform(e.redo,!0)}),this.stack.redo.forEach(function(e){e.undo=t.transform(e.undo,!0),e.redo=t.transform(e.redo,!0)})}},{key:"undo",value:function(){this.change("undo","redo")}}]),e}(y.default);v.DEFAULTS={delay:1e3,maxStack:100,userOnly:!1},e.default=v,e.getLastChangeIndex=s},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.IndentClass=void 0;var l=function(){function t(t,e){for(var n=0;n0&&this.children.tail.format(t,e)}},{key:"formats",value:function(){return o({},this.statics.blotName,this.statics.formats(this.domNode))}},{key:"insertBefore",value:function(t,n){if(t instanceof v)u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"insertBefore",this).call(this,t,n);else{var r=null==n?this.length():n.offset(this),o=this.split(r);o.parent.insertBefore(t,o)}}},{key:"optimize",value:function(t){u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"optimize",this).call(this,t);var n=this.next;null!=n&&n.prev===this&&n.statics.blotName===this.statics.blotName&&n.domNode.tagName===this.domNode.tagName&&n.domNode.getAttribute("data-checked")===this.domNode.getAttribute("data-checked")&&(n.moveChildren(this),n.remove())}},{key:"replace",value:function(t){if(t.statics.blotName!==this.statics.blotName){var n=f.default.create(this.statics.defaultChild);t.moveChildren(n),this.appendChild(n)}u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"replace",this).call(this,t)}}]),e}(y.default);b.blotName="list",b.scope=f.default.Scope.BLOCK_BLOT,b.tagName=["OL","UL"],b.defaultChild="list-item",b.allowedChildren=[v],e.ListItem=v,e.default=b},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=n(39),a=function(t){return t&&t.__esModule?t:{default:t}}(l),s=function(t){function e(){return r(this,e),o(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return i(e,t),e}(a.default);s.blotName="italic",s.tagName=["EM","I"],e.default=s},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;n-1?n?this.domNode.setAttribute(t,n):this.domNode.removeAttribute(t):a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"format",this).call(this,t,n)}}],[{key:"create",value:function(t){var n=a(e.__proto__||Object.getPrototypeOf(e),"create",this).call(this,t);return"string"==typeof t&&n.setAttribute("src",this.sanitize(t)),n}},{key:"formats",value:function(t){return f.reduce(function(e,n){return t.hasAttribute(n)&&(e[n]=t.getAttribute(n)),e},{})}},{key:"match",value:function(t){return/\.(jpe?g|gif|png)$/.test(t)||/^data:image\/.+;base64/.test(t)}},{key:"sanitize",value:function(t){return(0,c.sanitize)(t,["http","https","data"])?t:"//:0"}},{key:"value",value:function(t){return t.getAttribute("src")}}]),e}(u.default.Embed);h.blotName="image",h.tagName="IMG",e.default=h},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;n-1?n?this.domNode.setAttribute(t,n):this.domNode.removeAttribute(t):a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"format",this).call(this,t,n)}}],[{key:"create",value:function(t){var n=a(e.__proto__||Object.getPrototypeOf(e),"create",this).call(this,t);return n.setAttribute("frameborder","0"),n.setAttribute("allowfullscreen",!0),n.setAttribute("src",this.sanitize(t)),n}},{key:"formats",value:function(t){return f.reduce(function(e,n){return t.hasAttribute(n)&&(e[n]=t.getAttribute(n)),e},{})}},{key:"sanitize",value:function(t){return c.default.sanitize(t)}},{key:"value",value:function(t){return t.getAttribute("src")}}]),e}(s.BlockEmbed);h.blotName="video",h.className="ql-video",h.tagName="IFRAME",e.default=h},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.FormulaBlot=void 0;var a=function(){function t(t,e){for(var n=0;n0||null==this.cachedText)&&(this.domNode.innerHTML=t(e),this.domNode.normalize(),this.attach()),this.cachedText=e)}}]),e}(v.default);b.className="ql-syntax";var g=new c.default.Attributor.Class("token","hljs",{scope:c.default.Scope.INLINE}),m=function(t){function e(t,n){o(this,e);var r=i(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,t,n));if("function"!=typeof r.options.highlight)throw new Error("Syntax module requires highlight.js. Please include the library on the page before Quill.");var l=null;return r.quill.on(h.default.events.SCROLL_OPTIMIZE,function(){clearTimeout(l),l=setTimeout(function(){r.highlight(),l=null},r.options.interval)}),r.highlight(),r}return l(e,t),a(e,null,[{key:"register",value:function(){h.default.register(g,!0),h.default.register(b,!0)}}]),a(e,[{key:"highlight",value:function(){var t=this;if(!this.quill.selection.composing){this.quill.update(h.default.sources.USER);var e=this.quill.getSelection();this.quill.scroll.descendants(b).forEach(function(e){e.highlight(t.options.highlight)}),this.quill.update(h.default.sources.SILENT),null!=e&&this.quill.setSelection(e,h.default.sources.SILENT)}}}]),e}(d.default);m.DEFAULTS={highlight:function(){return null==window.hljs?null:function(t){return window.hljs.highlightAuto(t).value}}(),interval:1e3},e.CodeBlock=b,e.CodeToken=g,e.default=m},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function l(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function s(t,e,n){var r=document.createElement("button");r.setAttribute("type","button"),r.classList.add("ql-"+e),null!=n&&(r.value=n),t.appendChild(r)}function u(t,e){Array.isArray(e[0])||(e=[e]),e.forEach(function(e){var n=document.createElement("span");n.classList.add("ql-formats"),e.forEach(function(t){if("string"==typeof t)s(n,t);else{var e=Object.keys(t)[0],r=t[e];Array.isArray(r)?c(n,e,r):s(n,e,r)}}),t.appendChild(n)})}function c(t,e,n){var r=document.createElement("select");r.classList.add("ql-"+e),n.forEach(function(t){var e=document.createElement("option");!1!==t?e.setAttribute("value",t):e.setAttribute("selected","selected"),r.appendChild(e)}),t.appendChild(r)}Object.defineProperty(e,"__esModule",{value:!0}),e.addControls=e.default=void 0;var f=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),h=function(){function t(t,e){for(var n=0;n '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.BubbleTooltip=void 0;var a=function t(e,n,r){null===e&&(e=Function.prototype);var o=Object.getOwnPropertyDescriptor(e,n);if(void 0===o){var i=Object.getPrototypeOf(e);return null===i?void 0:t(i,n,r)}if("value"in o)return o.value;var l=o.get;if(void 0!==l)return l.call(r)},s=function(){function t(t,e){for(var n=0;n0&&o===h.default.sources.USER){r.show(),r.root.style.left="0px",r.root.style.width="",r.root.style.width=r.root.offsetWidth+"px";var i=r.quill.getLines(e.index,e.length);if(1===i.length)r.position(r.quill.getBounds(e));else{var l=i[i.length-1],a=r.quill.getIndex(l),s=Math.min(l.length()-1,e.index+e.length-a),u=r.quill.getBounds(new y.Range(a,s));r.position(u)}}else document.activeElement!==r.textbox&&r.quill.hasFocus()&&r.hide()}),r}return l(e,t),s(e,[{key:"listen",value:function(){var t=this;a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"listen",this).call(this),this.root.querySelector(".ql-close").addEventListener("click",function(){t.root.classList.remove("ql-editing")}),this.quill.on(h.default.events.SCROLL_OPTIMIZE,function(){setTimeout(function(){if(!t.root.classList.contains("ql-hidden")){var e=t.quill.getSelection();null!=e&&t.position(t.quill.getBounds(e))}},1)})}},{key:"cancel",value:function(){this.show()}},{key:"position",value:function(t){var n=a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"position",this).call(this,t),r=this.root.querySelector(".ql-tooltip-arrow");if(r.style.marginLeft="",0===n)return n;r.style.marginLeft=-1*n-r.offsetWidth/2+"px"}}]),e}(p.BaseTooltip);_.TEMPLATE=['','
    ','','',"
    "].join(""),e.BubbleTooltip=_,e.default=m},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),s=function t(e,n,r){null===e&&(e=Function.prototype);var o=Object.getOwnPropertyDescriptor(e,n);if(void 0===o){var i=Object.getPrototypeOf(e);return null===i?void 0:t(i,n,r)}if("value"in o)return o.value;var l=o.get;if(void 0!==l)return l.call(r)},u=function(){function t(t,e){for(var n=0;n','','',''].join(""),e.default=w}]).default}); -//# sourceMappingURL=quill.min.js.map \ No newline at end of file diff --git a/Oqtane.Client/wwwroot/js/site.js b/Oqtane.Client/wwwroot/js/site.js deleted file mode 100644 index 139597f9..00000000 --- a/Oqtane.Client/wwwroot/js/site.js +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/Oqtane.Client/wwwroot/loading.gif b/Oqtane.Client/wwwroot/loading.gif deleted file mode 100644 index cc70a7a8b3d426c30e76686fac70c0dcd4c70125..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8238 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 diff --git a/Oqtane.Client/wwwroot/oqtane.png b/Oqtane.Client/wwwroot/oqtane.png deleted file mode 100644 index 8d18cc15222db6ce7ad3fb6a2ae2556a92ed5e1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14299 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 { }); \ No newline at end of file From 82129eebbfabb7e9db38e83800f93d8039481fdc Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 23 Apr 2020 10:06:24 -0400 Subject: [PATCH 167/265] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 859b106c..87849a4e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Please note that this project is governed by the **[.NET Foundation Contributor **To get started with Oqtane:** - 1. Install **[.NET Core 3.2 SDK (v3.1.3)](https://dotnet.microsoft.com/download/dotnet-core/3.1)**. + 1. Install **[.NET Core 3.2 Preview4 SDK (v3.1.201)](https://dotnet.microsoft.com/download/dotnet-core/3.1)**. 2. Install the Preview edition of [Visual Studio 2019](https://visualstudio.microsoft.com/vs/preview/) (version 16.6 or higher) with the **ASP.NET and web development** workload. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**. From a09be84824cd9abb8ce43369199a4c262b12c7ff Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sat, 25 Apr 2020 10:58:38 +0200 Subject: [PATCH 168/265] Create Data directoty if does not exists --- Oqtane.Server/Infrastructure/DatabaseManager.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 76e91390..e4743800 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -38,6 +38,10 @@ namespace Oqtane.Infrastructure { var defaultConnectionString = _config.GetConnectionString(SettingKeys.ConnectionStringKey); var defaultAlias = GetInstallationConfig(SettingKeys.DefaultAliasKey, string.Empty); + var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); + + //create data directory if does not exists + if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory); // if no values specified, fallback to IDE installer if (string.IsNullOrEmpty(defaultConnectionString)) @@ -61,7 +65,6 @@ namespace Oqtane.Infrastructure if (result.Success) { - var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); WriteVersionInfo(defaultConnectionString); TenantMigration(defaultConnectionString, dataDirectory); } @@ -69,7 +72,6 @@ namespace Oqtane.Infrastructure if (_isInstalled && !IsDefaultSiteInstalled(defaultConnectionString)) { BuildDefaultSite(password,email); - } } @@ -210,7 +212,6 @@ namespace Oqtane.Infrastructure private static void ModuleMigration(Assembly assembly, string connectionString) { - Console.WriteLine($"Migrating assembly {assembly.FullName}"); var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) .WithScriptsEmbeddedInAssembly(assembly, s => !s.ToLower().Contains("uninstall.sql")); // scripts must be included as Embedded Resources From 91d1e7c684367a370dc273448a23b0f94ac9da65 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Sat, 25 Apr 2020 17:25:20 +0300 Subject: [PATCH 169/265] Updated to Blazor WebAssembly 3.2 preview 5 --- Oqtane.Client/Oqtane.Client.csproj | 4 ++-- Oqtane.Server/Oqtane.Server.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index acc4da87..fbae9656 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -27,8 +27,8 @@ - - + + diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index fd6d457d..0813064d 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -26,7 +26,7 @@ - + From 967f92c1aa8a814d6dd55a5e09101ca289958653 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Sat, 25 Apr 2020 17:35:50 +0300 Subject: [PATCH 170/265] Render line break conditionally in HtmlText module --- Oqtane.Client/Modules/HtmlText/Index.razor | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Oqtane.Client/Modules/HtmlText/Index.razor b/Oqtane.Client/Modules/HtmlText/Index.razor index 47bf0b61..abb59c7c 100644 --- a/Oqtane.Client/Modules/HtmlText/Index.razor +++ b/Oqtane.Client/Modules/HtmlText/Index.razor @@ -8,9 +8,15 @@ @((MarkupString)content) -
    +@if (PageState.EditMode) +{ +
    +} -

    +@if (PageState.EditMode) +{ +

    +} @code { private string content = ""; From 7606e7b488fe6f6c9607a5c90f55a8ee24482802 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sun, 26 Apr 2020 16:19:20 +0200 Subject: [PATCH 171/265] Permission Optimalization --- Oqtane.Server/Controllers/FolderController.cs | 11 +++--- Oqtane.Server/Controllers/PageController.cs | 17 ++++----- .../SiteTemplates/DefaultSiteTemplate.cs | 38 +++++++++---------- .../SiteTemplates/EmptySiteTemplate.cs | 10 ++--- Oqtane.Server/Repository/FileRepository.cs | 5 ++- Oqtane.Server/Repository/FolderRepository.cs | 11 +++--- .../Interfaces/IPermissionRepository.cs | 7 +++- .../Repository/ModuleDefinitionRepository.cs | 10 ++--- Oqtane.Server/Repository/ModuleRepository.cs | 17 +++++---- .../Repository/PageModuleRepository.cs | 17 ++++----- Oqtane.Server/Repository/PageRepository.cs | 19 ++++------ .../Repository/PermissionRepository.cs | 17 +++++++++ Oqtane.Server/Security/UserPermissions.cs | 2 +- 13 files changed, 97 insertions(+), 84 deletions(-) diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index ee75a536..ea0b9e1f 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -6,6 +6,7 @@ using Oqtane.Shared; using System.Linq; using System.Net; using Oqtane.Enums; +using Oqtane.Extensions; using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; @@ -17,14 +18,12 @@ namespace Oqtane.Controllers { private readonly IFolderRepository _folders; private readonly IUserPermissions _userPermissions; - private readonly IPermissionRepository _permissionRepository; private readonly ILogManager _logger; - public FolderController(IFolderRepository folders, IUserPermissions userPermissions, IPermissionRepository permissionRepository, ILogManager logger) + public FolderController(IFolderRepository folders, IUserPermissions userPermissions, ILogManager logger) { _folders = folders; _userPermissions = userPermissions; - _permissionRepository = permissionRepository; _logger = logger; } @@ -100,9 +99,9 @@ namespace Oqtane.Controllers } else { - permissions = _permissionRepository.EncodePermissions(new List { - new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }); + permissions = new List { + new Permission(PermissionNames.Edit, Constants.AdminRole, true), + }.EncodePermissions(); } if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions)) { diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 2b87c7e4..e65eee0d 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -7,6 +7,7 @@ using System.Linq; using Oqtane.Security; using System.Net; using Oqtane.Enums; +using Oqtane.Extensions; using Oqtane.Infrastructure; using Oqtane.Repository; @@ -19,17 +20,15 @@ namespace Oqtane.Controllers private readonly IModuleRepository _modules; private readonly IPageModuleRepository _pageModules; private readonly IUserPermissions _userPermissions; - private readonly IPermissionRepository _permissionRepository; private readonly ISyncManager _syncManager; private readonly ILogManager _logger; - public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IUserPermissions userPermissions, IPermissionRepository permissionRepository, ISyncManager syncManager, ILogManager logger) + public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger) { _pages = pages; _modules = modules; _pageModules = pageModules; _userPermissions = userPermissions; - _permissionRepository = permissionRepository; _syncManager = syncManager; _logger = logger; } @@ -113,9 +112,9 @@ namespace Oqtane.Controllers } else { - permissions = _permissionRepository.EncodePermissions(new List { + permissions = new List { new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }); + }.EncodePermissions(); } if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions)) @@ -156,10 +155,10 @@ namespace Oqtane.Controllers page.ThemeType = parent.ThemeType; page.LayoutType = parent.LayoutType; page.Icon = parent.Icon; - page.Permissions = _permissionRepository.EncodePermissions(new List { + page.Permissions = new List { new Permission(PermissionNames.View, userid, true), new Permission(PermissionNames.Edit, userid, true) - }); + }.EncodePermissions(); page.IsPersonalizable = false; page.UserId = int.Parse(userid); page = _pages.AddPage(page); @@ -173,10 +172,10 @@ namespace Oqtane.Controllers module.SiteId = page.SiteId; module.PageId = page.PageId; module.ModuleDefinitionName = pm.Module.ModuleDefinitionName; - module.Permissions = _permissionRepository.EncodePermissions(new List { + module.Permissions = new List { new Permission(PermissionNames.View, userid, true), new Permission(PermissionNames.Edit, userid, true) - }); + }.EncodePermissions(); module = _modules.AddModule(module); string content = _modules.ExportModule(pm.ModuleId); diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index daab95f1..bdd46680 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -3,21 +3,21 @@ using Oqtane.Infrastructure; using System.Collections.Generic; using Oqtane.Repository; using Microsoft.AspNetCore.Hosting; +using Oqtane.Extensions; using Oqtane.Shared; namespace Oqtane.SiteTemplates { public class DefaultSiteTemplate : ISiteTemplate { - private readonly IPermissionRepository _permissionRepository; + private readonly IWebHostEnvironment _environment; private readonly ISiteRepository _siteRepository; private readonly IFolderRepository _folderRepository; private readonly IFileRepository _fileRepository; - public DefaultSiteTemplate(IPermissionRepository permissionRepository, IWebHostEnvironment environment, ISiteRepository siteRepository, IFolderRepository folderRepository, IFileRepository fileRepository) + public DefaultSiteTemplate(IWebHostEnvironment environment, ISiteRepository siteRepository, IFolderRepository folderRepository, IFileRepository fileRepository) { - _permissionRepository = permissionRepository; _environment = environment; _siteRepository = siteRepository; _folderRepository = folderRepository; @@ -42,40 +42,40 @@ namespace Oqtane.SiteTemplates IsNavigation = true, IsPersonalizable = false, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions( new List { + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }) , + }.EncodePermissions() , PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Welcome To Oqtane...", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "
    " + "

    Join Our Community  Clone Our Repo

    " + "

    Blazor is a single-page app framework that lets you build interactive web applications using C# instead of JavaScript. Client-side Blazor relies on WebAssembly, an open web standard that does not require plugins or code transpilation in order to run natively in a web browser. Server-side Blazor uses SignalR to host your application on a web server and provide a responsive and robust debugging experience. Blazor applications works in all modern web browsers, including mobile browsers.

    " + "

    Blazor is a feature of ASP.NET Core 3, the popular cross platform web development framework from Microsoft that extends the .NET developer platform with tools and libraries for building web apps.

    " }, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "

    Copyright (c) 2019-2020 .NET Foundation

    " + "

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

    " + "

    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    " + "

    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    " }, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.RegisteredRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "

    Oqtane allows you to control access to your content using security roles. This module is only visible to Registered Users of the site.

    " } } @@ -89,18 +89,18 @@ namespace Oqtane.SiteTemplates IsNavigation = true, IsPersonalizable = false, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions(new List { + PagePermissions = new List { new Permission(PermissionNames.View, Constants.RegisteredRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.RegisteredRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "

    Oqtane allows you to control access to your content using security roles. This page is only visible to Registered Users of the site.

    " } } @@ -114,18 +114,18 @@ namespace Oqtane.SiteTemplates IsNavigation = true, IsPersonalizable = true, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions(new List { + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List { new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "My Page", Pane = "Content", - ModulePermissions = _permissionRepository.EncodePermissions( new List { + ModulePermissions = new List { new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), Content = "

    Oqtane offers native support for user personalized pages. If a page is identified as personalizable by the site administrator in the page settings, when an authenticated user visits the page they will see an edit button at the top right corner of the page next to their username. When they click this button the sytem will create a new version of the page and allow them to edit the page content.

    " } } diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/EmptySiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/EmptySiteTemplate.cs index 51c4a29b..1dfefdf0 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/EmptySiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/EmptySiteTemplate.cs @@ -1,6 +1,7 @@ using Oqtane.Models; using Oqtane.Infrastructure; using System.Collections.Generic; +using Oqtane.Extensions; using Oqtane.Repository; using Oqtane.Shared; @@ -8,11 +9,8 @@ namespace Oqtane.SiteTemplates { public class EmptySiteTemplate : ISiteTemplate { - private readonly IPermissionRepository _permissionRepository; - - public EmptySiteTemplate(IPermissionRepository permissionRepository) + public EmptySiteTemplate() { - _permissionRepository = permissionRepository; } public string Name @@ -33,11 +31,11 @@ namespace Oqtane.SiteTemplates IsNavigation = true, IsPersonalizable = false, EditMode = false, - PagePermissions = _permissionRepository.EncodePermissions( new List { + PagePermissions = new List { new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true) - }), + }.EncodePermissions(), PageTemplateModules = new List() }); diff --git a/Oqtane.Server/Repository/FileRepository.cs b/Oqtane.Server/Repository/FileRepository.cs index a74f2ce9..f532bb6e 100644 --- a/Oqtane.Server/Repository/FileRepository.cs +++ b/Oqtane.Server/Repository/FileRepository.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; +using Oqtane.Extensions; using Oqtane.Models; using Oqtane.Shared; @@ -23,7 +24,7 @@ namespace Oqtane.Repository IEnumerable files = _db.File.Where(item => item.FolderId == folderId).Include(item => item.Folder); foreach (File file in files) { - file.Folder.Permissions = _permissions.EncodePermissions(permissions); + file.Folder.Permissions = permissions.EncodePermissions(); } return files; } @@ -48,7 +49,7 @@ namespace Oqtane.Repository if (file != null) { IEnumerable permissions = _permissions.GetPermissions(EntityNames.Folder, file.FolderId).ToList(); - file.Folder.Permissions = _permissions.EncodePermissions(permissions); + file.Folder.Permissions = permissions.EncodePermissions(); } return file; } diff --git a/Oqtane.Server/Repository/FolderRepository.cs b/Oqtane.Server/Repository/FolderRepository.cs index c83fa215..56b42c7b 100644 --- a/Oqtane.Server/Repository/FolderRepository.cs +++ b/Oqtane.Server/Repository/FolderRepository.cs @@ -1,6 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; +using Oqtane.Extensions; using Oqtane.Models; using Oqtane.Shared; @@ -23,7 +24,7 @@ namespace Oqtane.Repository IEnumerable folders = _db.Folder.Where(item => item.SiteId == siteId); foreach(Folder folder in folders) { - folder.Permissions = _permissions.EncodePermissions(permissions.Where(item => item.EntityId == folder.FolderId)); + folder.Permissions = permissions.Where(item => item.EntityId == folder.FolderId).EncodePermissions(); } return folders; } @@ -49,8 +50,7 @@ namespace Oqtane.Repository Folder folder = _db.Folder.Find(folderId); if (folder != null) { - IEnumerable permissions = _permissions.GetPermissions(EntityNames.Folder, folder.FolderId).ToList(); - folder.Permissions = _permissions.EncodePermissions(permissions); + folder.Permissions = _permissions.GetPermissionString(EntityNames.Folder, folder.FolderId); } return folder; } @@ -60,8 +60,7 @@ namespace Oqtane.Repository Folder folder = _db.Folder.Where(item => item.SiteId == siteId && item.Path == path).FirstOrDefault(); if (folder != null) { - IEnumerable permissions = _permissions.GetPermissions(EntityNames.Folder, folder.FolderId).ToList(); - folder.Permissions = _permissions.EncodePermissions(permissions); + folder.Permissions = _permissions.GetPermissionString(EntityNames.Folder, folder.FolderId); } return folder; } diff --git a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs index 5be9bb50..21c349e4 100644 --- a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Oqtane.Models; +// ReSharper disable once CheckNamespace namespace Oqtane.Repository { public interface IPermissionRepository @@ -8,13 +9,17 @@ namespace Oqtane.Repository IEnumerable GetPermissions(int siteId, string entityName); IEnumerable GetPermissions(string entityName, int entityId); IEnumerable GetPermissions(string entityName, int entityId, string permissionName); + + string GetPermissionString(int siteId, string entityName); + string GetPermissionString(string entityName, int entityId); + string GetPermissionString(string entityName, int entityId, string permissionName); + Permission AddPermission(Permission permission); Permission UpdatePermission(Permission permission); void UpdatePermissions(int siteId, string entityName, int entityId, string permissionStrings); Permission GetPermission(int permissionId); void DeletePermission(int permissionId); void DeletePermissions(int siteId, string entityName, int entityId); - string EncodePermissions(IEnumerable permissionList); IEnumerable DecodePermissions(string permissions, int siteId, string entityName, int entityId); } } diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index 4803ba9e..b687ace3 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -115,7 +115,7 @@ namespace Oqtane.Repository } else { - moduledefinition.Permissions = _permissions.EncodePermissions(permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId)); + moduledefinition.Permissions = permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).EncodePermissions(); } // remove module definition from list as it is already synced moduledefs.Remove(moduledef); @@ -160,7 +160,7 @@ namespace Oqtane.Repository { if (modulecontroltype.Name != "ModuleBase" && !modulecontroltype.Namespace.EndsWith(".Controls")) { - string[] typename = modulecontroltype.AssemblyQualifiedName.Split(',').Select(item => item.Trim()).ToList().ToArray(); + string[] typename = modulecontroltype.AssemblyQualifiedName?.Split(',').Select(item => item.Trim()).ToArray(); string[] segments = typename[0].Split('.'); Array.Resize(ref segments, segments.Length - 1); string moduleType = string.Join(".", segments); @@ -195,7 +195,7 @@ namespace Oqtane.Repository // set internal properties moduledefinition.ModuleDefinitionName = qualifiedModuleType; moduledefinition.ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1]; - moduledefinition.AssemblyName = assembly.FullName.Split(",")[0]; + moduledefinition.AssemblyName = assembly.GetName().Name; if (assembly.FullName.StartsWith("Oqtane.Client")) { moduledefinition.Version = Constants.Version; @@ -225,8 +225,8 @@ namespace Oqtane.Repository moduledefinition = moduledefinitions[index]; // actions var modulecontrolobject = Activator.CreateInstance(modulecontroltype); - string actions = (string)modulecontroltype.GetProperty("Actions").GetValue(modulecontrolobject); - if (actions != "") + string actions = (string)modulecontroltype.GetProperty("Actions")?.GetValue(modulecontrolobject); + if (!string.IsNullOrEmpty(actions)) { foreach (string action in actions.Split(',')) { diff --git a/Oqtane.Server/Repository/ModuleRepository.cs b/Oqtane.Server/Repository/ModuleRepository.cs index 7d88f0a1..7f38a1bf 100644 --- a/Oqtane.Server/Repository/ModuleRepository.cs +++ b/Oqtane.Server/Repository/ModuleRepository.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Text.Json; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; @@ -52,9 +51,9 @@ namespace Oqtane.Repository Module module = _db.Module.Find(moduleId); if (module != null) { - List permissions = _permissions.GetPermissions("Module", module.ModuleId).ToList(); - module.Permissions = _permissions.EncodePermissions(permissions); + module.Permissions = _permissions.GetPermissionString("Module", module.ModuleId); } + return module; } @@ -75,7 +74,7 @@ namespace Oqtane.Repository if (module != null) { List moduledefinitions = _moduleDefinitions.GetModuleDefinitions(module.SiteId).ToList(); - ModuleDefinition moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault(); + ModuleDefinition moduledefinition = moduledefinitions.FirstOrDefault(item => item.ModuleDefinitionName == module.ModuleDefinitionName); if (moduledefinition != null) { ModuleContent modulecontent = new ModuleContent(); @@ -89,9 +88,10 @@ namespace Oqtane.Repository if (moduletype != null && moduletype.GetInterface("IPortable") != null) { var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); - modulecontent.Content = ((IPortable)moduleobject).ExportModule(module); + modulecontent.Content = ((IPortable) moduleobject).ExportModule(module); } } + content = JsonSerializer.Serialize(modulecontent); } } @@ -100,6 +100,7 @@ namespace Oqtane.Repository { // error occurred during export } + return content; } @@ -124,8 +125,8 @@ namespace Oqtane.Repository if (moduletype != null && moduletype.GetInterface("IPortable") != null) { var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); - ((IPortable)moduleobject).ImportModule(module, modulecontent.Content, modulecontent.Version); - success = true; + ((IPortable) moduleobject).ImportModule(module, modulecontent.Content, modulecontent.Version); + success = true; } } } @@ -136,8 +137,8 @@ namespace Oqtane.Repository { // error occurred during import } + return success; } - } } diff --git a/Oqtane.Server/Repository/PageModuleRepository.cs b/Oqtane.Server/Repository/PageModuleRepository.cs index ee85270d..e78abb07 100644 --- a/Oqtane.Server/Repository/PageModuleRepository.cs +++ b/Oqtane.Server/Repository/PageModuleRepository.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; +using Oqtane.Extensions; using Oqtane.Models; namespace Oqtane.Repository @@ -21,12 +22,12 @@ namespace Oqtane.Repository IEnumerable pagemodules = _db.PageModule .Include(item => item.Module) // eager load modules .Where(item => item.Module.SiteId == siteId); - if (pagemodules != null && pagemodules.Any()) + if (pagemodules.Any()) { IEnumerable permissions = _permissions.GetPermissions(pagemodules.FirstOrDefault().Module.SiteId, "Module").ToList(); foreach (PageModule pagemodule in pagemodules) { - pagemodule.Module.Permissions = _permissions.EncodePermissions(permissions.Where(item => item.EntityId == pagemodule.ModuleId)); + pagemodule.Module.Permissions = permissions.Where(item => item.EntityId == pagemodule.ModuleId).EncodePermissions(); } } return pagemodules; @@ -37,16 +38,16 @@ namespace Oqtane.Repository IEnumerable pagemodules = _db.PageModule .Include(item => item.Module) // eager load modules .Where(item => item.PageId == pageId); - if (pane != "" && pagemodules != null && pagemodules.Any()) + if (pane != "" && pagemodules.Any()) { pagemodules = pagemodules.Where(item => item.Pane == pane); } - if (pagemodules != null && pagemodules.Any()) + if (pagemodules.Any()) { IEnumerable permissions = _permissions.GetPermissions(pagemodules.FirstOrDefault().Module.SiteId, "Module").ToList(); foreach (PageModule pagemodule in pagemodules) { - pagemodule.Module.Permissions = _permissions.EncodePermissions(permissions.Where(item => item.EntityId == pagemodule.ModuleId)); + pagemodule.Module.Permissions = permissions.Where(item => item.EntityId == pagemodule.ModuleId).EncodePermissions(); } } return pagemodules; @@ -72,8 +73,7 @@ namespace Oqtane.Repository .SingleOrDefault(item => item.PageModuleId == pageModuleId); if (pagemodule != null) { - IEnumerable permissions = _permissions.GetPermissions("Module", pagemodule.ModuleId).ToList(); - pagemodule.Module.Permissions = _permissions.EncodePermissions(permissions); + pagemodule.Module.Permissions = _permissions.GetPermissionString("Module", pagemodule.ModuleId); } return pagemodule; } @@ -84,8 +84,7 @@ namespace Oqtane.Repository .SingleOrDefault(item => item.PageId == pageId && item.ModuleId == moduleId); if (pagemodule != null) { - IEnumerable permissions = _permissions.GetPermissions("Module", pagemodule.ModuleId).ToList(); - pagemodule.Module.Permissions = _permissions.EncodePermissions(permissions); + pagemodule.Module.Permissions = _permissions.GetPermissionString("Module", pagemodule.ModuleId); } return pagemodule; } diff --git a/Oqtane.Server/Repository/PageRepository.cs b/Oqtane.Server/Repository/PageRepository.cs index 9e106e74..9db59dc2 100644 --- a/Oqtane.Server/Repository/PageRepository.cs +++ b/Oqtane.Server/Repository/PageRepository.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; +using Oqtane.Extensions; using Oqtane.Models; using Oqtane.Shared; @@ -25,7 +26,7 @@ namespace Oqtane.Repository IEnumerable pages = _db.Page.Where(item => item.SiteId == siteId && item.UserId == null); foreach(Page page in pages) { - page.Permissions = _permissions.EncodePermissions(permissions.Where(item => item.EntityId == page.PageId)); + page.Permissions = permissions.Where(item => item.EntityId == page.PageId).EncodePermissions(); } return pages; } @@ -51,8 +52,7 @@ namespace Oqtane.Repository Page page = _db.Page.Find(pageId); if (page != null) { - IEnumerable permissions = _permissions.GetPermissions(EntityNames.Page, page.PageId).ToList(); - page.Permissions = _permissions.EncodePermissions(permissions); + page.Permissions = _permissions.GetPermissionString(EntityNames.Page, page.PageId); } return page; } @@ -62,27 +62,22 @@ namespace Oqtane.Repository Page page = _db.Page.Find(pageId); if (page != null) { - Page personalized = _db.Page.Where(item => item.SiteId == page.SiteId && item.Path == page.Path && item.UserId == userId).FirstOrDefault(); + Page personalized = _db.Page.FirstOrDefault(item => item.SiteId == page.SiteId && item.Path == page.Path && item.UserId == userId); if (personalized != null) { page = personalized; } - if (page != null) - { - IEnumerable permissions = _permissions.GetPermissions(EntityNames.Page, page.PageId).ToList(); - page.Permissions = _permissions.EncodePermissions(permissions); - } + page.Permissions = _permissions.GetPermissionString(EntityNames.Page, page.PageId); } return page; } public Page GetPage(string path, int siteId) { - Page page = _db.Page.Where(item => item.Path == path && item.SiteId == siteId).FirstOrDefault(); + Page page = _db.Page.FirstOrDefault(item => item.Path == path && item.SiteId == siteId); if (page != null) { - IEnumerable permissions = _permissions.GetPermissions(EntityNames.Page, page.PageId).ToList(); - page.Permissions = _permissions.EncodePermissions(permissions); + page.Permissions = _permissions.GetPermissionString(EntityNames.Page, page.PageId); } return page; } diff --git a/Oqtane.Server/Repository/PermissionRepository.cs b/Oqtane.Server/Repository/PermissionRepository.cs index fd9670d3..7a66b3bf 100644 --- a/Oqtane.Server/Repository/PermissionRepository.cs +++ b/Oqtane.Server/Repository/PermissionRepository.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Text.Json; using Microsoft.EntityFrameworkCore; +using Oqtane.Extensions; using Oqtane.Models; namespace Oqtane.Repository @@ -41,6 +42,22 @@ namespace Oqtane.Repository .Include(item => item.Role); // eager load roles } + public string GetPermissionString(int siteId, string entityName) + { + return GetPermissions(siteId, entityName)?.EncodePermissions(); + } + + public string GetPermissionString(string entityName, int entityId) + { + return GetPermissions(entityName, entityId)?.EncodePermissions(); + } + + public string GetPermissionString(string entityName, int entityId, string permissionName) + { + return GetPermissions(entityName, entityId, permissionName)?.EncodePermissions(); + } + + public Permission AddPermission(Permission permission) { _db.Permission.Add(permission); diff --git a/Oqtane.Server/Security/UserPermissions.cs b/Oqtane.Server/Security/UserPermissions.cs index 72108b5d..14a13199 100644 --- a/Oqtane.Server/Security/UserPermissions.cs +++ b/Oqtane.Server/Security/UserPermissions.cs @@ -19,7 +19,7 @@ namespace Oqtane.Security public bool IsAuthorized(ClaimsPrincipal user, string entityName, int entityId, string permissionName) { - return IsAuthorized(user, permissionName, _permissions.EncodePermissions(_permissions.GetPermissions(entityName, entityId, permissionName).ToList())); + return IsAuthorized(user, permissionName, _permissions.GetPermissionString(entityName, entityId, permissionName)); } public bool IsAuthorized(ClaimsPrincipal user, string permissionName, string permissions) From 58d3c406cd4319f21b8ce8baa4a622798197c3f2 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sun, 26 Apr 2020 13:15:02 -0400 Subject: [PATCH 172/265] added IInstallable interface and uninstall implementation for modules. Refactoring module installation to use interface still in progress. --- .../Modules/Admin/SystemInfo/Index.razor | 1 + Oqtane.Client/Modules/HtmlText/ModuleInfo.cs | 3 +- .../Controllers/ModuleDefinitionController.cs | 83 +++++++------------ .../Infrastructure/DatabaseManager.cs | 2 +- .../Infrastructure/Interfaces/IInstallable.cs | 8 ++ .../HtmlText/Manager/HtmlTextManager.cs | 20 ++++- .../HtmlText/Scripts/HtmlText.1.0.0.sql | 19 +++++ .../HtmlText/Scripts/HtmlText.Uninstall.sql | 2 + Oqtane.Server/Oqtane.Server.csproj | 7 ++ .../Repository/Interfaces/ISqlRepository.cs | 2 + .../Repository/ModuleDefinitionRepository.cs | 9 +- Oqtane.Server/Repository/ModuleRepository.cs | 1 - Oqtane.Server/Repository/SiteRepository.cs | 16 ++-- Oqtane.Server/Repository/SqlRepository.cs | 49 +++++++++++ Oqtane.Shared/Models/ModuleDefinition.cs | 12 ++- 15 files changed, 158 insertions(+), 76 deletions(-) create mode 100644 Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs create mode 100644 Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.1.0.0.sql create mode 100644 Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.Uninstall.sql diff --git a/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor b/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor index d4ac4947..8e19125f 100644 --- a/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor +++ b/Oqtane.Client/Modules/Admin/SystemInfo/Index.razor @@ -52,6 +52,7 @@

    Oqtane is an open source modular application framework built from the ground up using modern .NET Core technology. It leverages the revolutionary new Blazor component model to create a fully dynamic web development experience which can be executed on a client or server. Whether you are looking for a platform to accelerate your web development efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.

    - + + + +
    +
    - + @@ -34,7 +43,7 @@ else
    - + @@ -48,16 +57,16 @@ else

    -
    RoleRoles   @context.Role.Name - @if (!context.Role.IsSystem) - { + @if (context.Role.Name != Constants.RegisteredRole) + { - } + }
    +Access Framework API @code { public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; diff --git a/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs b/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs index 48b674d5..4be90a8b 100644 --- a/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs +++ b/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs @@ -9,7 +9,8 @@ namespace Oqtane.Modules.HtmlText Name = "HtmlText", Description = "Renders HTML or Text Content", Version = "1.0.0", - ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server" + ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server", + ReleaseVersions = "1.0.0" }; } } diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index b197e62d..74ff43b9 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -12,7 +12,7 @@ using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; using System; -using System.Runtime.InteropServices.ComTypes; +using Microsoft.Extensions.DependencyInjection; // ReSharper disable StringIndexOfIsCultureSpecific.1 namespace Oqtane.Controllers @@ -25,21 +25,17 @@ namespace Oqtane.Controllers private readonly IUserPermissions _userPermissions; private readonly IInstallationManager _installationManager; private readonly IWebHostEnvironment _environment; - private readonly ITenantResolver _resolver; - private readonly ITenantRepository _tenants; - private readonly ISqlRepository _sql; + private readonly IServiceProvider _serviceProvider; private readonly ILogManager _logger; - public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantResolver resolver, ITenantRepository tenants, ISqlRepository sql, ILogManager logger) + public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ILogManager logger) { _moduleDefinitions = moduleDefinitions; _modules = modules; _userPermissions = userPermissions; _installationManager = installationManager; _environment = environment; - _resolver = resolver; - _tenants = tenants; - _sql = sql; + _serviceProvider = serviceProvider; _logger = logger; } @@ -100,57 +96,40 @@ namespace Oqtane.Controllers [Authorize(Roles = Constants.HostRole)] public void Delete(int id, int siteid) { - List moduledefinitions = _moduleDefinitions.GetModuleDefinitions(siteid).ToList(); - ModuleDefinition moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionId == id).FirstOrDefault(); + ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, siteid); if (moduledefinition != null) { - // server assembly name should follow client naming convention - string assemblyname = Utilities.GetAssemblyName(moduledefinition.ModuleDefinitionName).Replace(".Client",".Server"); - - string uninstallScript = ""; - Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(a => a.GetName().Name == assemblyname); - if (assembly != null) + if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType)) { - Stream resourceStream = assembly.GetManifestResourceStream(assemblyname + ".Scripts.Uninstall.sql"); - if (resourceStream != null) + Type moduletype = Type.GetType(moduledefinition.ServerManagerType); + if (moduletype != null && moduletype.GetInterface("IInstallable") != null) { - using (var reader = new StreamReader(resourceStream)) - { - uninstallScript = reader.ReadToEnd(); - } + var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); + ((IInstallable)moduleobject).Uninstall(); } } - foreach (Tenant tenant in _tenants.GetTenants()) - { - // uninstall module database schema - if (!string.IsNullOrEmpty(uninstallScript)) - { - _sql.ExecuteScript(tenant, uninstallScript); - _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Uninstall Script Executed For {AssemblyName}", assemblyname); - } - // clean up module schema versions - _sql.ExecuteNonQuery(tenant, "DELETE FROM [dbo].[SchemaVersions] WHERE ScriptName LIKE '" + assemblyname + "%'"); - _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Schema Versions Removed For {AssemblyName}", assemblyname); - } - // format root assembly name - assemblyname = assemblyname.Replace(".Server", ""); - - // clean up module static resource folder - string folder = Path.Combine(_environment.WebRootPath, "Modules\\" + assemblyname); - if (Directory.Exists(folder)) + string assemblyname = Utilities.GetAssemblyName(moduledefinition.ModuleDefinitionName); + if (assemblyname != "Oqtane.Client") { - Directory.Delete(folder, true); - _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Removed For {AssemblynName}", assemblyname); - } + assemblyname = assemblyname.Replace(".Client", ""); - // remove module assembly from /bin - string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - foreach (string file in Directory.EnumerateFiles(binfolder, assemblyname + "*.*")) - { - System.IO.File.Delete(file); - _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly Removed {Filename}", file); + // clean up module static resource folder + string folder = Path.Combine(_environment.WebRootPath, "Modules\\" + assemblyname); + if (Directory.Exists(folder)) + { + Directory.Delete(folder, true); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Removed For {AssemblynName}", assemblyname); + } + + // remove module assembly from /bin + string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + foreach (string file in Directory.EnumerateFiles(binfolder, assemblyname + "*.*")) + { + System.IO.File.Delete(file); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly Removed {Filename}", file); + } } // remove module definition @@ -247,12 +226,6 @@ namespace Oqtane.Controllers text = text.Replace("[File]", Path.GetFileName(filePath)); text = text.Replace("[FrameworkVersion]", Constants.Version); System.IO.File.WriteAllText(filePath, text); - - if (Path.GetExtension(filePath).ToLower() == ".sql" && !filePath.ToLower().Contains("uninstall")) - { - // execute installation script in curent tenant - _sql.ExecuteScript(_resolver.GetTenant(), text); - } } DirectoryInfo[] folders = current.GetDirectories(); diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 76e91390..f283e075 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -184,7 +184,7 @@ namespace Oqtane.Infrastructure .SqlDatabase(connectionString) .WithVariable("ConnectionString", connectionString) .WithVariable("Alias", alias) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => master || !s.Contains("Master.")); + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master.")); var dbUpgrade = dbUpgradeConfig.Build(); if (!dbUpgrade.IsUpgradeRequired()) diff --git a/Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs b/Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs new file mode 100644 index 00000000..c71de4f6 --- /dev/null +++ b/Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs @@ -0,0 +1,8 @@ +namespace Oqtane.Infrastructure +{ + public interface IInstallable + { + bool Install(string version); + bool Uninstall(); + } +} diff --git a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs index f37ac523..4d38e250 100644 --- a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs +++ b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs @@ -1,17 +1,31 @@ -using Oqtane.Models; +using Oqtane.Infrastructure; +using Oqtane.Models; +using Oqtane.Repository; using Oqtane.Modules.HtmlText.Models; using Oqtane.Modules.HtmlText.Repository; using System.Net; namespace Oqtane.Modules.HtmlText.Manager { - public class HtmlTextManager : IPortable + public class HtmlTextManager : IInstallable, IPortable { private IHtmlTextRepository _htmlTexts; + private ISqlRepository _sql; - public HtmlTextManager(IHtmlTextRepository htmltexts) + public HtmlTextManager(IHtmlTextRepository htmltexts, ISqlRepository sql) { _htmlTexts = htmltexts; + _sql = sql; + } + + public bool Install(string version) + { + return _sql.ExecuteEmbeddedScript(GetType().Assembly, "HtmlText." + version + ".sql"); + } + + public bool Uninstall() + { + return _sql.ExecuteEmbeddedScript(GetType().Assembly, "HtmlText.Uninstall.sql"); } public string ExportModule(Module module) diff --git a/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.1.0.0.sql b/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.1.0.0.sql new file mode 100644 index 00000000..5c54eae2 --- /dev/null +++ b/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.1.0.0.sql @@ -0,0 +1,19 @@ +CREATE TABLE [dbo].[HtmlText]( + [HtmlTextId] [int] IDENTITY(1,1) NOT NULL, + [ModuleId] [int] NOT NULL, + [Content] [nvarchar](max) NOT NULL, + [CreatedBy] [nvarchar](256) NOT NULL, + [CreatedOn] [datetime] NOT NULL, + [ModifiedBy] [nvarchar](256) NOT NULL, + [ModifiedOn] [datetime] NOT NULL, + CONSTRAINT [PK_HtmlText] PRIMARY KEY CLUSTERED + ( + [HtmlTextId] ASC + ) +) +GO + +ALTER TABLE [dbo].[HtmlText] WITH CHECK ADD CONSTRAINT [FK_HtmlText_Module] FOREIGN KEY([ModuleId]) +REFERENCES [dbo].[Module] ([ModuleId]) +ON DELETE CASCADE +GO diff --git a/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.Uninstall.sql b/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.Uninstall.sql new file mode 100644 index 00000000..b0831b67 --- /dev/null +++ b/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.Uninstall.sql @@ -0,0 +1,2 @@ +DROP TABLE [dbo].[HtmlText] +GO diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index fd6d457d..63634523 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -18,6 +18,13 @@ + + + + + + + diff --git a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs index 29dfe8ba..037e9416 100644 --- a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs @@ -1,10 +1,12 @@ using System.Data.SqlClient; +using System.Reflection; using Oqtane.Models; namespace Oqtane.Repository { public interface ISqlRepository { + bool ExecuteEmbeddedScript(Assembly assembly, string script); void ExecuteScript(Tenant tenant, string script); int ExecuteNonQuery(Tenant tenant, string query); SqlDataReader ExecuteReader(Tenant tenant, string query); diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index 4803ba9e..5bc8ee64 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -188,18 +188,15 @@ namespace Oqtane.Repository { Name = moduleType.Substring(moduleType.LastIndexOf(".") + 1), Description = "Manage " + moduleType.Substring(moduleType.LastIndexOf(".") + 1), - Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : ""), - Version = "1.0.0" + Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : "") }; } // set internal properties moduledefinition.ModuleDefinitionName = qualifiedModuleType; + moduledefinition.Version = ""; // will be populated from database moduledefinition.ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1]; moduledefinition.AssemblyName = assembly.FullName.Split(",")[0]; - if (assembly.FullName.StartsWith("Oqtane.Client")) - { - moduledefinition.Version = Constants.Version; - } + if (string.IsNullOrEmpty(moduledefinition.Categories)) { moduledefinition.Categories = "Common"; diff --git a/Oqtane.Server/Repository/ModuleRepository.cs b/Oqtane.Server/Repository/ModuleRepository.cs index 7d88f0a1..1ec27d3f 100644 --- a/Oqtane.Server/Repository/ModuleRepository.cs +++ b/Oqtane.Server/Repository/ModuleRepository.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Text.Json; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index f85294df..5861aa04 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -20,7 +19,6 @@ namespace Oqtane.Repository private readonly IRoleRepository _roleRepository; private readonly IProfileRepository _profileRepository; private readonly IFolderRepository _folderRepository; - private readonly IFileRepository _fileRepository; private readonly IPageRepository _pageRepository; private readonly IModuleRepository _moduleRepository; private readonly IPageModuleRepository _pageModuleRepository; @@ -30,15 +28,14 @@ namespace Oqtane.Repository private readonly IConfigurationRoot _config; - public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IFileRepository fileRepository, IPageRepository pageRepository, - IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IPermissionRepository permissionRepository, IServiceProvider serviceProvider, + public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IPageRepository pageRepository, + IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IServiceProvider serviceProvider, IConfigurationRoot config) { _db = context; _roleRepository = roleRepository; _profileRepository = profileRepository; _folderRepository = folderRepository; - _fileRepository = fileRepository; _pageRepository = pageRepository; _moduleRepository = moduleRepository; _pageModuleRepository = pageModuleRepository; @@ -787,7 +784,14 @@ namespace Oqtane.Repository if (moduletype != null && moduletype.GetInterface("IPortable") != null) { var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); - ((IPortable) moduleobject).ImportModule(module, pagetemplatemodule.Content, moduledefinition.Version); + try + { + ((IPortable)moduleobject).ImportModule(module, pagetemplatemodule.Content, moduledefinition.Version); + } + catch + { + // error in module import + } } } diff --git a/Oqtane.Server/Repository/SqlRepository.cs b/Oqtane.Server/Repository/SqlRepository.cs index f20b28e6..0e5c2f3c 100644 --- a/Oqtane.Server/Repository/SqlRepository.cs +++ b/Oqtane.Server/Repository/SqlRepository.cs @@ -1,12 +1,61 @@ using System; using System.Data; using System.Data.SqlClient; +using System.IO; +using System.Linq; +using System.Reflection; using Oqtane.Models; namespace Oqtane.Repository { public class SqlRepository : ISqlRepository { + private readonly ITenantRepository _tenants; + + public SqlRepository(ITenantRepository tenants) + { + _tenants = tenants; + } + + public bool ExecuteEmbeddedScript(Assembly assembly, string filename) + { + // script must be included as an Embedded Resource within an assembly + bool success = true; + string uninstallScript = ""; + + if (assembly != null) + { + string name = assembly.GetManifestResourceNames().FirstOrDefault(item => item.EndsWith("." + filename)); + if (name != null) + { + Stream resourceStream = assembly.GetManifestResourceStream(name); + if (resourceStream != null) + { + using (var reader = new StreamReader(resourceStream)) + { + uninstallScript = reader.ReadToEnd(); + } + } + } + } + + if (!string.IsNullOrEmpty(uninstallScript)) + { + foreach (Tenant tenant in _tenants.GetTenants()) + { + try + { + ExecuteScript(tenant, uninstallScript); + } + catch + { + success = false; + } + } + } + + return success; + } public void ExecuteScript(Tenant tenant, string script) { diff --git a/Oqtane.Shared/Models/ModuleDefinition.cs b/Oqtane.Shared/Models/ModuleDefinition.cs index 82836f0c..504cc890 100644 --- a/Oqtane.Shared/Models/ModuleDefinition.cs +++ b/Oqtane.Shared/Models/ModuleDefinition.cs @@ -19,6 +19,7 @@ namespace Oqtane.Models PermissionNames = ""; ServerManagerType = ""; ControlTypeRoutes = ""; + ReleaseVersions = ""; Template = ""; } @@ -34,8 +35,7 @@ namespace Oqtane.Models public string ModifiedBy { get; set; } public DateTime ModifiedOn { get; set; } - [NotMapped] - public int SiteId { get; set; } + // additional IModule properties [NotMapped] public string Owner { get; set; } [NotMapped] @@ -53,12 +53,18 @@ namespace Oqtane.Models [NotMapped] public string ControlTypeRoutes { get; set; } [NotMapped] - public string Template { get; set; } + public string ReleaseVersions { get; set; } + + // internal properties + [NotMapped] + public int SiteId { get; set; } [NotMapped] public string ControlTypeTemplate { get; set; } [NotMapped] public string AssemblyName { get; set; } [NotMapped] public string Permissions { get; set; } + [NotMapped] + public string Template { get; set; } } } From 6b2411b396f2493575990314dac99b48b27a0e3f Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sun, 26 Apr 2020 13:39:56 -0400 Subject: [PATCH 173/265] fixed merge issue --- Oqtane.Server/Controllers/ModuleDefinitionController.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 5eeb4919..4ba5d613 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -130,10 +130,6 @@ namespace Oqtane.Controllers System.IO.File.Delete(file); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly Removed {Filename}", file); } - - // clean up module schema versions - _sql.ExecuteNonQuery(tenant, "DELETE FROM [dbo].[SchemaVersions] WHERE ScriptName LIKE '" + assemblyname + "%'"); - _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Schema Versions Removed For {AssemblyName}", assemblyname); } // remove module definition From fa91a2fd46ff9d14f757af3f9909257240e93ca7 Mon Sep 17 00:00:00 2001 From: Mike Casas Date: Sun, 26 Apr 2020 14:52:39 -0400 Subject: [PATCH 174/265] Update to Preview 5. --- .../External/Client/[Owner].[Module]s.Module.Client.csproj | 6 +++--- .../External/Server/[Owner].[Module]s.Module.Server.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj index e8eb366f..830b9b85 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj @@ -12,9 +12,9 @@ - - - + + + diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj index fadde9f7..884263a6 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj @@ -23,7 +23,7 @@ - + From 43a94eb60a616ebec53927c9b9fad53fcd97f960 Mon Sep 17 00:00:00 2001 From: Mike Casas Date: Sun, 26 Apr 2020 16:16:40 -0400 Subject: [PATCH 175/265] Fix for the correct version. --- .../External/Client/[Owner].[Module]s.Module.Client.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj index 830b9b85..d23e3647 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj @@ -14,7 +14,7 @@ - + From 8dd2677b8f24f2597a3dd8fe0b79899da76545c8 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sun, 26 Apr 2020 20:24:00 +0200 Subject: [PATCH 176/265] Menu component refactoring Login component refactoring --- Oqtane.Client/Themes/Controls/Login.razor | 41 +----- Oqtane.Client/Themes/Controls/LoginBase.cs | 49 +++++++ Oqtane.Client/Themes/Controls/Menu.razor | 135 ++---------------- Oqtane.Client/Themes/Controls/MenuBase.cs | 45 ++++++ .../Themes/Controls/MenuHorizontal.Razor | 42 ++++++ .../Themes/Controls/MenuVertical.razor | 27 ++++ 6 files changed, 175 insertions(+), 164 deletions(-) create mode 100644 Oqtane.Client/Themes/Controls/LoginBase.cs create mode 100644 Oqtane.Client/Themes/Controls/MenuBase.cs create mode 100644 Oqtane.Client/Themes/Controls/MenuHorizontal.Razor create mode 100644 Oqtane.Client/Themes/Controls/MenuVertical.razor diff --git a/Oqtane.Client/Themes/Controls/Login.razor b/Oqtane.Client/Themes/Controls/Login.razor index e23467e9..55d975aa 100644 --- a/Oqtane.Client/Themes/Controls/Login.razor +++ b/Oqtane.Client/Themes/Controls/Login.razor @@ -1,9 +1,5 @@ @namespace Oqtane.Themes.Controls -@inherits ThemeControlBase -@inject NavigationManager NavigationManager -@inject IUserService UserService -@inject IJSRuntime jsRuntime -@inject IServiceProvider ServiceProvider +@inherits LoginBase @@ -16,38 +12,3 @@ - - -@code { - private void LoginUser() - { - var returnurl = PageState.Alias.Path; - if (PageState.Page.Path != "/") - { - returnurl += "/" + PageState.Page.Path; - } - - NavigationManager.NavigateTo(NavigateUrl("login", "returnurl=" + returnurl)); - } - - private async Task LogoutUser() - { - await UserService.LogoutUserAsync(PageState.User); - - if (PageState.Runtime == Runtime.Server) - { - // server-side Blazor - var interop = new Interop(jsRuntime); - string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken"); - var fields = new { __RequestVerificationToken = antiforgerytoken, returnurl = (PageState.Alias.Path + "/" + PageState.Page.Path) }; - await interop.SubmitForm("/pages/logout/", fields); - } - else - { - // client-side Blazor - var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); - authstateprovider.NotifyAuthenticationChanged(); - NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload")); - } - } -} diff --git a/Oqtane.Client/Themes/Controls/LoginBase.cs b/Oqtane.Client/Themes/Controls/LoginBase.cs new file mode 100644 index 00000000..a4282c08 --- /dev/null +++ b/Oqtane.Client/Themes/Controls/LoginBase.cs @@ -0,0 +1,49 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using Oqtane.Providers; +using Oqtane.Services; +using Oqtane.UI; + +namespace Oqtane.Themes.Controls +{ + public class LoginBase : ThemeControlBase + { + [Inject] public NavigationManager NavigationManager {get;set;} + [Inject]public IUserService UserService {get;set;} + [Inject]public IJSRuntime jsRuntime {get;set;} + [Inject]public IServiceProvider ServiceProvider {get;set;} + + protected void LoginUser() + { + var returnurl = PageState.Alias.Path; + if (PageState.Page.Path != "/") + { + returnurl += "/" + PageState.Page.Path; + } + NavigationManager.NavigateTo(NavigateUrl("login", "returnurl=" + returnurl)); + } + + protected async Task LogoutUser() + { + await UserService.LogoutUserAsync(PageState.User); + + if (PageState.Runtime == Runtime.Server) + { + // server-side Blazor + var interop = new Interop(jsRuntime); + string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken"); + var fields = new { __RequestVerificationToken = antiforgerytoken, returnurl = (PageState.Alias.Path + "/" + PageState.Page.Path) }; + await interop.SubmitForm("/pages/logout/", fields); + } + else + { + // client-side Blazor + var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); + authstateprovider.NotifyAuthenticationChanged(); + NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload")); + } + } + } +} diff --git a/Oqtane.Client/Themes/Controls/Menu.razor b/Oqtane.Client/Themes/Controls/Menu.razor index 442729d6..0f2610cc 100644 --- a/Oqtane.Client/Themes/Controls/Menu.razor +++ b/Oqtane.Client/Themes/Controls/Menu.razor @@ -1,133 +1,20 @@ @namespace Oqtane.Themes.Controls -@inherits ThemeControlBase -@if (menu != string.Empty) +@switch (Orientation) { -
    - @((MarkupString)menu) -
    + case "Horizontal": + + break; + default: // Vertical + { + + break; + } } -@code { - private string menu = string.Empty; - +@code{ + [Parameter] public string Orientation { get; set; } - protected override void OnParametersSet() - { - switch (Orientation) - { - case "Horizontal": - CreateHorizontalMenu(); - break; - default: // Vertical - CreateVerticalMenu(); - break; - } - } - - private void CreateVerticalMenu() - { - var level = -1; - var securitylevel = int.MaxValue; - - menu = ""; - } - - private void CreateHorizontalMenu() - { - var url = String.Empty; - var target = String.Empty; - - menu = ""; - menu += "
    "; - menu += "
      "; - - foreach (Page p in PageState.Pages.Where(item => item.IsNavigation && !item.IsDeleted)) - { - if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, p.Permissions) && p.ParentId == PageState.Page.ParentId && p.Level == PageState.Page.Level) - { - if (string.IsNullOrEmpty(p.Url)) - { - url = NavigateUrl(p.Path); - target = String.Empty; - } - else - { - url = p.Url; - if (p.Url.StartsWith("http")) - { - target = " target=\"_new\""; - } - } - - if (p.PageId == PageState.Page.PageId) - { - menu += "
    • " + - "" + - ((p.Icon != string.Empty) ? " " : string.Empty) + - p.Name + " (current)
    • "; - } - else - { - menu += "
    • " + - "" + - ((p.Icon != string.Empty) ? " " : string.Empty) + - p.Name + "
    • "; - } - } - } - - menu += "
    "; - menu += "
    "; - } } diff --git a/Oqtane.Client/Themes/Controls/MenuBase.cs b/Oqtane.Client/Themes/Controls/MenuBase.cs new file mode 100644 index 00000000..03eecdef --- /dev/null +++ b/Oqtane.Client/Themes/Controls/MenuBase.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Linq; +using Oqtane.Models; +using Oqtane.Security; +using Oqtane.Shared; + +namespace Oqtane.Themes.Controls +{ + public class MenuBase : ThemeControlBase + { + private List _menuPages; + + protected IEnumerable MenuPages => _menuPages ?? (_menuPages = GetMenuPages().ToList()); + + protected string GetTarget(Page page) + { + return page.Url.StartsWith("http") ? "_new" : string.Empty; + } + + protected string GetUrl(Page page) + { + return string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url; + } + + private IEnumerable GetMenuPages() + { + var securityLevel = int.MaxValue; + foreach (Page p in PageState.Pages.Where(item => item.IsNavigation && !item.IsDeleted)) + { + if (p.Level <= securityLevel && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) + { + securityLevel = int.MaxValue; + yield return p; + } + else + { + if (securityLevel == int.MaxValue) + { + securityLevel = p.Level; + } + } + } + } + } +} diff --git a/Oqtane.Client/Themes/Controls/MenuHorizontal.Razor b/Oqtane.Client/Themes/Controls/MenuHorizontal.Razor new file mode 100644 index 00000000..708a3059 --- /dev/null +++ b/Oqtane.Client/Themes/Controls/MenuHorizontal.Razor @@ -0,0 +1,42 @@ +@namespace Oqtane.Themes.Controls +@inherits MenuBase +@if (MenuPages.Any()) +{ +
    + + + +
    +} diff --git a/Oqtane.Client/Themes/Controls/MenuVertical.razor b/Oqtane.Client/Themes/Controls/MenuVertical.razor new file mode 100644 index 00000000..8991b277 --- /dev/null +++ b/Oqtane.Client/Themes/Controls/MenuVertical.razor @@ -0,0 +1,27 @@ +@namespace Oqtane.Themes.Controls +@inherits MenuBase +@if (MenuPages.Any()) +{ + +} From cf567ee1522a411dc77b0fdf02aedc24f62bfb2b Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 27 Apr 2020 08:18:13 -0400 Subject: [PATCH 177/265] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87849a4e..15b1222f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Please note that this project is governed by the **[.NET Foundation Contributor **To get started with Oqtane:** - 1. Install **[.NET Core 3.2 Preview4 SDK (v3.1.201)](https://dotnet.microsoft.com/download/dotnet-core/3.1)**. + 1. Install **[.NET Core 3.2 Preview5 SDK (v3.1.201)](https://dotnet.microsoft.com/download/dotnet-core/3.1)**. 2. Install the Preview edition of [Visual Studio 2019](https://visualstudio.microsoft.com/vs/preview/) (version 16.6 or higher) with the **ASP.NET and web development** workload. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**. From 8523f0d719228c3bda2b9a089a86453d876feb31 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 27 Apr 2020 08:20:58 -0400 Subject: [PATCH 178/265] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 02a0812b..9a5062f7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 .NET Foundation +Copyright (c) 2018-2020 .NET Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From ced2051704255bef2a2ec3bfcdadfd9936a8ffef Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Mon, 27 Apr 2020 16:50:36 +0200 Subject: [PATCH 179/265] Theme base components refactoring --- .../Themes/Controls/Breadcrumbs.razor | 42 ++--- Oqtane.Client/Themes/Controls/Logo.razor | 17 +- Oqtane.Client/Themes/Controls/MenuBase.cs | 6 +- .../Themes/Controls/ModuleActions.razor | 125 +------------- .../Themes/Controls/ModuleActionsBase.cs | 154 ++++++++++++++++++ 5 files changed, 193 insertions(+), 151 deletions(-) create mode 100644 Oqtane.Client/Themes/Controls/ModuleActionsBase.cs diff --git a/Oqtane.Client/Themes/Controls/Breadcrumbs.razor b/Oqtane.Client/Themes/Controls/Breadcrumbs.razor index c0107601..7175f85d 100644 --- a/Oqtane.Client/Themes/Controls/Breadcrumbs.razor +++ b/Oqtane.Client/Themes/Controls/Breadcrumbs.razor @@ -1,32 +1,34 @@ @namespace Oqtane.Themes.Controls @inherits ThemeControlBase -@if (breadcrumbs != string.Empty) +@if (BreadCrumbPages.Any()) { - @((MarkupString)breadcrumbs) + } @code { - string breadcrumbs = string.Empty; - protected override void OnParametersSet() + protected IEnumerable BreadCrumbPages => GetBreadCrumbPages().Reverse().ToList(); + + protected string ActiveClass(Page page) { - breadcrumbs = string.Empty; - int? pageid = PageState.Page.PageId; - for (int i = PageState.Pages.Count - 1; i >= 0; i--) + return (page.PageId == PageState.Page.PageId) ? " active" : string.Empty; + } + + private IEnumerable GetBreadCrumbPages() + { + var page = PageState.Page; + do { - var p = PageState.Pages[i]; - if (p.PageId == pageid) - { - breadcrumbs = "
  • " + p.Name + "
  • " + breadcrumbs; - pageid = p.ParentId; - } - } - - if (breadcrumbs != "") - { - breadcrumbs = "
      " + breadcrumbs + "
    "; - } + yield return page; + page = PageState.Pages.FirstOrDefault(p => page != null && p.PageId == page.ParentId); + } while (page != null); } } diff --git a/Oqtane.Client/Themes/Controls/Logo.razor b/Oqtane.Client/Themes/Controls/Logo.razor index ee8151f5..998acfd6 100644 --- a/Oqtane.Client/Themes/Controls/Logo.razor +++ b/Oqtane.Client/Themes/Controls/Logo.razor @@ -2,17 +2,20 @@ @inherits ThemeControlBase @inject NavigationManager NavigationManager -@((MarkupString)logo) +@if (PageState.Site.LogoFileId != null) +{ + + @PageState.Site.Name + +} @code { - string logo = ""; - - protected override void OnParametersSet() + string Href { - if (PageState.Site.LogoFileId != null) + get { var uri = new Uri(NavigationManager.Uri); - logo = "\"""; + return $"{uri.Scheme}://{uri.Authority}"; } } -} \ No newline at end of file +} diff --git a/Oqtane.Client/Themes/Controls/MenuBase.cs b/Oqtane.Client/Themes/Controls/MenuBase.cs index 03eecdef..c63f0dc8 100644 --- a/Oqtane.Client/Themes/Controls/MenuBase.cs +++ b/Oqtane.Client/Themes/Controls/MenuBase.cs @@ -8,13 +8,11 @@ namespace Oqtane.Themes.Controls { public class MenuBase : ThemeControlBase { - private List _menuPages; - - protected IEnumerable MenuPages => _menuPages ?? (_menuPages = GetMenuPages().ToList()); + protected IEnumerable MenuPages => GetMenuPages().ToList(); protected string GetTarget(Page page) { - return page.Url.StartsWith("http") ? "_new" : string.Empty; + return page.Url != null && page.Url.StartsWith("http") ? "_new" : string.Empty; } protected string GetUrl(Page page) diff --git a/Oqtane.Client/Themes/Controls/ModuleActions.razor b/Oqtane.Client/Themes/Controls/ModuleActions.razor index a3d15e8c..6ca55f1a 100644 --- a/Oqtane.Client/Themes/Controls/ModuleActions.razor +++ b/Oqtane.Client/Themes/Controls/ModuleActions.razor @@ -1,135 +1,20 @@ @namespace Oqtane.Themes.Controls -@inherits ContainerBase -@inject NavigationManager NavigationManager -@inject IUserService UserService -@inject IPageModuleService PageModuleService +@inherits ModuleActionsBase @if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions)) { } - -@code { - private List actions; - - protected override void OnParametersSet() - { - if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions)) - { - actions = new List(); - actions.Add(new ActionViewModel { Action = "settings", Name = "Manage Settings" }); - - if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "") - { - actions.Add(new ActionViewModel { Action = "import", Name = "Import Content" }); - actions.Add(new ActionViewModel { Action = "export", Name = "Export Content" }); - } - - actions.Add(new ActionViewModel { Action = "delete", Name = "Delete Module" }); - actions.Add(new ActionViewModel { Action = "", Name = "" }); - - if (ModuleState.PaneModuleIndex > 0) - { - actions.Add(new ActionViewModel { Action = "<<", Name = "Move To Top" }); - } - - if (ModuleState.PaneModuleIndex > 0) - { - actions.Add(new ActionViewModel { Action = "<", Name = "Move Up" }); - } - - if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1)) - { - actions.Add(new ActionViewModel { Action = ">", Name = "Move Down" }); - } - - if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1)) - { - actions.Add(new ActionViewModel { Action = ">>", Name = "Move To Bottom" }); - } - - foreach (string pane in PageState.Page.Panes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) - { - if (pane != ModuleState.Pane) - { - actions.Add(new ActionViewModel { Action = pane, Name = "Move To " + pane + " Pane" }); - } - } - } - } - - protected async Task ModuleAction(string action) - { - if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions)) - { - PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); - - string url = NavigateUrl(); - switch (action) - { - case "<<": - pagemodule.Order = 0; - await PageModuleService.UpdatePageModuleAsync(pagemodule); - await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); - break; - case "<": - pagemodule.Order -= 3; - await PageModuleService.UpdatePageModuleAsync(pagemodule); - await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); - break; - case ">": - pagemodule.Order += 3; - await PageModuleService.UpdatePageModuleAsync(pagemodule); - await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); - break; - case ">>": - pagemodule.Order = int.MaxValue; - await PageModuleService.UpdatePageModuleAsync(pagemodule); - await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); - break; - case "settings": - url = EditUrl(pagemodule.ModuleId, "Settings"); - break; - case "import": - url = EditUrl(pagemodule.ModuleId, "Import"); - break; - case "export": - url = EditUrl(pagemodule.ModuleId, "Export"); - break; - case "delete": - pagemodule.IsDeleted = true; - await PageModuleService.UpdatePageModuleAsync(pagemodule); - await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); - break; - default: // move to pane - string pane = pagemodule.Pane; - pagemodule.Pane = action; - pagemodule.Order = int.MaxValue; // add to bottom of pane - await PageModuleService.UpdatePageModuleAsync(pagemodule); - await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); - await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pane); - break; - } - NavigationManager.NavigateTo(url); - } - } - - public class ActionViewModel - { - public string Action { set; get; } - public string Name { set; get; } - } -} diff --git a/Oqtane.Client/Themes/Controls/ModuleActionsBase.cs b/Oqtane.Client/Themes/Controls/ModuleActionsBase.cs new file mode 100644 index 00000000..c840128b --- /dev/null +++ b/Oqtane.Client/Themes/Controls/ModuleActionsBase.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using Oqtane.Models; +using Oqtane.Security; +using Oqtane.Services; +using Oqtane.Shared; + +// ReSharper disable UnassignedGetOnlyAutoProperty +// ReSharper disable MemberCanBePrivate.Global + +namespace Oqtane.Themes.Controls +{ + public class ModuleActionsBase : ContainerBase + { + [Inject]public NavigationManager NavigationManager{get; set;} + [Inject]public IPageModuleService PageModuleService{get; set;} + + protected List Actions; + + protected override void OnParametersSet() + { + Actions = GetActions(); + } + + protected virtual List GetActions() + { + var actionList = new List(); + if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions)) + { + + actionList.Add(new ActionViewModel {Name = "Manage Settings", Action = async (u,m) => Settings(u, m)}); + + if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "") + { + actionList.Add(new ActionViewModel {Name = "Import Content", Action = async(u,m)=> EditUrl(m.ModuleId, "Import")}); + actionList.Add(new ActionViewModel {Name = "Export Content", Action = async(u,m)=> EditUrl(m.ModuleId, "Export")}); + } + + actionList.Add(new ActionViewModel {Name = "Delete Module" , Action = async (u,m) => await DeleteModule(u, m)}); + actionList.Add(new ActionViewModel {Name = "" }); + + if (ModuleState.PaneModuleIndex > 0) + { + actionList.Add(new ActionViewModel {Name = "Move To Top" , Action = async (s,m) => await MoveTop(s, m)}); + } + + if (ModuleState.PaneModuleIndex > 0) + { + actionList.Add(new ActionViewModel {Name = "Move Up" , Action = async (s,m) => await MoveUp(s, m)}); + } + + if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1)) + { + actionList.Add(new ActionViewModel {Name = "Move Down", Action = async (s,m) => await MoveDown(s, m) }); + } + + if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1)) + { + actionList.Add(new ActionViewModel {Name = "Move To Bottom", Action = async (s,m) => await MoveBottom(s, m) }); + } + + foreach (string pane in PageState.Page.Panes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + { + if (pane != ModuleState.Pane) + { + actionList.Add(new ActionViewModel {Name = "Move To " + pane + " Pane", Action = async (s,m) => await MoveToPane(s,pane, m) }); + } + } + } + return actionList; + } + + protected async Task ModuleAction(ActionViewModel action) + { + if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions)) + { + PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); + + string url = NavigateUrl(); + + if (action.Action!=null) + { + url = await action.Action(url, pagemodule); + } + NavigationManager.NavigateTo(url); + } + } + + private async Task MoveToPane(string url, string newPane, PageModule pagemodule) + { + string oldPane = pagemodule.Pane; + pagemodule.Pane = newPane; + pagemodule.Order = int.MaxValue; // add to bottom of pane + await PageModuleService.UpdatePageModuleAsync(pagemodule); + await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); + await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, oldPane); + return url; + } + + private async Task DeleteModule(string url,PageModule pagemodule) + { + pagemodule.IsDeleted = true; + await PageModuleService.UpdatePageModuleAsync(pagemodule); + await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); + return url; + } + + private string Settings(string url, PageModule pagemodule) + { + url = EditUrl(pagemodule.ModuleId, "Settings"); + return url; + } + + private async Task MoveTop(string s, PageModule pagemodule) + { + pagemodule.Order = 0; + await PageModuleService.UpdatePageModuleAsync(pagemodule); + await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); + return s; + } + + private async Task MoveBottom(string s, PageModule pagemodule) + { + pagemodule.Order = int.MaxValue; + await PageModuleService.UpdatePageModuleAsync(pagemodule); + await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); + return s; + } + private async Task MoveUp(string s, PageModule pagemodule) + { + pagemodule.Order -= 3; + await PageModuleService.UpdatePageModuleAsync(pagemodule); + await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); + return s; + } + private async Task MoveDown(string s, PageModule pagemodule) + { + pagemodule.Order += 3; + await PageModuleService.UpdatePageModuleAsync(pagemodule); + await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); + return s; + } + + public class ActionViewModel + { + public string Name { set; get; } + + public Func> Action { set; get; } + + } + } +} From d2fd8c7f4eba857980d964a3e090b2767b501110 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 27 Apr 2020 14:18:58 -0400 Subject: [PATCH 180/265] improved responsive design for Blazaor theme to make it more mobile friendly --- .../Themes/BlazorTheme/Default.razor | 19 +++++- Oqtane.Client/Themes/Controls/Logo.razor | 2 +- .../Oqtane.Themes.BlazorTheme/Theme.css | 58 +++++++++++++++++-- 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/Oqtane.Client/Themes/BlazorTheme/Default.razor b/Oqtane.Client/Themes/BlazorTheme/Default.razor index 89008b7a..7d9301e2 100644 --- a/Oqtane.Client/Themes/BlazorTheme/Default.razor +++ b/Oqtane.Client/Themes/BlazorTheme/Default.razor @@ -1,14 +1,27 @@ @namespace Oqtane.Themes.BlazorTheme @inherits ThemeBase + +
    -
    +
    diff --git a/Oqtane.Client/Themes/Controls/Logo.razor b/Oqtane.Client/Themes/Controls/Logo.razor index ee8151f5..eda885fe 100644 --- a/Oqtane.Client/Themes/Controls/Logo.razor +++ b/Oqtane.Client/Themes/Controls/Logo.razor @@ -12,7 +12,7 @@ if (PageState.Site.LogoFileId != null) { var uri = new Uri(NavigationManager.Uri); - logo = "\"""; + logo = "\"""; } } } \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css index cacd218a..2b863ed1 100644 --- a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css +++ b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css @@ -1,4 +1,8 @@ - +.breadcrumbs { + background-color: #e6e6e6; + border-bottom: 1px solid #d6d5d5; +} + .top-row { height: 3.5rem; display: flex; @@ -22,8 +26,8 @@ background-color: rgba(0,0,0,0.4); } - .sidebar .navbar-brand { - font-size: 1.1rem; + .sidebar .navbar-toggler .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .sidebar .oi { @@ -33,6 +37,10 @@ top: -2px; } +.breadcrumb { + margin-bottom: 0; +} + .app-menu .nav-item { font-size: 0.9rem; padding-bottom: 0.5rem; @@ -84,6 +92,13 @@ flex-direction: row; } + .breadcrumbs { + position: fixed; + left: 275px; + top: 0; + z-index: 1; + } + .sidebar { width: 250px; height: 100vh; @@ -94,7 +109,6 @@ .main .top-row { position: sticky; top: 0; - z-index: 9999; } .main > div { @@ -110,6 +124,10 @@ /* Never collapse the sidebar for wide screens */ display: block; } + + .main > .container { + margin-top: 30px; + } } @-webkit-keyframes sk-stretchdelay { @@ -133,3 +151,35 @@ -webkit-transform: scaleY(1.0); } } + +@media (max-width: 767px) { + .sidebar { + margin-top: 3.5rem; + position: fixed; + width: 100%; + } + + .main > .top-row.px-4 { + display: flex; + position: fixed; + left: 0; + top: 0; + width: 100%; + } + + .ml-md-auto { + margin-left: auto; + } + + .breadcrumbs { + position: fixed; + top: 150px; + width: 100%; + left: 0; + } + + .main > .container { + margin-top: 200px; + } +} + From e6c26210a0ad5c3369f1199e91cf1436f3c5c24b Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 27 Apr 2020 15:31:32 -0400 Subject: [PATCH 181/265] z-order tweak to Blazor theme --- Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css | 1 + 1 file changed, 1 insertion(+) diff --git a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css index 2b863ed1..114c2584 100644 --- a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css +++ b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css @@ -16,6 +16,7 @@ .main .top-row { background-color: #e6e6e6; border-bottom: 1px solid #d6d5d5; + z-index: 9999; } .sidebar { From e29b4f9d8d30971c93a140bba3d25a50861660e1 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Mon, 27 Apr 2020 22:43:24 +0200 Subject: [PATCH 182/265] Theme base components refactoring - clear of async warnings --- .../Themes/Controls/ModuleActionsBase.cs | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/Oqtane.Client/Themes/Controls/ModuleActionsBase.cs b/Oqtane.Client/Themes/Controls/ModuleActionsBase.cs index c840128b..9dd748e6 100644 --- a/Oqtane.Client/Themes/Controls/ModuleActionsBase.cs +++ b/Oqtane.Client/Themes/Controls/ModuleActionsBase.cs @@ -14,9 +14,9 @@ namespace Oqtane.Themes.Controls { public class ModuleActionsBase : ContainerBase { - [Inject]public NavigationManager NavigationManager{get; set;} - [Inject]public IPageModuleService PageModuleService{get; set;} - + [Inject] public NavigationManager NavigationManager { get; set; } + [Inject] public IPageModuleService PageModuleService { get; set; } + protected List Actions; protected override void OnParametersSet() @@ -27,70 +27,78 @@ namespace Oqtane.Themes.Controls protected virtual List GetActions() { var actionList = new List(); - if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions)) + if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions)) { - - actionList.Add(new ActionViewModel {Name = "Manage Settings", Action = async (u,m) => Settings(u, m)}); - + actionList.Add(new ActionViewModel {Name = "Manage Settings", Action = async (u, m) => await Settings(u, m)}); + if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "") { - actionList.Add(new ActionViewModel {Name = "Import Content", Action = async(u,m)=> EditUrl(m.ModuleId, "Import")}); - actionList.Add(new ActionViewModel {Name = "Export Content", Action = async(u,m)=> EditUrl(m.ModuleId, "Export")}); + actionList.Add(new ActionViewModel {Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import")}); + actionList.Add(new ActionViewModel {Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export")}); } - - actionList.Add(new ActionViewModel {Name = "Delete Module" , Action = async (u,m) => await DeleteModule(u, m)}); - actionList.Add(new ActionViewModel {Name = "" }); - + + actionList.Add(new ActionViewModel {Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m)}); + actionList.Add(new ActionViewModel {Name = ""}); + if (ModuleState.PaneModuleIndex > 0) { - actionList.Add(new ActionViewModel {Name = "Move To Top" , Action = async (s,m) => await MoveTop(s, m)}); + actionList.Add(new ActionViewModel {Name = "Move To Top", Action = async (s, m) => await MoveTop(s, m)}); } - + if (ModuleState.PaneModuleIndex > 0) { - actionList.Add(new ActionViewModel {Name = "Move Up" , Action = async (s,m) => await MoveUp(s, m)}); + actionList.Add(new ActionViewModel {Name = "Move Up", Action = async (s, m) => await MoveUp(s, m)}); } - + if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1)) { - actionList.Add(new ActionViewModel {Name = "Move Down", Action = async (s,m) => await MoveDown(s, m) }); + actionList.Add(new ActionViewModel {Name = "Move Down", Action = async (s, m) => await MoveDown(s, m)}); } - + if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1)) { - actionList.Add(new ActionViewModel {Name = "Move To Bottom", Action = async (s,m) => await MoveBottom(s, m) }); + actionList.Add(new ActionViewModel {Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)}); } - - foreach (string pane in PageState.Page.Panes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + + foreach (string pane in PageState.Page.Panes.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries)) { if (pane != ModuleState.Pane) { - actionList.Add(new ActionViewModel {Name = "Move To " + pane + " Pane", Action = async (s,m) => await MoveToPane(s,pane, m) }); + actionList.Add(new ActionViewModel {Name = "Move To " + pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m)}); } } } + return actionList; } + private async Task EditUrlAsync(string url, int moduleId, string import) + { + await Task.Yield(); + EditUrl(moduleId, import); + return url; + } + protected async Task ModuleAction(ActionViewModel action) { - if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions)) + if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions)) { PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); string url = NavigateUrl(); - if (action.Action!=null) + if (action.Action != null) { url = await action.Action(url, pagemodule); } + NavigationManager.NavigateTo(url); } } private async Task MoveToPane(string url, string newPane, PageModule pagemodule) { - string oldPane = pagemodule.Pane; + string oldPane = pagemodule.Pane; pagemodule.Pane = newPane; pagemodule.Order = int.MaxValue; // add to bottom of pane await PageModuleService.UpdatePageModuleAsync(pagemodule); @@ -99,7 +107,7 @@ namespace Oqtane.Themes.Controls return url; } - private async Task DeleteModule(string url,PageModule pagemodule) + private async Task DeleteModule(string url, PageModule pagemodule) { pagemodule.IsDeleted = true; await PageModuleService.UpdatePageModuleAsync(pagemodule); @@ -107,8 +115,9 @@ namespace Oqtane.Themes.Controls return url; } - private string Settings(string url, PageModule pagemodule) + private async Task Settings(string url, PageModule pagemodule) { + await Task.Yield(); url = EditUrl(pagemodule.ModuleId, "Settings"); return url; } @@ -120,7 +129,7 @@ namespace Oqtane.Themes.Controls await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); return s; } - + private async Task MoveBottom(string s, PageModule pagemodule) { pagemodule.Order = int.MaxValue; @@ -128,6 +137,7 @@ namespace Oqtane.Themes.Controls await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); return s; } + private async Task MoveUp(string s, PageModule pagemodule) { pagemodule.Order -= 3; @@ -135,6 +145,7 @@ namespace Oqtane.Themes.Controls await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); return s; } + private async Task MoveDown(string s, PageModule pagemodule) { pagemodule.Order += 3; @@ -148,7 +159,6 @@ namespace Oqtane.Themes.Controls public string Name { set; get; } public Func> Action { set; get; } - } } } From 375c7060296b312dfea5c0e0d41f7d9c29fa0114 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Tue, 28 Apr 2020 14:51:27 +0200 Subject: [PATCH 183/265] Get custom theme and layout on a default install --- Oqtane.Client/UI/Installer.razor | 23 ------------------- .../Controllers/InstallationController.cs | 1 + .../Infrastructure/DatabaseManager.cs | 2 +- 3 files changed, 2 insertions(+), 24 deletions(-) diff --git a/Oqtane.Client/UI/Installer.razor b/Oqtane.Client/UI/Installer.razor index bc537880..c20459b3 100644 --- a/Oqtane.Client/UI/Installer.razor +++ b/Oqtane.Client/UI/Installer.razor @@ -179,31 +179,8 @@ }; var installation = await InstallationService.Install(config); - //TODO: Should be moved to Database manager if (installation.Success) { - Site site = new Site(); - site.TenantId = -1; // will be populated on server - site.Name = "Default Site"; - site.LogoFileId = null; - site.FaviconFileId = null; - site.DefaultThemeType = Constants.DefaultTheme; - site.DefaultLayoutType = Constants.DefaultLayout; - site.DefaultContainerType = Constants.DefaultContainer; - site.PwaIsEnabled = false; - site.PwaAppIconFileId = null; - site.PwaSplashIconFileId = null; - site.AllowRegistration = false; - site = await SiteService.AddSiteAsync(site, null); - - User user = new User(); - user.SiteId = site.SiteId; - user.Username = _hostUsername; - user.Password = _hostPassword; - user.Email = _hostEmail; - user.DisplayName = _hostUsername; - user = await UserService.AddUserAsync(user); - NavigationManager.NavigateTo("", true); } else diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index c812c47e..713d065a 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -44,6 +44,7 @@ namespace Oqtane.Controllers _config.Reload(); } + _databaseManager.BuildDefaultSite(config.Password, config.HostEmail); installation.Success = true; return installation; } diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index bffd2f7f..a896a337 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -315,7 +315,7 @@ namespace Oqtane.Infrastructure } } - private void BuildDefaultSite(string password, string email) + public void BuildDefaultSite(string password, string email) { using (var scope = _serviceScopeFactory.CreateScope()) { From 4e5ca01ebdd937e037c8bf950a2409e3c7b2250b Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Wed, 29 Apr 2020 21:55:12 +0200 Subject: [PATCH 184/265] WebAssembly references causes build error in some scenarios. Works without them. https://gitter.im/aspnet/Blazor?at=5e915f6ee24b4d6c44fe1827 --- .../External/Client/[Owner].[Module]s.Module.Client.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj index d23e3647..757eb45c 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj @@ -12,8 +12,8 @@ - - + + From 3be19df4a5268371b4dd9de40fd0e50b554f31ad Mon Sep 17 00:00:00 2001 From: Sean Long Date: Wed, 29 Apr 2020 20:49:12 -0400 Subject: [PATCH 185/265] Fixed add profile property --- Oqtane.Client/Modules/Admin/Profiles/Edit.razor | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor index d232a5ac..96a65b18 100644 --- a/Oqtane.Client/Modules/Admin/Profiles/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Profiles/Edit.razor @@ -144,7 +144,7 @@ { profile = new Profile(); } - + profile.Name = _name; profile.Title = _title; profile.Description = _description; @@ -154,8 +154,14 @@ profile.DefaultValue = _defaultvalue; profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired)); profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate)); - profile = await ProfileService.UpdateProfileAsync(profile); - + if (_profileid != -1) + { + profile = await ProfileService.UpdateProfileAsync(profile); + }else + { + profile = await ProfileService.AddProfileAsync(profile); + } + await logger.LogInformation("Profile Saved {Profile}", profile); NavigationManager.NavigateTo(NavigateUrl()); } From 34538dd945fa4bb02f789a0f71f4131d19d6c40d Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 30 Apr 2020 13:58:04 -0400 Subject: [PATCH 186/265] install/upgrade refactoring to consolidate all use cases and implement IInstallable interface for modules, moved tenant creation to site management UI, fixed z-order issues in Blazor theme, enhanced JS Interop methods to support integrity and crossorigin --- .../Templates/External/Client/Index.razor | 3 +- .../Templates/External/Client/ModuleInfo.cs | 3 +- .../Server/Manager/[Module]Manager.cs | 18 +- ...1.00.00.sql => [Owner].[Module].1.0.0.sql} | 0 ...all.sql => [Owner].[Module].Uninstall.sql} | 0 .../[Owner].[Module]s.Module.Server.csproj | 8 +- .../Modules/[Module]/Index.razor | 3 +- .../Modules/[Module]/ModuleInfo.cs | 3 +- .../[Module]/Manager/[Module]Manager.cs | 18 +- ...1.00.00.sql => [Owner].[Module].1.0.0.sql} | 0 ...all.sql => [Owner].[Module].Uninstall.sql} | 0 Oqtane.Client/Modules/Admin/Sites/Add.razor | 319 +++++--- Oqtane.Client/Modules/Admin/Tenants/Add.razor | 156 ---- .../Modules/Admin/Tenants/Edit.razor | 6 +- .../Modules/Admin/Tenants/Index.razor | 25 +- Oqtane.Client/UI/Installer.razor | 199 ++--- Oqtane.Client/UI/Interop.cs | 8 +- Oqtane.Client/UI/ThemeBuilder.razor | 6 +- .../Controllers/InstallationController.cs | 42 +- .../Controllers/ModuleDefinitionController.cs | 23 +- .../Infrastructure/DatabaseManager.cs | 759 ++++++++++-------- .../Interfaces/IDatabaseManager.cs | 12 + .../Infrastructure/Interfaces/IInstallable.cs | 8 +- Oqtane.Server/Infrastructure/LogManager.cs | 10 +- .../HtmlText/Manager/HtmlTextManager.cs | 8 +- Oqtane.Server/Oqtane.Server.csproj | 5 - Oqtane.Server/Program.cs | 5 +- .../Repository/Context/InstallationContext.cs | 6 +- .../Interfaces/IModuleDefinitionRepository.cs | 1 + .../Repository/Interfaces/ISqlRepository.cs | 2 +- .../Repository/ModuleDefinitionRepository.cs | 38 +- Oqtane.Server/Repository/ModuleRepository.cs | 24 +- Oqtane.Server/Repository/SiteRepository.cs | 4 +- Oqtane.Server/Repository/SqlRepository.cs | 41 +- Oqtane.Server/Repository/TenantResolver.cs | 77 +- Oqtane.Server/Scripts/Master.00.00.00.sql | 30 - Oqtane.Server/Scripts/Master.00.00.01.sql | 7 +- Oqtane.Server/Security/UserPermissions.cs | 9 +- Oqtane.Server/Startup.cs | 2 +- .../Oqtane.Themes.BlazorTheme/Theme.css | 31 +- Oqtane.Server/wwwroot/js/interop.js | 28 +- Oqtane.Shared/Models/Tenant.cs | 1 - Oqtane.Shared/Shared/Constants.cs | 1 + Oqtane.Shared/Shared/InstallConfig.cs | 14 +- 44 files changed, 1051 insertions(+), 912 deletions(-) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/{01.00.00.sql => [Owner].[Module].1.0.0.sql} (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/{Uninstall.sql => [Owner].[Module].Uninstall.sql} (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/{01.00.00.sql => [Owner].[Module].1.0.0.sql} (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/{Uninstall.sql => [Owner].[Module].Uninstall.sql} (100%) delete mode 100644 Oqtane.Client/Modules/Admin/Tenants/Add.razor create mode 100644 Oqtane.Server/Infrastructure/Interfaces/IDatabaseManager.cs diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor index 9318628b..68c31a34 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor @@ -62,7 +62,8 @@ else - Repository\I[Module]Repository.cs - interface for defining repository methods
    - Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core
    - Repository\[Module]Context.cs - provides a DB Context for data access
    -- Scripts\01.00.00.sql - database schema definition

    +- Scripts\[Module].1.0.0.sql - database schema definition script

    +- Scripts\[Module].Uninstall.sql - database uninstall script

    [RootPath]Shared\
    - [Owner].[Module]s.Module.Shared.csproj - shared project
    - Models\[Module].cs - model definition

    diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs index ed7c765b..a95a461e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs @@ -11,7 +11,8 @@ namespace [Owner].[Module]s.Modules Description = "[Module]", Version = "1.0.0", Dependencies = "[Owner].[Module]s.Module.Shared", - ServerManagerType = "[ServerManagerType]" + ServerManagerType = "[ServerManagerType]", + ReleaseVersions = "1.0.0" }; } } diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Manager/[Module]Manager.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Manager/[Module]Manager.cs index 7265d4eb..0b987600 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Manager/[Module]Manager.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Manager/[Module]Manager.cs @@ -3,18 +3,32 @@ using System.Linq; using System.Text.Json; using Oqtane.Modules; using Oqtane.Models; +using Oqtane.Infrastructure; +using Oqtane.Repository; using [Owner].[Module]s.Models; using [Owner].[Module]s.Repository; namespace [Owner].[Module]s.Manager { - public class [Module]Manager : IPortable + public class [Module]Manager : IInstallable, IPortable { private I[Module]Repository _[Module]s; + private ISqlRepository _sql; - public [Module]Manager(I[Module]Repository [Module]s) + public [Module]Manager(I[Module]Repository [Module]s, ISqlRepository sql) { _[Module]s = [Module]s; + _sql = sql; + } + + public bool Install(Tenant tenant, string version) + { + return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module]." + version + ".sql"); + } + + public bool Uninstall(Tenant tenant) + { + return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module].Uninstall.sql"); } public string ExportModule(Module module) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/01.00.00.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module].1.0.0.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/01.00.00.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module].1.0.0.sql diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/Uninstall.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module].Uninstall.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/Uninstall.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module].Uninstall.sql diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj index 884263a6..d9ec6aee 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj @@ -13,13 +13,13 @@ - - + + - - + + diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor index 90536f4a..15cf068b 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor @@ -54,7 +54,8 @@ else - Repository\I[Module]Repository.cs - interface for defining repository methods
    - Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core
    - Repository\[Module]Context.cs - provides a DB Context for data access
    -- Scripts\01.00.00.sql - database schema definition

    +- Scripts\[Module].1.0.0.sql - database schema definition script

    +- Scripts\[Module].Uninstall.sql - database uninstall script

    [RootPath]Oqtane.Shared\Modules\[Module]\
    - Models\[Module].cs - model definition

    diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs index ed7c765b..a95a461e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs @@ -11,7 +11,8 @@ namespace [Owner].[Module]s.Modules Description = "[Module]", Version = "1.0.0", Dependencies = "[Owner].[Module]s.Module.Shared", - ServerManagerType = "[ServerManagerType]" + ServerManagerType = "[ServerManagerType]", + ReleaseVersions = "1.0.0" }; } } diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs index 7265d4eb..0b987600 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs @@ -3,18 +3,32 @@ using System.Linq; using System.Text.Json; using Oqtane.Modules; using Oqtane.Models; +using Oqtane.Infrastructure; +using Oqtane.Repository; using [Owner].[Module]s.Models; using [Owner].[Module]s.Repository; namespace [Owner].[Module]s.Manager { - public class [Module]Manager : IPortable + public class [Module]Manager : IInstallable, IPortable { private I[Module]Repository _[Module]s; + private ISqlRepository _sql; - public [Module]Manager(I[Module]Repository [Module]s) + public [Module]Manager(I[Module]Repository [Module]s, ISqlRepository sql) { _[Module]s = [Module]s; + _sql = sql; + } + + public bool Install(Tenant tenant, string version) + { + return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module]." + version + ".sql"); + } + + public bool Uninstall(Tenant tenant) + { + return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module].Uninstall.sql"); } public string ExportModule(Module module) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/01.00.00.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module].1.0.0.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/01.00.00.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module].1.0.0.sql diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/Uninstall.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module].Uninstall.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/Uninstall.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module].Uninstall.sql diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 7c18431c..40ac6f39 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -5,8 +5,9 @@ @inject IAliasService AliasService @inject ISiteService SiteService @inject IThemeService ThemeService -@inject ISiteTemplateService SiteTemplateService +@inject ISiteTemplateService SiteTemplateService @inject IUserService UserService +@inject IInstallationService InstallationService @if (_tenants == null) { @@ -17,21 +18,7 @@ else - - - - - @if (!_isinitialized) + + + + + @if (_tenantid == "+") { + + + + + + + + + + + + + + + + + + + + + + + + @if (!_integratedsecurity) + { + + + + + + + + + } @@ -116,7 +188,7 @@ else } @@ -132,16 +204,24 @@ else private List _siteTemplates; private List _themeList; private List _tenants; - private string _tenantid = "-1"; + private string _tenantid = "-"; + + private string _tenantname = string.Empty; + private string _databasetype = "LocalDB"; + private string _server = "(LocalDb)\\MSSQLLocalDB"; + private string _database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm"); + private string _username = string.Empty; + private string _password = string.Empty; + private bool _integratedsecurity = true; + private string _hostusername = Constants.HostUser; + private string _hostpassword = string.Empty; + private string _name = string.Empty; private string _urls = string.Empty; private string _themetype = string.Empty; private string _layouttype = string.Empty; private string _containertype = string.Empty; private string _sitetemplatetype = string.Empty; - private bool _isinitialized = true; - private string _username = string.Empty; - private string _password = string.Empty; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; @@ -153,29 +233,29 @@ else _themes = ThemeService.GetThemeTypes(_themeList); _containers = ThemeService.GetContainerTypes(_themeList); _siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync(); - _username = Constants.HostUser; } - private async void TenantChanged(ChangeEventArgs e) + private void TenantChanged(ChangeEventArgs e) { - try + _tenantid = (string)e.Value; + if (string.IsNullOrEmpty(_tenantname)) { - _tenantid = (string)e.Value; - if (_tenantid != "-1") - { - var tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid)); - if (tenant != null) - { - _isinitialized = tenant.IsInitialized; - StateHasChanged(); - } - } + _tenantname = _name; } - catch (Exception ex) + StateHasChanged(); + } + + private void SetIntegratedSecurity(ChangeEventArgs e) + { + if (Convert.ToBoolean((string)e.Value)) { - await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", _tenantid, ex.Message); - AddModuleMessage("Error Loading Tenant", MessageType.Error); + _integratedsecurity = true; } + else + { + _integratedsecurity = false; + } + StateHasChanged(); } private async void ThemeChanged(ChangeEventArgs e) @@ -191,7 +271,7 @@ else { _panelayouts = new Dictionary(); } - + StateHasChanged(); } catch (Exception ex) @@ -203,105 +283,116 @@ else private async Task SaveSite() { - if (_tenantid != "-1" && _name != string.Empty && _urls != string.Empty && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)) && !string.IsNullOrEmpty(_containertype) && !string.IsNullOrEmpty(_sitetemplatetype)) + if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)) && !string.IsNullOrEmpty(_containertype) && !string.IsNullOrEmpty(_sitetemplatetype)) { - var unique = true; + var duplicates = new List(); var aliases = await AliasService.GetAliasesAsync(); foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { if (aliases.Exists(item => item.Name == name)) { - unique = false; + duplicates.Add(name); } } - - if (unique) + + if (duplicates.Count == 0) { - var isvalid = true; + InstallConfig config = new InstallConfig(); - if (!_isinitialized) + if (_tenantid == "+") { - var user = new User(); - user.SiteId = PageState.Site.SiteId; - user.Username = _username; - user.Password = _password; - user = await UserService.LoginUserAsync(user, false, false); - isvalid = user.IsAuthenticated; - } - - if (isvalid) - { - ShowProgressIndicator(); - - aliases = new List(); - _urls = _urls.Replace("\n", ","); - - foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) - { - var alias = new Alias(); - alias.Name = name; - alias.TenantId = int.Parse(_tenantid); - alias.SiteId = -1; - alias = await AliasService.AddAliasAsync(alias); - aliases.Add(alias); - } - - var site = new Site(); - site.TenantId = int.Parse(_tenantid); - site.Name = _name; - site.LogoFileId = null; - site.FaviconFileId = null; - site.DefaultThemeType = _themetype; - site.DefaultLayoutType = (_layouttype == null ? string.Empty : _layouttype); - site.DefaultContainerType = _containertype; - site.PwaIsEnabled = false; - site.PwaAppIconFileId = null; - site.PwaSplashIconFileId = null; - site.AllowRegistration = false; - site.SiteTemplateType = _sitetemplatetype; - site = await SiteService.AddSiteAsync(site, aliases[0]); - - foreach (Alias alias in aliases) - { - alias.SiteId = site.SiteId; - await AliasService.UpdateAliasAsync(alias); - } - - if (!_isinitialized) + if (!string.IsNullOrEmpty(_tenantname) && _tenants.FirstOrDefault(item => item.Name == _tenantname) == null) { + // validate host credentials var user = new User(); - user.SiteId = site.SiteId; - user.Username = _username; - user.Password = _password; - user.Email = PageState.User.Email; - user.DisplayName = PageState.User.DisplayName; - user = await UserService.AddUserAsync(user, aliases[0]); - - if (user != null) + user.SiteId = PageState.Site.SiteId; + user.Username = Constants.HostUser; + user.Password = _hostpassword; + user = await UserService.LoginUserAsync(user, false, false); + if (user.IsAuthenticated) { - var tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid)); - if (tenant != null) + if (!string.IsNullOrEmpty(_server) && !string.IsNullOrEmpty(_database)) { - tenant.IsInitialized = true; - await TenantService.UpdateTenantAsync(tenant); + var connectionString = string.Empty; + if (_databasetype == "LocalDB") + { + connectionString = "Data Source=" + _server + ";AttachDbFilename=|DataDirectory|\\" + _database + ".mdf;Initial Catalog=" + _database + ";Integrated Security=SSPI;"; + } + else + { + connectionString = "Data Source=" + _server + ";Initial Catalog=" + _database + ";"; + + if (_integratedsecurity) + { + connectionString += "Integrated Security=SSPI;"; + } + else + { + connectionString += "User ID=" + _username + ";Password=" + _password; + } + } + + config.ConnectionString = connectionString; + config.HostPassword = _hostpassword; + config.HostEmail = user.Email; + config.HostName = user.DisplayName; + config.TenantName = _tenantname; + config.IsNewTenant = true; + } + else + { + AddModuleMessage("You Must Specify A Server And Database", MessageType.Error); } } + else + { + AddModuleMessage("Invalid Host Password", MessageType.Error); + } + } + else + { + AddModuleMessage("Tenant Name Is Missing Or Already Exists", MessageType.Error); } - - await Log(aliases[0], LogLevel.Information, string.Empty, null, "Site Created {Site}", site); - - var uri = new Uri(NavigationManager.Uri); - NavigationManager.NavigateTo(uri.Scheme + "://" + aliases[0].Name, true); } else { - await logger.LogError("Invalid Password Entered For Host {Username}", _username); - AddModuleMessage("Invalid Host Password", MessageType.Error); + var tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid)); + if (tenant != null) + { + config.TenantName = tenant.Name; + config.ConnectionString= tenant.DBConnectionString; + config.IsNewTenant = false; + } + } + + if (!string.IsNullOrEmpty(config.TenantName)) + { + config.SiteName = _name; + config.Aliases = _urls.Replace("\n", ","); + config.DefaultTheme = _themetype; + config.DefaultLayout = _layouttype; + config.DefaultContainer = _containertype; + config.SiteTemplate = _sitetemplatetype; + + ShowProgressIndicator(); + + var installation = await InstallationService.Install(config); + if (installation.Success) + { + var aliasname = config.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0]; + var uri = new Uri(NavigationManager.Uri); + NavigationManager.NavigateTo(uri.Scheme + "://" + aliasname, true); + } + else + { + await logger.LogError("Error Creating Site {Error}", installation.Message); + AddModuleMessage(installation.Message, MessageType.Error); + } } } else { - AddModuleMessage("An Alias Specified Has Already Been Used For Another Site", MessageType.Warning); + AddModuleMessage(string.Join(", ", duplicates.ToArray()) + " Already Used For Another Site", MessageType.Warning); } } else diff --git a/Oqtane.Client/Modules/Admin/Tenants/Add.razor b/Oqtane.Client/Modules/Admin/Tenants/Add.razor deleted file mode 100644 index 3fedf8fb..00000000 --- a/Oqtane.Client/Modules/Admin/Tenants/Add.razor +++ /dev/null @@ -1,156 +0,0 @@ -@namespace Oqtane.Modules.Admin.Tenants -@inherits ModuleBase -@inject NavigationManager NavigationManager -@inject ITenantService TenantService -@inject IInstallationService InstallationService - -
    - - - -
    - + @@ -101,14 +88,99 @@ else
    + + + +
    +
    +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    - +
    - +
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - -Cancel - -@code { - private string name = string.Empty; - private string type = "LocalDB"; - private string server = "(LocalDb)\\MSSQLLocalDB"; - private string database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm"); - private string username = string.Empty; - private string password = string.Empty; - private string schema = string.Empty; - private string integratedsecurity = "display: none;"; - - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; - - private void SetIntegratedSecurity(ChangeEventArgs e) - { - if (Convert.ToBoolean((string)e.Value)) - { - integratedsecurity = "display: none;"; - } - else - { - integratedsecurity = string.Empty; - } - } - - private async Task SaveTenant() - { - if (!string.IsNullOrEmpty(name)) - { - ShowProgressIndicator(); - - var connectionString = string.Empty; - if (type == "LocalDB") - { - connectionString = "Data Source=" + server + ";AttachDbFilename=|DataDirectory|\\" + database + ".mdf;Initial Catalog=" + database + ";Integrated Security=SSPI;"; - } - else - { - connectionString = "Data Source=" + server + ";Initial Catalog=" + database + ";"; - - if (integratedsecurity == "display: none;") - { - connectionString += "Integrated Security=SSPI;"; - } - else - { - connectionString += "User ID=" + username + ";Password=" + password; - - } - } - - var config = new InstallConfig - { - IsMaster = false, - ConnectionString = connectionString, - }; - - var installation = await InstallationService.Install(config); - if (installation.Success) - { - //TODO : Move to Database Manager - var tenant = new Tenant - { - Name = name, - DBConnectionString = connectionString, - IsInitialized = false - }; - await TenantService.AddTenantAsync(tenant); - await logger.LogInformation("Tenant Created {Tenant}", tenant); - - NavigationManager.NavigateTo(NavigateUrl()); - } - else - { - await logger.LogError("Error Creating Tenant {Error}", installation.Message); - AddModuleMessage(installation.Message, MessageType.Error); - } - } - else - { - AddModuleMessage("You Must Provide A Name For The Tenant", MessageType.Warning); - } - } -} diff --git a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor index f8a06db1..94ee91d6 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor @@ -6,7 +6,7 @@ diff --git a/Oqtane.Client/Modules/Admin/Tenants/Index.razor b/Oqtane.Client/Modules/Admin/Tenants/Index.razor index 018c1f68..ead272dc 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Index.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Index.razor @@ -1,6 +1,7 @@ @namespace Oqtane.Modules.Admin.Tenants @inherits ModuleBase @inject ITenantService TenantService +@inject IAliasService AliasService @if (tenants == null) { @@ -8,8 +9,6 @@ } else { - -
    @@ -39,9 +38,25 @@ else { try { - await TenantService.DeleteTenantAsync(Tenant.TenantId); - await logger.LogInformation("Tenant Deleted {Tenant}", Tenant); - StateHasChanged(); + string message = string.Empty; + var aliases = await AliasService.GetAliasesAsync(); + foreach (var alias in aliases) + { + if (alias.TenantId == Tenant.TenantId) + { + message += ", " + alias.Name; + } + } + if (string.IsNullOrEmpty(message)) + { + await TenantService.DeleteTenantAsync(Tenant.TenantId); + await logger.LogInformation("Tenant Deleted {Tenant}", Tenant); + StateHasChanged(); + } + else + { + AddModuleMessage("Tenant Cannot Be Deleted Until The Following Sites Are Deleted: " + message.Substring(2), MessageType.Warning); + } } catch (Exception ex) { diff --git a/Oqtane.Client/UI/Installer.razor b/Oqtane.Client/UI/Installer.razor index c20459b3..c3afd00d 100644 --- a/Oqtane.Client/UI/Installer.razor +++ b/Oqtane.Client/UI/Installer.razor @@ -7,116 +7,116 @@
    - +
    -
    +
    -

    Database Configuration


    +

    Database Configuration


    - + @if (name == Constants.MasterTenant) @@ -21,10 +21,10 @@
    - + - +
     
    - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + +
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    - - - -
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    -

    Application Administrator


    +

    Application Administrator


    - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
    - - - -
    - - - -
    - - - -
    - - - -
    + + + +
    + + + +
    + + + +
    + + + +
    -
    +
    -

    +

    @((MarkupString) _message)
    @@ -146,7 +146,7 @@ private async Task Install() { - if (_hostUsername != "" && _hostPassword.Length >= 6 && _hostPassword == _confirmPassword && _hostEmail != "") + if (_serverName != "" && _databaseName != "" && _hostUsername != "" && _hostPassword.Length >= 6 && _hostPassword == _confirmPassword && _hostEmail != "") { _loadingDisplay = ""; StateHasChanged(); @@ -169,19 +169,24 @@ } } + Uri uri = new Uri(NavigationManager.Uri); + var config = new InstallConfig { ConnectionString = connectionstring, - HostUser = _hostUsername, + Aliases = uri.Authority, HostEmail = _hostEmail, - Password = _hostPassword, - IsMaster = true, + HostPassword = _hostPassword, + HostName = Constants.HostUser, + TenantName = Constants.MasterTenant, + IsNewTenant = true, + SiteName = Constants.DefaultSite }; var installation = await InstallationService.Install(config); if (installation.Success) { - NavigationManager.NavigateTo("", true); + NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true); } else { diff --git a/Oqtane.Client/UI/Interop.cs b/Oqtane.Client/UI/Interop.cs index b57d1444..73aaf029 100644 --- a/Oqtane.Client/UI/Interop.cs +++ b/Oqtane.Client/UI/Interop.cs @@ -73,13 +73,13 @@ namespace Oqtane.UI } } - public Task IncludeLink(string id, string rel, string url, string type) + public Task IncludeLink(string id, string rel, string url, string type, string integrity, string crossorigin) { try { _jsRuntime.InvokeAsync( "interop.includeLink", - id, rel, url, type); + id, rel, url, type, integrity, crossorigin); return Task.CompletedTask; } catch @@ -88,13 +88,13 @@ namespace Oqtane.UI } } - public Task IncludeScript(string id, string src, string content, string location) + public Task IncludeScript(string id, string src, string content, string location, string integrity, string crossorigin) { try { _jsRuntime.InvokeAsync( "interop.includeScript", - id, src, content, location); + id, src, content, location, integrity, crossorigin); return Task.CompletedTask; } catch diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index 4f5d76f2..1649ac89 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -22,7 +22,7 @@ } if (PageState.Site.FaviconFileId != null) { - await interop.IncludeLink("fav-icon", "shortcut icon", Utilities.ContentUrl(PageState.Alias.Path, PageState.Site.FaviconFileId.Value), "image/x-icon"); + await interop.IncludeLink("fav-icon", "shortcut icon", Utilities.ContentUrl(PageState.Alias.Path, PageState.Site.FaviconFileId.Value), "image/x-icon", "", ""); } if (PageState.Site.PwaIsEnabled) { @@ -74,7 +74,7 @@ "document.getElementById('pwa-manifest').setAttribute('href', url); " + "} " + ", 1000);"; - await interop.IncludeScript("pwa-manifestscript", "", manifest, "body"); + await interop.IncludeScript("pwa-manifestscript", "", manifest, "body", "", ""); // service worker must be in root of site string serviceworker = "if ('serviceWorker' in navigator) { " + @@ -84,6 +84,6 @@ "console.log('ServiceWorker Registration Failed ', err); " + "}); " + "}"; - await interop.IncludeScript("pwa-serviceworker", "", serviceworker, "body"); + await interop.IncludeScript("pwa-serviceworker", "", serviceworker, "body", "", ""); } } diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index 713d065a..b930db48 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -5,8 +5,6 @@ using Oqtane.Models; using Oqtane.Shared; using Oqtane.Infrastructure; -// ReSharper disable StringIndexOfIsCultureSpecific.1 - namespace Oqtane.Controllers { [Route("{site}/api/[controller]")] @@ -14,9 +12,9 @@ namespace Oqtane.Controllers { private readonly IConfigurationRoot _config; private readonly IInstallationManager _installationManager; - private readonly DatabaseManager _databaseManager; + private readonly IDatabaseManager _databaseManager; - public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, DatabaseManager databaseManager) + public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, IDatabaseManager databaseManager) { _config = config; _installationManager = installationManager; @@ -27,33 +25,17 @@ namespace Oqtane.Controllers [HttpPost] public Installation Post([FromBody] InstallConfig config) { - //TODO Security ???? var installation = new Installation {Success = false, Message = ""}; - if (ModelState.IsValid && (!_databaseManager.IsInstalled || !config.IsMaster)) + if (ModelState.IsValid && (User.IsInRole(Constants.HostRole) || string.IsNullOrEmpty(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) { - bool master = config.IsMaster; - - config.Alias = config.Alias ?? HttpContext.Request.Host.Value; - var result = DatabaseManager.InstallDatabase(config); - - if (result.Success) - { - if (master) - { - _config.Reload(); - } - - _databaseManager.BuildDefaultSite(config.Password, config.HostEmail); - installation.Success = true; - return installation; - } - - installation.Message = result.Message; - return installation; + installation = _databaseManager.Install(config); + } + else + { + installation.Message = "Installation Not Authorized"; } - installation.Message = "Application Is Already Installed"; return installation; } @@ -61,12 +43,8 @@ namespace Oqtane.Controllers [HttpGet("installed")] public Installation IsInstalled() { - var installation = new Installation {Success = false, Message = ""}; - - installation.Success = _databaseManager.IsInstalled; - installation.Message = _databaseManager.Message; - - return installation; + bool isInstalled = _databaseManager.IsInstalled(); + return new Installation {Success = isInstalled, Message = string.Empty}; } [HttpGet("upgrade")] diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 4ba5d613..673e42b3 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -22,16 +22,18 @@ namespace Oqtane.Controllers { private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly IModuleRepository _modules; + private readonly ITenantRepository _tenants; private readonly IUserPermissions _userPermissions; private readonly IInstallationManager _installationManager; private readonly IWebHostEnvironment _environment; private readonly IServiceProvider _serviceProvider; private readonly ILogManager _logger; - public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ILogManager logger) + public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules,ITenantRepository tenants, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ILogManager logger) { _moduleDefinitions = moduleDefinitions; _modules = modules; + _tenants = tenants; _userPermissions = userPermissions; _installationManager = installationManager; _environment = environment; @@ -104,8 +106,18 @@ namespace Oqtane.Controllers Type moduletype = Type.GetType(moduledefinition.ServerManagerType); if (moduletype != null && moduletype.GetInterface("IInstallable") != null) { - var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); - ((IInstallable)moduleobject).Uninstall(); + foreach (Tenant tenant in _tenants.GetTenants()) + { + try + { + var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); + ((IInstallable)moduleobject).Uninstall(tenant); + } + catch + { + // an error occurred executing the uninstall + } + } } } @@ -190,6 +202,11 @@ namespace Oqtane.Controllers module.ModuleDefinitionName = moduleDefinition.ModuleDefinitionName; _modules.UpdateModule(module); + if (moduleDefinition.Template == "internal") + { + // need logic to add embedded scripts to Oqtane.Server.csproj - also you need to remove them on uninstall + } + _installationManager.RestartApplication(); } } diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index a896a337..7fd44c66 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.Data; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using DbUp; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; @@ -15,193 +15,172 @@ using Oqtane.Extensions; using Oqtane.Models; using Oqtane.Repository; using Oqtane.Shared; +using Oqtane.Enums; using File = System.IO.File; namespace Oqtane.Infrastructure { - public class DatabaseManager + public class DatabaseManager : IDatabaseManager { private readonly IConfigurationRoot _config; private readonly IServiceScopeFactory _serviceScopeFactory; - private bool _isInstalled; + private readonly IMemoryCache _cache; - public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory) + public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory, IMemoryCache cache) { _config = config; _serviceScopeFactory = serviceScopeFactory; + _cache = cache; } - public string Message { get; set; } - - public void StartupMigration() + public bool IsInstalled() { - var defaultConnectionString = _config.GetConnectionString(SettingKeys.ConnectionStringKey); - var defaultAlias = GetInstallationConfig(SettingKeys.DefaultAliasKey, string.Empty); - var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); - - //create data directory if does not exists - if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory); - - // if no values specified, fallback to IDE installer - if (string.IsNullOrEmpty(defaultConnectionString)) - { - IsInstalled = false; - return; - } - - var freshInstall = !IsMasterInstalled(defaultConnectionString); - var password = GetInstallationConfig(SettingKeys.HostPasswordKey, String.Empty); - var email = GetInstallationConfig(SettingKeys.HostEmailKey, String.Empty); - if (freshInstall && (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(email) || string.IsNullOrEmpty(defaultAlias))) - { - IsInstalled = false; - Message = "Incomplete startup install configuration"; - return; - } - - var result = MasterMigration(defaultConnectionString, defaultAlias, null, true); - IsInstalled = result.Success; - - if (result.Success) - { - WriteVersionInfo(defaultConnectionString); - TenantMigration(defaultConnectionString, dataDirectory); - } - - if (_isInstalled && !IsDefaultSiteInstalled(defaultConnectionString)) - { - BuildDefaultSite(password,email); - } - } - - public bool IsInstalled - { - get - { - if (!_isInstalled) _isInstalled = CheckInstallState(); - - return _isInstalled; - } - set => _isInstalled = value; - } - - private bool CheckInstallState() - { - var defaultConnectionString = _config.GetConnectionString(SettingKeys.ConnectionStringKey); + var defaultConnectionString = NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)); var result = !string.IsNullOrEmpty(defaultConnectionString); if (result) { using (var scope = _serviceScopeFactory.CreateScope()) { - var dbContext = scope.ServiceProvider.GetRequiredService(); - result = dbContext.Database.CanConnect(); + var db = scope.ServiceProvider.GetRequiredService(); + result = db.Database.CanConnect(); + if (result) + { + try + { + result = db.Tenant.Any(); + } + catch + { + result = false; + } + } } - if (result) - { - //I think this is obsolete now and not accurate, maybe check presence of some table, Version ??? - var dbUpgradeConfig = DeployChanges - .To - .SqlDatabase(defaultConnectionString) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master")); + } + return result; + } - result = !dbUpgradeConfig.Build().IsUpgradeRequired(); - if (!result) Message = "Master Installation Scripts Have Not Been Executed"; + public Installation Install() + { + return Install(null); + } + + public Installation Install(InstallConfig install) + { + var result = new Installation { Success = false, Message = string.Empty }; + + // get configuration + if (install == null) + { + // startup or silent installation + install = new InstallConfig { ConnectionString = _config.GetConnectionString(SettingKeys.ConnectionStringKey), TenantName = Constants.MasterTenant, IsNewTenant = false }; + + if (!IsInstalled()) + { + install.Aliases = GetInstallationConfig(SettingKeys.DefaultAliasKey, string.Empty); + install.HostPassword = GetInstallationConfig(SettingKeys.HostPasswordKey, string.Empty); + install.HostEmail = GetInstallationConfig(SettingKeys.HostEmailKey, string.Empty); + + if (!string.IsNullOrEmpty(install.ConnectionString) && !string.IsNullOrEmpty(install.Aliases) && !string.IsNullOrEmpty(install.HostPassword) && !string.IsNullOrEmpty(install.HostEmail)) + { + // silent install + install.HostName = Constants.HostUser; + install.SiteTemplate = GetInstallationConfig(SettingKeys.SiteTemplateKey, Constants.DefaultSiteTemplate); + install.DefaultTheme = GetInstallationConfig(SettingKeys.DefaultThemeKey, Constants.DefaultTheme); + install.DefaultLayout = GetInstallationConfig(SettingKeys.DefaultLayoutKey, Constants.DefaultLayout); + install.DefaultContainer = GetInstallationConfig(SettingKeys.DefaultContainerKey, Constants.DefaultContainer); + install.SiteName = Constants.DefaultSite; + install.IsNewTenant = true; + } + else + { + // silent installation is missing required information + install.ConnectionString = ""; + } + } + } + else + { + // install wizard or add new site + if (!string.IsNullOrEmpty(install.Aliases)) + { + if (string.IsNullOrEmpty(install.SiteTemplate)) + { + install.SiteTemplate = GetInstallationConfig(SettingKeys.SiteTemplateKey, Constants.DefaultSiteTemplate); + } + if (string.IsNullOrEmpty(install.DefaultTheme)) + { + install.DefaultTheme = GetInstallationConfig(SettingKeys.DefaultThemeKey, Constants.DefaultTheme); + if (string.IsNullOrEmpty(install.DefaultLayout)) + { + install.DefaultLayout = GetInstallationConfig(SettingKeys.DefaultLayoutKey, Constants.DefaultLayout); + } + } + if (string.IsNullOrEmpty(install.DefaultContainer)) + { + install.DefaultContainer = GetInstallationConfig(SettingKeys.DefaultContainerKey, Constants.DefaultContainer); + } } else { - Message = "Database is not available"; + result.Message = "Invalid Installation Configuration"; + install.ConnectionString = ""; } } - else + + // proceed with installation/migration + if (!string.IsNullOrEmpty(install.ConnectionString)) { - Message = "Connection string is empty"; + result = CreateDatabase(install); + if (result.Success) + { + result = MigrateMaster(install); + if (result.Success) + { + result = CreateTenant(install); + if (result.Success) + { + result = MigrateTenants(install); + if (result.Success) + { + result = MigrateModules(install); + if (result.Success) + { + result = CreateSite(install); + } + } + } + } + } } + return result; } - - public static string NormalizeConnectionString(string connectionString, string dataDirectory) + private Installation CreateDatabase(InstallConfig install) { - connectionString = connectionString - .Replace("|DataDirectory|", dataDirectory); - return connectionString; - } + var result = new Installation { Success = false, Message = string.Empty }; - public static Installation InstallDatabase([NotNull] InstallConfig installConfig) - { - var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); - var result = new Installation {Success = false, Message = ""}; - - var alias = installConfig.Alias; - var connectionString = NormalizeConnectionString(installConfig.ConnectionString, dataDirectory); - - if (!string.IsNullOrEmpty(connectionString) && !string.IsNullOrEmpty(alias)) + if (install.IsNewTenant) { - result = MasterMigration(connectionString, alias, result, installConfig.IsMaster); - if (installConfig.IsMaster && result.Success) + try { - WriteVersionInfo(connectionString); - TenantMigration(connectionString, dataDirectory); - UpdateConnectionStringSetting(connectionString); + //create data directory if does not exist + var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); + if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory); + + using (var dbc = new DbContext(new DbContextOptionsBuilder().UseSqlServer(NormalizeConnectionString(install.ConnectionString)).Options)) + { + // create empty database if it does not exist + dbc.Database.EnsureCreated(); + result.Success = true; + } } - return result; - } - - result = new Installation - { - Success = false, - Message = "Connection string is empty", - }; - return result; - } - - private static Installation MasterMigration(string connectionString, string alias, Installation result, bool master) - { - if (result == null) result = new Installation {Success = false, Message = string.Empty}; - - bool firstInstall; - try - { - // create empty database if does not exists - // dbup database creation does not work correctly on localdb databases - using (var dbc = new DbContext(new DbContextOptionsBuilder().UseSqlServer(connectionString).Options)) + catch (Exception ex) { - dbc.Database.EnsureCreated(); - //check for vanilla db - firstInstall = !TableExists(dbc, "SchemaVersions"); + result.Message = ex.Message; } } - catch (Exception e) - { - result.Message = e.Message; - Console.WriteLine(e); - return result; - } - // when alias is not specified on first install, fallback to ide - if (firstInstall && string.IsNullOrEmpty(alias)) return result; - - var dbUpgradeConfig = DeployChanges - .To - .SqlDatabase(connectionString) - .WithVariable("ConnectionString", connectionString) - .WithVariable("Alias", alias) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master.")); - - var dbUpgrade = dbUpgradeConfig.Build(); - if (!dbUpgrade.IsUpgradeRequired()) - { - result.Success = true; - result.Message = string.Empty; - return result; - } - - var upgradeResult = dbUpgrade.PerformUpgrade(); - if (!upgradeResult.Successful) - { - Console.WriteLine(upgradeResult.Error.Message); - result.Message = upgradeResult.Error.Message; - } else { result.Success = true; @@ -210,74 +189,326 @@ namespace Oqtane.Infrastructure return result; } - private static void ModuleMigration(Assembly assembly, string connectionString) + private Installation MigrateMaster(InstallConfig install) { - Console.WriteLine($"Migrating assembly {assembly.FullName}"); - var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) - .WithScriptsEmbeddedInAssembly(assembly, s => !s.ToLower().Contains("uninstall.sql")); // scripts must be included as Embedded Resources - var dbUpgrade = dbUpgradeConfig.Build(); - if (dbUpgrade.IsUpgradeRequired()) + var result = new Installation { Success = false, Message = string.Empty }; + + if (install.TenantName == Constants.MasterTenant) { - var result = dbUpgrade.PerformUpgrade(); - if (!result.Successful) + var upgradeConfig = DeployChanges + .To + .SqlDatabase(NormalizeConnectionString(install.ConnectionString)) + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master.")); + + var upgrade = upgradeConfig.Build(); + if (upgrade.IsUpgradeRequired()) { - // TODO: log result.Error.Message - problem is logger is not available here + var upgradeResult = upgrade.PerformUpgrade(); + result.Success = upgradeResult.Successful; + if (!result.Success) + { + result.Message = upgradeResult.Error.Message; + } + } + else + { + result.Success = true; + } + + if (result.Success) + { + CreateApplicationVersion(install.ConnectionString); + UpdateConnectionString(install.ConnectionString); } } + else + { + result.Success = true; + } + + return result; } - private static void WriteVersionInfo(string connectionString) + private Installation CreateTenant(InstallConfig install) { - using (var db = new InstallationContext(connectionString)) + var result = new Installation { Success = false, Message = string.Empty }; + + if (!string.IsNullOrEmpty(install.TenantName) && !string.IsNullOrEmpty(install.Aliases)) { - var version = db.ApplicationVersion.ToList().LastOrDefault(); - if (version == null || version.Version != Constants.Version) + using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) { - version = new ApplicationVersion {Version = Constants.Version, CreatedOn = DateTime.UtcNow}; + Tenant tenant; + if (install.IsNewTenant) + { + tenant = new Tenant { Name = install.TenantName, DBConnectionString = DenormalizeConnectionString(install.ConnectionString), CreatedBy = "", CreatedOn = DateTime.UtcNow, ModifiedBy = "", ModifiedOn = DateTime.UtcNow }; + db.Tenant.Add(tenant); + db.SaveChanges(); + _cache.Remove("tenants"); + + if (install.TenantName == Constants.MasterTenant) + { + var job = new Job { Name = "Notification Job", JobType = "Oqtane.Infrastructure.NotificationJob, Oqtane.Server", Frequency = "m", Interval = 1, StartDate = null, EndDate = null, IsEnabled = false, IsStarted = false, IsExecuting = false, NextExecution = null, RetentionHistory = 10, CreatedBy = "", CreatedOn = DateTime.UtcNow, ModifiedBy = "", ModifiedOn = DateTime.UtcNow }; + db.Job.Add(job); + db.SaveChanges(); + _cache.Remove("jobs"); + } + } + else + { + tenant = db.Tenant.FirstOrDefault(item => item.Name == install.TenantName); + } + + foreach (string aliasname in install.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + { + var alias = new Alias { Name = aliasname, TenantId = tenant.TenantId, SiteId = -1, CreatedBy = "", CreatedOn = DateTime.UtcNow, ModifiedBy = "", ModifiedOn = DateTime.UtcNow }; + db.Alias.Add(alias); + db.SaveChanges(); + } + _cache.Remove("aliases"); + } + } + + result.Success = true; + + return result; + } + + private Installation MigrateTenants(InstallConfig install) + { + var result = new Installation { Success = false, Message = string.Empty }; + + using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) + { + foreach (var tenant in db.Tenant.ToList()) + { + var upgradeConfig = DeployChanges.To.SqlDatabase(NormalizeConnectionString(tenant.DBConnectionString)) + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant")); + + var upgrade = upgradeConfig.Build(); + if (upgrade.IsUpgradeRequired()) + { + var upgradeResult = upgrade.PerformUpgrade(); + result.Success = upgradeResult.Successful; + if (!result.Success) + { + result.Message = upgradeResult.Error.Message; + } + } + } + } + + if (string.IsNullOrEmpty(result.Message)) + { + result.Success = true; + } + + return result; + } + + private Installation MigrateModules(InstallConfig install) + { + var result = new Installation { Success = false, Message = string.Empty }; + + using (var scope = _serviceScopeFactory.CreateScope()) + { + var moduledefinitions = scope.ServiceProvider.GetRequiredService(); + foreach (var moduledefinition in moduledefinitions.GetModuleDefinitions()) + { + if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType) && !string.IsNullOrEmpty(moduledefinition.ReleaseVersions)) + { + string[] versions = moduledefinition.ReleaseVersions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) + { + foreach (var tenant in db.Tenant.ToList()) + { + int index = Array.FindIndex(versions, item => item == moduledefinition.Version); + if (tenant.Name == install.TenantName && install.TenantName != Constants.MasterTenant) + { + index = -1; + } + if (index != (versions.Length - 1)) + { + if (index == -1) index = 0; + for (int i = index; i < versions.Length; i++) + { + Type moduletype = Type.GetType(moduledefinition.ServerManagerType); + if (moduletype != null && moduletype.GetInterface("IInstallable") != null) + { + try + { + var moduleobject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduletype); + ((IInstallable)moduleobject).Install(tenant, versions[i]); + } + catch (Exception ex) + { + result.Message = "An Error Occurred Installing " + moduledefinition.Name + " - " + ex.Message.ToString(); + } + } + } + } + } + if (moduledefinition.Version != versions[versions.Length - 1]) + { + moduledefinition.Version = versions[versions.Length - 1]; + db.Entry(moduledefinition).State = EntityState.Modified; + db.SaveChanges(); + } + } + } + } + } + + if (string.IsNullOrEmpty(result.Message)) + { + result.Success = true; + } + + return result; + } + + private Installation CreateSite(InstallConfig install) + { + var result = new Installation { Success = false, Message = string.Empty }; + + if (!string.IsNullOrEmpty(install.TenantName) && !string.IsNullOrEmpty(install.Aliases) && !string.IsNullOrEmpty(install.SiteName)) + { + using (var scope = _serviceScopeFactory.CreateScope()) + { + // use the SiteState to set the Alias explicitly so the tenant can be resolved + var aliases = scope.ServiceProvider.GetRequiredService(); + string firstalias = install.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0]; + var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstalias); + var siteState = scope.ServiceProvider.GetRequiredService(); + siteState.Alias = alias; + + var sites = scope.ServiceProvider.GetRequiredService(); + var site = sites.GetSites().FirstOrDefault(item => item.Name == install.SiteName); + if (site == null) + { + var tenants = scope.ServiceProvider.GetRequiredService(); + var users = scope.ServiceProvider.GetRequiredService(); + var roles = scope.ServiceProvider.GetRequiredService(); + var userroles = scope.ServiceProvider.GetRequiredService(); + var folders = scope.ServiceProvider.GetRequiredService(); + var log = scope.ServiceProvider.GetRequiredService(); + var identityUserManager = scope.ServiceProvider.GetRequiredService>(); + + var tenant = tenants.GetTenants().FirstOrDefault(item => item.Name == install.TenantName); + + site = new Site + { + TenantId = tenant.TenantId, + Name = install.SiteName, + LogoFileId = null, + DefaultThemeType = install.DefaultTheme, + DefaultLayoutType = install.DefaultLayout, + DefaultContainerType = install.DefaultContainer, + SiteTemplateType = install.SiteTemplate + }; + site = sites.AddSite(site); + + IdentityUser identityUser = identityUserManager.FindByNameAsync(Constants.HostUser).GetAwaiter().GetResult(); + if (identityUser == null) + { + identityUser = new IdentityUser { UserName = Constants.HostUser, Email = install.HostEmail, EmailConfirmed = true }; + var create = identityUserManager.CreateAsync(identityUser, install.HostPassword).GetAwaiter().GetResult(); + if (create.Succeeded) + { + var user = new User + { + SiteId = site.SiteId, + Username = Constants.HostUser, + Password = install.HostPassword, + Email = install.HostEmail, + DisplayName = install.HostName, + LastIPAddress = "", + LastLoginOn = null + }; + + user = users.AddUser(user); + var hostRoleId = roles.GetRoles(user.SiteId, true).FirstOrDefault(item => item.Name == Constants.HostRole)?.RoleId ?? 0; + var userRole = new UserRole { UserId = user.UserId, RoleId = hostRoleId, EffectiveDate = null, ExpiryDate = null }; + userroles.AddUserRole(userRole); + + // add user folder + var folder = folders.GetFolder(user.SiteId, Utilities.PathCombine("Users", "\\")); + if (folder != null) + { + folders.AddFolder(new Folder + { + SiteId = folder.SiteId, + ParentId = folder.FolderId, + Name = "My Folder", + Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), "\\"), + Order = 1, + IsSystem = true, + Permissions = new List + { + new Permission(PermissionNames.Browse, user.UserId, true), + new Permission(PermissionNames.View, Constants.AllUsersRole, true), + new Permission(PermissionNames.Edit, user.UserId, true), + }.EncodePermissions(), + }); + } + } + } + + foreach (string aliasname in install.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + { + alias = aliases.GetAliases().FirstOrDefault(item => item.Name == aliasname); + alias.SiteId = site.SiteId; + aliases.UpdateAlias(alias); + } + + log.Log(site.SiteId, LogLevel.Trace, this, LogFunction.Create, "Site Created {Site}", site); + } + } + } + + result.Success = true; + + return result; + } + + private void CreateApplicationVersion(string connectionString) + { + using (var db = new InstallationContext(NormalizeConnectionString(connectionString))) + { + var version = db.ApplicationVersion.FirstOrDefault(item => item.Version == Constants.Version); + if (version == null) + { + version = new ApplicationVersion { Version = Constants.Version, CreatedOn = DateTime.UtcNow }; db.ApplicationVersion.Add(version); db.SaveChanges(); } } } - - private static void TenantMigration(string connectionString, string dataDirectory) + + private string NormalizeConnectionString(string connectionString) { - Console.WriteLine("Tenant migration"); - var assemblies = AppDomain.CurrentDomain.GetAssemblies() - .Where(item => item.FullName != null && item.FullName.ToLower().Contains(".module.")).ToArray(); + var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); + connectionString = connectionString.Replace("|DataDirectory|", dataDirectory); + return connectionString; + } - // get tenants - using (var db = new InstallationContext(connectionString)) + private string DenormalizeConnectionString(string connectionString) + { + var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); + connectionString = connectionString.Replace(dataDirectory, "|DataDirectory|"); + return connectionString; + } + + public void UpdateConnectionString(string connectionString) + { + connectionString = DenormalizeConnectionString(connectionString); + if (_config.GetConnectionString(SettingKeys.ConnectionStringKey) != connectionString) { - foreach (var tenant in db.Tenant.ToList()) - { - Console.WriteLine($"Migrating tenant {tenant.Name}"); - connectionString = NormalizeConnectionString(tenant.DBConnectionString, dataDirectory); - // upgrade framework - var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant")); - var dbUpgrade = dbUpgradeConfig.Build(); - if (dbUpgrade.IsUpgradeRequired()) - { - var result = dbUpgrade.PerformUpgrade(); - if (!result.Successful) - { - // TODO: log result.Error.Message - problem is logger is not available here - } - } - - // iterate through Oqtane module assemblies and execute any database scripts - foreach (var assembly in assemblies) ModuleMigration(assembly, connectionString); - } + AddOrUpdateAppSetting($"ConnectionStrings:{SettingKeys.ConnectionStringKey}", connectionString); + _config.Reload(); } } - public static void UpdateConnectionStringSetting(string connectionString) - { - AddOrUpdateAppSetting($"ConnectionStrings:{SettingKeys.ConnectionStringKey}", connectionString); - } - - public static void AddOrUpdateAppSetting(string sectionPathKey, T value) + public void AddOrUpdateAppSetting(string sectionPathKey, T value) { try { @@ -296,7 +527,7 @@ namespace Oqtane.Infrastructure } } - private static void SetValueRecursively(string sectionPathKey, dynamic jsonObj, T value) + private void SetValueRecursively(string sectionPathKey, dynamic jsonObj, T value) { // split the string at the first ':' character var remainingSections = sectionPathKey.Split(":", 2); @@ -315,51 +546,6 @@ namespace Oqtane.Infrastructure } } - public void BuildDefaultSite(string password, string email) - { - using (var scope = _serviceScopeFactory.CreateScope()) - { - //Gather required services - var siteRepository = scope.ServiceProvider.GetRequiredService(); - - // Build default site only if no site present - if (siteRepository.GetSites().Any()) return; - - var users = scope.ServiceProvider.GetRequiredService(); - var roles = scope.ServiceProvider.GetRequiredService(); - var userRoles = scope.ServiceProvider.GetRequiredService(); - var folders = scope.ServiceProvider.GetRequiredService(); - var identityUserManager = scope.ServiceProvider.GetRequiredService>(); - var tenants = scope.ServiceProvider.GetRequiredService(); - - var tenant = tenants.GetTenants().First(); - - var site = new Site - { - TenantId = tenant.TenantId, - Name = "Default Site", - LogoFileId = null, - DefaultThemeType = GetInstallationConfig(SettingKeys.DefaultThemeKey, Constants.DefaultTheme), - DefaultLayoutType = GetInstallationConfig(SettingKeys.DefaultLayoutKey, Constants.DefaultLayout), - DefaultContainerType = GetInstallationConfig(SettingKeys.DefaultContainerKey, Constants.DefaultContainer), - SiteTemplateType = GetInstallationConfig(SettingKeys.SiteTemplateKey, Constants.DefaultSiteTemplate), - }; - site = siteRepository.AddSite(site); - - var user = new User - { - SiteId = site.SiteId, - Username = Constants.HostUser, - Password = password, - Email = email, - DisplayName = Constants.HostUser - }; - CreateHostUser(folders, userRoles, roles, users, identityUserManager, user); - tenant.IsInitialized = true; - tenants.UpdateTenant(tenant); - } - } - private string GetInstallationConfig(string key, string defaultValue) { var value = _config.GetSection(SettingKeys.InstallationSection).GetValue(key, defaultValue); @@ -368,96 +554,5 @@ namespace Oqtane.Infrastructure return value; } - private static void CreateHostUser(IFolderRepository folderRepository, IUserRoleRepository userRoleRepository, IRoleRepository roleRepository, IUserRepository userRepository, UserManager identityUserManager, User user) - { - var identityUser = new IdentityUser {UserName = user.Username, Email = user.Email, EmailConfirmed = true}; - var result = identityUserManager.CreateAsync(identityUser, user.Password).GetAwaiter().GetResult(); - - if (result.Succeeded) - { - user.LastLoginOn = null; - user.LastIPAddress = ""; - var newUser = userRepository.AddUser(user); - - // assign to host role if this is the host user ( initial installation ) - if (user.Username == Constants.HostUser) - { - var hostRoleId = roleRepository.GetRoles(user.SiteId, true).FirstOrDefault(item => item.Name == Constants.HostRole)?.RoleId ?? 0; - var userRole = new UserRole {UserId = newUser.UserId, RoleId = hostRoleId, EffectiveDate = null, ExpiryDate = null}; - userRoleRepository.AddUserRole(userRole); - } - - // add folder for user - var folder = folderRepository.GetFolder(user.SiteId, Utilities.PathCombine("Users","\\")); - if (folder != null) - folderRepository.AddFolder(new Folder - { - SiteId = folder.SiteId, - ParentId = folder.FolderId, - Name = "My Folder", - Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),"\\"), - Order = 1, - IsSystem = true, - Permissions = new List - { - new Permission(PermissionNames.Browse, newUser.UserId, true), - new Permission(PermissionNames.View, Constants.AllUsersRole, true), - new Permission(PermissionNames.Edit, newUser.UserId, true), - }.EncodePermissions(), - }); - } - } - - private static bool IsDefaultSiteInstalled(string connectionString) - { - using (var db = new InstallationContext(connectionString)) - { - return db.Tenant.Any(t => t.IsInitialized); - } - } - - private static bool IsMasterInstalled(string connectionString) - { - using (var db = new InstallationContext(connectionString)) - { - - //check if DbUp was initialized - return TableExists(db, "SchemaVersions"); - } - } - - public static bool TableExists(DbContext context, string tableName) - { - return TableExists(context, "dbo", tableName); - } - - public static bool TableExists(DbContext context, string schema, string tableName) - { - if (!context.Database.CanConnect()) return false; - var connection = context.Database.GetDbConnection(); - - if (connection.State.Equals(ConnectionState.Closed)) - connection.Open(); - - using (var command = connection.CreateCommand()) - { - command.CommandText = @" - SELECT 1 FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = @Schema - AND TABLE_NAME = @TableName"; - - var schemaParam = command.CreateParameter(); - schemaParam.ParameterName = "@Schema"; - schemaParam.Value = schema; - command.Parameters.Add(schemaParam); - - var tableNameParam = command.CreateParameter(); - tableNameParam.ParameterName = "@TableName"; - tableNameParam.Value = tableName; - command.Parameters.Add(tableNameParam); - - return command.ExecuteScalar() != null; - } - } } } diff --git a/Oqtane.Server/Infrastructure/Interfaces/IDatabaseManager.cs b/Oqtane.Server/Infrastructure/Interfaces/IDatabaseManager.cs new file mode 100644 index 00000000..ffb0ff9c --- /dev/null +++ b/Oqtane.Server/Infrastructure/Interfaces/IDatabaseManager.cs @@ -0,0 +1,12 @@ +using Oqtane.Models; +using Oqtane.Shared; + +namespace Oqtane.Infrastructure +{ + public interface IDatabaseManager + { + bool IsInstalled(); + Installation Install(); + Installation Install(InstallConfig install); + } +} diff --git a/Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs b/Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs index c71de4f6..baef7184 100644 --- a/Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs +++ b/Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs @@ -1,8 +1,10 @@ -namespace Oqtane.Infrastructure +using Oqtane.Models; + +namespace Oqtane.Infrastructure { public interface IInstallable { - bool Install(string version); - bool Uninstall(); + bool Install(Tenant tenant, string version); + bool Uninstall(Tenant tenant); } } diff --git a/Oqtane.Server/Infrastructure/LogManager.cs b/Oqtane.Server/Infrastructure/LogManager.cs index b46bc8dc..52a4736a 100644 --- a/Oqtane.Server/Infrastructure/LogManager.cs +++ b/Oqtane.Server/Infrastructure/LogManager.cs @@ -69,10 +69,14 @@ namespace Oqtane.Infrastructure { log.UserId = user.UserId; } - HttpRequest request = _accessor.HttpContext.Request; - if (request != null) + log.Url = ""; + if (_accessor.HttpContext != null) { - log.Url = $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}"; + HttpRequest request = _accessor.HttpContext.Request; + if (request != null) + { + log.Url = $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}"; + } } Type type = Type.GetType(@class.ToString()); diff --git a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs index 4d38e250..7e068076 100644 --- a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs +++ b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs @@ -18,14 +18,14 @@ namespace Oqtane.Modules.HtmlText.Manager _sql = sql; } - public bool Install(string version) + public bool Install(Tenant tenant, string version) { - return _sql.ExecuteEmbeddedScript(GetType().Assembly, "HtmlText." + version + ".sql"); + return _sql.ExecuteScript(tenant, GetType().Assembly, "HtmlText." + version + ".sql"); } - public bool Uninstall() + public bool Uninstall(Tenant tenant) { - return _sql.ExecuteEmbeddedScript(GetType().Assembly, "HtmlText.Uninstall.sql"); + return _sql.ExecuteScript(tenant, GetType().Assembly, "HtmlText.Uninstall.sql"); } public string ExportModule(Module module) diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 95471ca0..347e9fc0 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -17,11 +17,6 @@ Oqtane - - - - - diff --git a/Oqtane.Server/Program.cs b/Oqtane.Server/Program.cs index 08d1fa06..c4b5492f 100644 --- a/Oqtane.Server/Program.cs +++ b/Oqtane.Server/Program.cs @@ -12,11 +12,10 @@ namespace Oqtane.Server public static void Main(string[] args) { var host = BuildWebHost(args); - // execute any database migrations for the framework or extensions using (var serviceScope = host.Services.GetRequiredService().CreateScope()) { - var databaseManager = serviceScope.ServiceProvider.GetService(); - databaseManager.StartupMigration(); + var databaseManager = serviceScope.ServiceProvider.GetService(); + databaseManager.Install(); } host.Run(); } diff --git a/Oqtane.Server/Repository/Context/InstallationContext.cs b/Oqtane.Server/Repository/Context/InstallationContext.cs index 7b971a16..18694939 100644 --- a/Oqtane.Server/Repository/Context/InstallationContext.cs +++ b/Oqtane.Server/Repository/Context/InstallationContext.cs @@ -17,7 +17,11 @@ namespace Oqtane.Repository protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlServer(_connectionString); - public virtual DbSet ApplicationVersion { get; set; } + public virtual DbSet Alias { get; set; } public virtual DbSet Tenant { get; set; } + public virtual DbSet ModuleDefinition { get; set; } + public virtual DbSet Job { get; set; } + + public virtual DbSet ApplicationVersion { get; set; } } } diff --git a/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs index 32c59549..dd516461 100644 --- a/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs @@ -5,6 +5,7 @@ namespace Oqtane.Repository { public interface IModuleDefinitionRepository { + IEnumerable GetModuleDefinitions(); IEnumerable GetModuleDefinitions(int sideId); ModuleDefinition GetModuleDefinition(int moduleDefinitionId, int sideId); void UpdateModuleDefinition(ModuleDefinition moduleDefinition); diff --git a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs index 037e9416..a1402e3b 100644 --- a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs @@ -6,8 +6,8 @@ namespace Oqtane.Repository { public interface ISqlRepository { - bool ExecuteEmbeddedScript(Assembly assembly, string script); void ExecuteScript(Tenant tenant, string script); + bool ExecuteScript(Tenant tenant, Assembly assembly, string filename); int ExecuteNonQuery(Tenant tenant, string query); SqlDataReader ExecuteReader(Tenant tenant, string query); } diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index 6db44ac0..117c7ae7 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -25,6 +25,11 @@ namespace Oqtane.Repository _permissions = permissions; } + public IEnumerable GetModuleDefinitions() + { + return LoadModuleDefinitions(-1); // used only during startup + } + public IEnumerable GetModuleDefinitions(int siteId) { return LoadModuleDefinitions(siteId); @@ -72,8 +77,12 @@ namespace Oqtane.Repository } List moduleDefinitions = _moduleDefinitions; - // get module definition permissions for site - List permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList(); + List permissions = new List(); + if (siteId != -1) + { + // get module definition permissions for site + permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList(); + } // get module definitions in database List moduledefs = _db.ModuleDefinition.ToList(); @@ -88,7 +97,10 @@ namespace Oqtane.Repository moduledef = new ModuleDefinition { ModuleDefinitionName = moduledefinition.ModuleDefinitionName }; _db.ModuleDefinition.Add(moduledef); _db.SaveChanges(); - _permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions); + if (siteId != -1) + { + _permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions); + } } else { @@ -109,13 +121,16 @@ namespace Oqtane.Repository { moduledefinition.Version = moduledef.Version; } - if (permissions.Count == 0) + if (siteId != -1) { - _permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions); - } - else - { - moduledefinition.Permissions = permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).EncodePermissions(); + if (permissions.Count == 0) + { + _permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions); + } + else + { + moduledefinition.Permissions = permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).EncodePermissions(); + } } // remove module definition from list as it is already synced moduledefs.Remove(moduledef); @@ -131,7 +146,10 @@ namespace Oqtane.Repository // any remaining module definitions are orphans foreach (ModuleDefinition moduledefinition in moduledefs) { - _permissions.DeletePermissions(siteId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId); + if (siteId != -1) + { + _permissions.DeletePermissions(siteId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId); + } _db.ModuleDefinition.Remove(moduledefinition); // delete _db.SaveChanges(); } diff --git a/Oqtane.Server/Repository/ModuleRepository.cs b/Oqtane.Server/Repository/ModuleRepository.cs index 7f38a1bf..a66517fc 100644 --- a/Oqtane.Server/Repository/ModuleRepository.cs +++ b/Oqtane.Server/Repository/ModuleRepository.cs @@ -87,8 +87,15 @@ namespace Oqtane.Repository Type moduletype = Type.GetType(moduledefinition.ServerManagerType); if (moduletype != null && moduletype.GetInterface("IPortable") != null) { - var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); - modulecontent.Content = ((IPortable) moduleobject).ExportModule(module); + try + { + var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); + modulecontent.Content = ((IPortable)moduleobject).ExportModule(module); + } + catch + { + // error in IPortable implementation + } } } @@ -124,9 +131,16 @@ namespace Oqtane.Repository Type moduletype = Type.GetType(moduledefinition.ServerManagerType); if (moduletype != null && moduletype.GetInterface("IPortable") != null) { - var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); - ((IPortable) moduleobject).ImportModule(module, modulecontent.Content, modulecontent.Version); - success = true; + try + { + var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); + ((IPortable)moduleobject).ImportModule(module, modulecontent.Content, modulecontent.Version); + success = true; + } + catch + { + // error in IPortable implementation + } } } } diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 91cbccbf..0e39a6bf 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -784,14 +784,14 @@ namespace Oqtane.Repository Type moduletype = Type.GetType(moduledefinition.ServerManagerType); if (moduletype != null && moduletype.GetInterface("IPortable") != null) { - var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); try { + var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); ((IPortable)moduleobject).ImportModule(module, pagetemplatemodule.Content, moduledefinition.Version); } catch { - // error in module import + // error in IPortable implementation } } } diff --git a/Oqtane.Server/Repository/SqlRepository.cs b/Oqtane.Server/Repository/SqlRepository.cs index 0e5c2f3c..fcc4caf3 100644 --- a/Oqtane.Server/Repository/SqlRepository.cs +++ b/Oqtane.Server/Repository/SqlRepository.cs @@ -10,18 +10,21 @@ namespace Oqtane.Repository { public class SqlRepository : ISqlRepository { - private readonly ITenantRepository _tenants; - public SqlRepository(ITenantRepository tenants) + public void ExecuteScript(Tenant tenant, string script) { - _tenants = tenants; + // execute script in curent tenant + foreach (string query in script.Split("GO", StringSplitOptions.RemoveEmptyEntries)) + { + ExecuteNonQuery(tenant, query); + } } - public bool ExecuteEmbeddedScript(Assembly assembly, string filename) + public bool ExecuteScript(Tenant tenant, Assembly assembly, string filename) { // script must be included as an Embedded Resource within an assembly bool success = true; - string uninstallScript = ""; + string script = ""; if (assembly != null) { @@ -33,39 +36,27 @@ namespace Oqtane.Repository { using (var reader = new StreamReader(resourceStream)) { - uninstallScript = reader.ReadToEnd(); + script = reader.ReadToEnd(); } } } } - if (!string.IsNullOrEmpty(uninstallScript)) + if (!string.IsNullOrEmpty(script)) { - foreach (Tenant tenant in _tenants.GetTenants()) + try { - try - { - ExecuteScript(tenant, uninstallScript); - } - catch - { - success = false; - } + ExecuteScript(tenant, script); + } + catch + { + success = false; } } return success; } - public void ExecuteScript(Tenant tenant, string script) - { - // execute script in curent tenant - foreach (string query in script.Split("GO", StringSplitOptions.RemoveEmptyEntries)) - { - ExecuteNonQuery(tenant, query); - } - } - public int ExecuteNonQuery(Tenant tenant, string query) { SqlConnection conn = new SqlConnection(FormatConnectionString(tenant.DBConnectionString)); diff --git a/Oqtane.Server/Repository/TenantResolver.cs b/Oqtane.Server/Repository/TenantResolver.cs index 8a5b85c4..f5035a65 100644 --- a/Oqtane.Server/Repository/TenantResolver.cs +++ b/Oqtane.Server/Repository/TenantResolver.cs @@ -17,47 +17,48 @@ namespace Oqtane.Repository int aliasId = -1; string aliasName = ""; - // get alias identifier based on request context - if (accessor.HttpContext != null) + if (siteState != null && siteState.Alias != null) { - // check if an alias is passed as a querystring parameter ( for cross tenant access ) - if (accessor.HttpContext.Request.Query.ContainsKey("aliasid")) - { - aliasId = int.Parse(accessor.HttpContext.Request.Query["aliasid"]); - } - else // get the alias from the request url - { - aliasName = accessor.HttpContext.Request.Host.Value; - string path = accessor.HttpContext.Request.Path.Value; - string[] segments = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries); - if (segments.Length > 1 && segments[1] == "api" && segments[0] != "~") - { - aliasName += "/" + segments[0]; - } - - if (aliasName.EndsWith("/")) - { - aliasName = aliasName.Substring(0, aliasName.Length - 1); - } - } - } - else // background processes can pass in an alias using the SiteState service - { - aliasId = siteState?.Alias?.AliasId ?? -1; - } - - // get the alias and tenant - IEnumerable aliases = aliasRepository.GetAliases().ToList(); // cached - if (aliasId != -1) - { - _alias = aliases.FirstOrDefault(item => item.AliasId == aliasId); + // background processes can pass in an alias using the SiteState service + _alias = siteState.Alias; } else - { - - _alias = aliases.FirstOrDefault(item => item.Name == aliasName - //if here is only one alias and other methods fail, take it (case of startup install) - || aliases.Count() == 1); + { + // get alias identifier based on request context + if (accessor.HttpContext != null) + { + // check if an alias is passed as a querystring parameter ( for cross tenant access ) + if (accessor.HttpContext.Request.Query.ContainsKey("aliasid")) + { + aliasId = int.Parse(accessor.HttpContext.Request.Query["aliasid"]); + } + else // get the alias from the request url + { + aliasName = accessor.HttpContext.Request.Host.Value; + string path = accessor.HttpContext.Request.Path.Value; + string[] segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + if (segments.Length > 1 && segments[1] == "api" && segments[0] != "~") + { + aliasName += "/" + segments[0]; + } + + if (aliasName.EndsWith("/")) + { + aliasName = aliasName.Substring(0, aliasName.Length - 1); + } + } + } + + // get the alias + IEnumerable aliases = aliasRepository.GetAliases().ToList(); // cached + if (aliasId != -1) + { + _alias = aliases.FirstOrDefault(item => item.AliasId == aliasId); + } + else + { + _alias = aliases.FirstOrDefault(item => item.Name == aliasName || aliases.Count() == 1); + } } if (_alias != null) diff --git a/Oqtane.Server/Scripts/Master.00.00.00.sql b/Oqtane.Server/Scripts/Master.00.00.00.sql index 0b10fb01..fcfdd3ef 100644 --- a/Oqtane.Server/Scripts/Master.00.00.00.sql +++ b/Oqtane.Server/Scripts/Master.00.00.00.sql @@ -7,8 +7,6 @@ CREATE TABLE [dbo].[Tenant]( [TenantId] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](100) NOT NULL, [DBConnectionString] [nvarchar](1024) NOT NULL, - [DBSchema] [nvarchar](50) NOT NULL, - [IsInitialized] [bit] NOT NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, @@ -119,32 +117,4 @@ REFERENCES [dbo].[Job] ([JobId]) ON DELETE CASCADE GO -/* - -Create seed data - -*/ -SET IDENTITY_INSERT [dbo].[Tenant] ON -GO -INSERT [dbo].[Tenant] ([TenantId], [Name], [DBConnectionString], [DBSchema], [IsInitialized], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (1, N'Master', N'$ConnectionString$', N'', 0, '', getdate(), '', getdate()) -GO -SET IDENTITY_INSERT [dbo].[Tenant] OFF -GO - -SET IDENTITY_INSERT [dbo].[Alias] ON -GO -INSERT [dbo].[Alias] ([AliasId], [Name], [TenantId], [SiteId], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (1, N'$Alias$', 1, 1, '', getdate(), '', getdate()) -GO -SET IDENTITY_INSERT [dbo].[Alias] OFF -GO - -SET IDENTITY_INSERT [dbo].[Job] ON -GO -INSERT [dbo].[Job] ([JobId], [Name], [JobType], [Frequency], [Interval], [StartDate], [EndDate], [IsEnabled], [IsStarted], [IsExecuting], [NextExecution], [RetentionHistory], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (1, N'Notification Job', N'Oqtane.Infrastructure.NotificationJob, Oqtane.Server', N'm', 1, null, null, 0, 0, 0, null, 10, '', getdate(), '', getdate()) -GO -SET IDENTITY_INSERT [dbo].[Job] OFF -GO diff --git a/Oqtane.Server/Scripts/Master.00.00.01.sql b/Oqtane.Server/Scripts/Master.00.00.01.sql index 02943e96..49ec882c 100644 --- a/Oqtane.Server/Scripts/Master.00.00.01.sql +++ b/Oqtane.Server/Scripts/Master.00.00.01.sql @@ -1,2 +1,5 @@ -alter table Tenant drop column DBSchema -go +/* + +schema updates + +*/ diff --git a/Oqtane.Server/Security/UserPermissions.cs b/Oqtane.Server/Security/UserPermissions.cs index 14a13199..1e89c4cd 100644 --- a/Oqtane.Server/Security/UserPermissions.cs +++ b/Oqtane.Server/Security/UserPermissions.cs @@ -55,7 +55,14 @@ namespace Oqtane.Security public User GetUser() { - return GetUser(_accessor.HttpContext.User); + if (_accessor.HttpContext != null) + { + return GetUser(_accessor.HttpContext.User); + } + else + { + return null; + } } } } diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 52599d6e..9e24a6fa 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -155,7 +155,7 @@ namespace Oqtane services.AddSingleton(Configuration); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); // install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain ) InstallationManager.UnpackPackages("Modules,Themes", _webRoot); diff --git a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css index 114c2584..c69fc61d 100644 --- a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css +++ b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css @@ -16,7 +16,6 @@ .main .top-row { background-color: #e6e6e6; border-bottom: 1px solid #d6d5d5; - z-index: 9999; } .sidebar { @@ -42,6 +41,10 @@ margin-bottom: 0; } +.app-controlpanel { + z-index: 9999; +} + .app-menu .nav-item { font-size: 0.9rem; padding-bottom: 0.5rem; @@ -97,19 +100,21 @@ position: fixed; left: 275px; top: 0; - z-index: 1; + z-index: 3 } - + .sidebar { width: 250px; height: 100vh; position: sticky; top: 0; + z-index: 1 } .main .top-row { position: sticky; top: 0; + z-index: 2 } .main > div { @@ -154,10 +159,23 @@ } @media (max-width: 767px) { + .breadcrumbs { + position: fixed; + top: 150px; + width: 100%; + left: 0; + z-index: 1; + } + .sidebar { margin-top: 3.5rem; position: fixed; width: 100%; + z-index: 2; + } + + .main .top-row { + z-index: 2; } .main > .top-row.px-4 { @@ -172,13 +190,6 @@ margin-left: auto; } - .breadcrumbs { - position: fixed; - top: 150px; - width: 100%; - left: 0; - } - .main > .container { margin-top: 200px; } diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index ec289bbe..aa0529a0 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -48,7 +48,7 @@ window.interop = { } } }, - includeLink: function (id, rel, url, type) { + includeLink: function (id, rel, url, type, integrity, crossorigin) { var link; if (id !== "") { link = document.getElementById(id); @@ -66,6 +66,12 @@ window.interop = { if (type !== "") { link.type = type; } + if (integrity !== "") { + link.integrity = integrity; + } + if (crossorigin !== "") { + link.crossorigin = crossorigin; + } document.head.appendChild(link); } else { @@ -78,9 +84,15 @@ window.interop = { if (type !== "" && link.type !== type) { link.setAttribute('type', type); } + if (integrity !== "" && link.integrity !== integrity) { + link.setAttribute('integrity', integrity); + } + if (crossorigin !== "" && link.crossorigin !== crossorigin) { + link.setAttribute('crossorigin', crossorigin); + } } }, - includeScript: function (id, src, content, location) { + includeScript: function (id, src, content, location, integrity, crossorigin) { var script; if (id !== "") { script = document.getElementById(id); @@ -92,6 +104,12 @@ window.interop = { } if (src !== "") { script.src = src; + if (integrity !== "") { + script.integrity = integrity; + } + if (crossorigin !== "") { + script.crossorigin = crossorigin; + } } else { script.innerHTML = content; @@ -108,6 +126,12 @@ window.interop = { if (script.src !== src) { script.src = src; } + if (integrity !== "" && script.integrity !== integrity) { + script.setAttribute('integrity', integrity); + } + if (crossorigin !== "" && script.crossorigin !== crossorigin) { + script.setAttribute('crossorigin', crossorigin); + } } else { if (script.innerHTML !== content) { diff --git a/Oqtane.Shared/Models/Tenant.cs b/Oqtane.Shared/Models/Tenant.cs index 3f0a518a..ed392544 100644 --- a/Oqtane.Shared/Models/Tenant.cs +++ b/Oqtane.Shared/Models/Tenant.cs @@ -7,7 +7,6 @@ namespace Oqtane.Models public int TenantId { get; set; } public string Name { get; set; } public string DBConnectionString { get; set; } - public bool IsInitialized { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } public string ModifiedBy { get; set; } diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index eacaa4c6..f4edd1ce 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -33,6 +33,7 @@ public const string HostUser = "host"; public const string MasterTenant = "Master"; + public const string DefaultSite = "Default Site"; public const string AllUsersRole = "All Users"; public const string HostRole = "Host Users"; diff --git a/Oqtane.Shared/Shared/InstallConfig.cs b/Oqtane.Shared/Shared/InstallConfig.cs index f7f065b4..2ab74c74 100644 --- a/Oqtane.Shared/Shared/InstallConfig.cs +++ b/Oqtane.Shared/Shared/InstallConfig.cs @@ -2,11 +2,17 @@ { public class InstallConfig { - public string Alias { get; set; } public string ConnectionString { get; set; } - public string HostUser { get; set; } - public string Password { get; set; } + public string Aliases { get; set; } + public string TenantName { get; set; } + public bool IsNewTenant { get; set; } + public string SiteName { get; set; } + public string HostPassword { get; set; } public string HostEmail { get; set; } - public bool IsMaster { get; set; } + public string HostName { get; set; } + public string SiteTemplate { get; set; } + public string DefaultTheme { get; set; } + public string DefaultLayout { get; set; } + public string DefaultContainer { get; set; } } } From 573c6235923878e9b483e934185b3928692183b0 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 30 Apr 2020 14:36:30 -0400 Subject: [PATCH 187/265] fixes to module templates --- .../ModuleCreator/Templates/External/Client/Index.razor | 4 ++-- .../Server/[Owner].[Module]s.Module.Server.csproj | 9 ++------- .../Internal/Oqtane.Client/Modules/[Module]/Index.razor | 4 ++-- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor index 68c31a34..15d660be 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor @@ -62,8 +62,8 @@ else - Repository\I[Module]Repository.cs - interface for defining repository methods
    - Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core
    - Repository\[Module]Context.cs - provides a DB Context for data access
    -- Scripts\[Module].1.0.0.sql - database schema definition script

    -- Scripts\[Module].Uninstall.sql - database uninstall script

    +- Scripts\[Owner].[Module].1.0.0.sql - database schema definition script

    +- Scripts\[Owner].[Module].Uninstall.sql - database uninstall script

    [RootPath]Shared\
    - [Owner].[Module]s.Module.Shared.csproj - shared project
    - Models\[Module].cs - model definition

    diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj index d9ec6aee..02a34494 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj @@ -13,13 +13,8 @@ - - - - - - - + + diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor index 15cf068b..2c9f1764 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor @@ -54,8 +54,8 @@ else - Repository\I[Module]Repository.cs - interface for defining repository methods
    - Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core
    - Repository\[Module]Context.cs - provides a DB Context for data access
    -- Scripts\[Module].1.0.0.sql - database schema definition script

    -- Scripts\[Module].Uninstall.sql - database uninstall script

    +- Scripts\[Owner].[Module].1.0.0.sql - database schema definition script

    +- Scripts\[Owner].[Module].Uninstall.sql - database uninstall script

    [RootPath]Oqtane.Shared\Modules\[Module]\
    - Models\[Module].cs - model definition

    From d387e6e57370c58706701a673d5be5afbfc3b793 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Fri, 1 May 2020 11:24:10 +0200 Subject: [PATCH 188/265] Page editor fix Script check --- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 6 +++--- Oqtane.Server/Infrastructure/DatabaseManager.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 056cc9bf..9be6c14b 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -359,7 +359,7 @@ Page page = null; try { - if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype))) + if (_name != string.Empty) { page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); string currentPath = page.Path; @@ -375,7 +375,7 @@ { _path = _path.Substring(_path.LastIndexOf("/") + 1); } - if (string.IsNullOrEmpty(_parentid)) + if (string.IsNullOrEmpty(_parentid) || _parentid == "-1") { page.ParentId = null; page.Path = Utilities.GetFriendlyUrl(_path); @@ -459,7 +459,7 @@ } else { - AddModuleMessage("You Must Provide Page Name And Theme", MessageType.Warning); + AddModuleMessage("You Must Provide Page Name", MessageType.Warning); } } catch (Exception ex) diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 7fd44c66..863a8ce4 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -198,7 +198,7 @@ namespace Oqtane.Infrastructure var upgradeConfig = DeployChanges .To .SqlDatabase(NormalizeConnectionString(install.ConnectionString)) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master.")); + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master.") && s.EndsWith(".sql",StringComparison.OrdinalIgnoreCase)); var upgrade = upgradeConfig.Build(); if (upgrade.IsUpgradeRequired()) @@ -282,7 +282,7 @@ namespace Oqtane.Infrastructure foreach (var tenant in db.Tenant.ToList()) { var upgradeConfig = DeployChanges.To.SqlDatabase(NormalizeConnectionString(tenant.DBConnectionString)) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant")); + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant") && s.EndsWith(".sql",StringComparison.OrdinalIgnoreCase)); var upgrade = upgradeConfig.Build(); if (upgrade.IsUpgradeRequired()) From 7c6dc6d774f62545a68a58be6b76ed3fdd2104b9 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 1 May 2020 10:27:14 -0400 Subject: [PATCH 189/265] Added ability to execute version specific code during framework upgrade (removed ApplicationVersion table and replaced with Version field on Tenant table), updated version number to 0.9.0 and renamed install scripts to match - this will be a baseline release which will be upgradeable --- Oqtane.Client/Oqtane.Client.csproj | 2 +- .../Services/Interfaces/ISettingService.cs | 4 +- Oqtane.Client/Services/SettingService.cs | 8 +- Oqtane.Package/Oqtane.Framework.nuspec | 2 +- .../Controllers/SettingController.cs | 2 +- .../Infrastructure/DatabaseManager.cs | 65 +++++++------ .../Interfaces/IUpgradeManager.cs | 9 ++ .../Infrastructure/UpgradeManager.cs | 93 +++++++++++++++++++ Oqtane.Server/Oqtane.Server.csproj | 8 +- .../Repository/Context/InstallationContext.cs | 2 - .../Repository/Interfaces/ISiteRepository.cs | 1 + Oqtane.Server/Repository/SiteRepository.cs | 2 +- .../{Master.00.00.00.sql => Master.0.9.0.sql} | 12 +-- Oqtane.Server/Scripts/Master.00.00.01.sql | 5 - .../{Tenant.00.00.00.sql => Tenant.0.9.0.sql} | 69 ++++++++++++++ Oqtane.Server/Scripts/Tenant.00.00.01.sql | 68 -------------- Oqtane.Server/Startup.cs | 2 +- Oqtane.Shared/Models/ApplicationVersion.cs | 11 --- Oqtane.Shared/Models/Tenant.cs | 1 + Oqtane.Shared/Oqtane.Shared.csproj | 2 +- Oqtane.Shared/Shared/Constants.cs | 3 +- Oqtane.Shared/Shared/EntityNames.cs | 2 +- Oqtane.Upgrade/Oqtane.Upgrade.csproj | 2 +- 23 files changed, 230 insertions(+), 145 deletions(-) create mode 100644 Oqtane.Server/Infrastructure/Interfaces/IUpgradeManager.cs create mode 100644 Oqtane.Server/Infrastructure/UpgradeManager.cs rename Oqtane.Server/Scripts/{Master.00.00.00.sql => Master.0.9.0.sql} (90%) delete mode 100644 Oqtane.Server/Scripts/Master.00.00.01.sql rename Oqtane.Server/Scripts/{Tenant.00.00.00.sql => Tenant.0.9.0.sql} (86%) delete mode 100644 Oqtane.Server/Scripts/Tenant.00.00.01.sql delete mode 100644 Oqtane.Shared/Models/ApplicationVersion.cs diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index fbae9656..2e86a59a 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -6,7 +6,7 @@ 7.3 3.0 Debug;Release - 0.0.9 + 0.9.0 Oqtane Shaun Walker .NET Foundation diff --git a/Oqtane.Client/Services/Interfaces/ISettingService.cs b/Oqtane.Client/Services/Interfaces/ISettingService.cs index f7c06591..97c6266d 100644 --- a/Oqtane.Client/Services/Interfaces/ISettingService.cs +++ b/Oqtane.Client/Services/Interfaces/ISettingService.cs @@ -6,9 +6,9 @@ namespace Oqtane.Services { public interface ISettingService { - Task> GetHostSettingsAsync(); + Task> GetTenantSettingsAsync(); - Task UpdateHostSettingsAsync(Dictionary hostSettings); + Task UpdateTenantSettingsAsync(Dictionary tenantSettings); Task> GetSiteSettingsAsync(int siteId); diff --git a/Oqtane.Client/Services/SettingService.cs b/Oqtane.Client/Services/SettingService.cs index d966fb92..c1d96915 100644 --- a/Oqtane.Client/Services/SettingService.cs +++ b/Oqtane.Client/Services/SettingService.cs @@ -26,14 +26,14 @@ namespace Oqtane.Services get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "Setting"); } } - public async Task> GetHostSettingsAsync() + public async Task> GetTenantSettingsAsync() { - return await GetSettingsAsync(EntityNames.Host, -1); + return await GetSettingsAsync(EntityNames.Tenant, -1); } - public async Task UpdateHostSettingsAsync(Dictionary hostSettings) + public async Task UpdateTenantSettingsAsync(Dictionary tenantSettings) { - await UpdateSettingsAsync(hostSettings, EntityNames.Host, -1); + await UpdateSettingsAsync(tenantSettings, EntityNames.Tenant, -1); } public async Task> GetSiteSettingsAsync(int siteId) diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec index 20827508..b537a230 100644 --- a/Oqtane.Package/Oqtane.Framework.nuspec +++ b/Oqtane.Package/Oqtane.Framework.nuspec @@ -2,7 +2,7 @@ Oqtane.Framework - 0.0.9 + 0.9.0 Shaun Walker .NET Foundation Oqtane Framework diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index 2599fde7..44437344 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -123,7 +123,7 @@ namespace Oqtane.Controllers } switch (entityName) { - case EntityNames.Host: + case EntityNames.Tenant: authorized = User.IsInRole(Constants.HostRole); break; case EntityNames.Site: diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 863a8ce4..61b62589 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -26,7 +26,6 @@ namespace Oqtane.Infrastructure private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IMemoryCache _cache; - public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory, IMemoryCache cache) { _config = config; @@ -217,7 +216,6 @@ namespace Oqtane.Infrastructure if (result.Success) { - CreateApplicationVersion(install.ConnectionString); UpdateConnectionString(install.ConnectionString); } } @@ -277,21 +275,43 @@ namespace Oqtane.Infrastructure { var result = new Installation { Success = false, Message = string.Empty }; - using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) - { - foreach (var tenant in db.Tenant.ToList()) - { - var upgradeConfig = DeployChanges.To.SqlDatabase(NormalizeConnectionString(tenant.DBConnectionString)) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant") && s.EndsWith(".sql",StringComparison.OrdinalIgnoreCase)); + string[] versions = Constants.ReleaseVersions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - var upgrade = upgradeConfig.Build(); - if (upgrade.IsUpgradeRequired()) + using (var scope = _serviceScopeFactory.CreateScope()) + { + var upgrades = scope.ServiceProvider.GetRequiredService(); + + using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) + { + foreach (var tenant in db.Tenant.ToList()) { - var upgradeResult = upgrade.PerformUpgrade(); - result.Success = upgradeResult.Successful; - if (!result.Success) + var upgradeConfig = DeployChanges.To.SqlDatabase(NormalizeConnectionString(tenant.DBConnectionString)) + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant.") && s.EndsWith(".sql", StringComparison.OrdinalIgnoreCase)); + + var upgrade = upgradeConfig.Build(); + if (upgrade.IsUpgradeRequired()) { - result.Message = upgradeResult.Error.Message; + var upgradeResult = upgrade.PerformUpgrade(); + result.Success = upgradeResult.Successful; + if (!result.Success) + { + result.Message = upgradeResult.Error.Message; + } + } + + // execute any version specific upgrade logic + string version = tenant.Version; + int index = Array.FindIndex(versions, item => item == version); + if (index != (versions.Length - 1)) + { + if (index == -1) index = 0; + for (int i = index; i < versions.Length; i++) + { + upgrades.Upgrade(tenant, versions[i]); + } + tenant.Version = versions[versions.Length - 1]; + db.Entry(tenant).State = EntityState.Modified; + db.SaveChanges(); } } } @@ -460,6 +480,9 @@ namespace Oqtane.Infrastructure aliases.UpdateAlias(alias); } + tenant.Version = Constants.Version; + tenants.UpdateTenant(tenant); + log.Log(site.SiteId, LogLevel.Trace, this, LogFunction.Create, "Site Created {Site}", site); } } @@ -469,20 +492,6 @@ namespace Oqtane.Infrastructure return result; } - - private void CreateApplicationVersion(string connectionString) - { - using (var db = new InstallationContext(NormalizeConnectionString(connectionString))) - { - var version = db.ApplicationVersion.FirstOrDefault(item => item.Version == Constants.Version); - if (version == null) - { - version = new ApplicationVersion { Version = Constants.Version, CreatedOn = DateTime.UtcNow }; - db.ApplicationVersion.Add(version); - db.SaveChanges(); - } - } - } private string NormalizeConnectionString(string connectionString) { diff --git a/Oqtane.Server/Infrastructure/Interfaces/IUpgradeManager.cs b/Oqtane.Server/Infrastructure/Interfaces/IUpgradeManager.cs new file mode 100644 index 00000000..e941bbfd --- /dev/null +++ b/Oqtane.Server/Infrastructure/Interfaces/IUpgradeManager.cs @@ -0,0 +1,9 @@ +using Oqtane.Models; + +namespace Oqtane.Infrastructure +{ + public interface IUpgradeManager + { + void Upgrade(Tenant tenant, string version); + } +} diff --git a/Oqtane.Server/Infrastructure/UpgradeManager.cs b/Oqtane.Server/Infrastructure/UpgradeManager.cs new file mode 100644 index 00000000..2c538d97 --- /dev/null +++ b/Oqtane.Server/Infrastructure/UpgradeManager.cs @@ -0,0 +1,93 @@ +using Microsoft.Extensions.DependencyInjection; +using Oqtane.Extensions; +using Oqtane.Models; +using Oqtane.Repository; +using Oqtane.Shared; +using System.Collections.Generic; +using System.Linq; + +namespace Oqtane.Infrastructure +{ + public class UpgradeManager : IUpgradeManager + { + private readonly IAliasRepository _aliases; + private readonly IServiceScopeFactory _serviceScopeFactory; + + public UpgradeManager(IAliasRepository aliases, IServiceScopeFactory serviceScopeFactory) + { + _aliases = aliases; + _serviceScopeFactory = serviceScopeFactory; + } + + public void Upgrade(Tenant tenant, string version) + { + // core framework upgrade logic - note that you can check if current tenant is Master if you only want to execute logic once + var pageTemplates = new List(); + + switch (version) + { + case "0.9.0": + // add a page to all existing sites on upgrade + + //pageTemplates.Add(new PageTemplate + //{ + // Name = "Test", + // Parent = "", + // Path = "test", + // Icon = Icons.Badge, + // IsNavigation = true, + // IsPersonalizable = false, + // EditMode = false, + // PagePermissions = new List + // { + // new Permission(PermissionNames.View, Constants.AdminRole, true), + // new Permission(PermissionNames.View, Constants.AllUsersRole, true), + // new Permission(PermissionNames.Edit, Constants.AdminRole, true) + // }.EncodePermissions(), + // PageTemplateModules = new List + // { + // new PageTemplateModule + // { + // ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Login.Index).ToModuleDefinitionName(), Title = "Test", Pane = "Content", + // ModulePermissions = new List + // { + // new Permission(PermissionNames.View, Constants.AdminRole, true), + // new Permission(PermissionNames.View, Constants.AllUsersRole, true), + // new Permission(PermissionNames.Edit, Constants.AdminRole, true) + // }.EncodePermissions(), + // Content = "" + // } + // } + //}); + CreateSitePages(tenant, pageTemplates); + break; + } + } + + private void CreateSitePages(Tenant tenant, List pageTemplates) + { + if (pageTemplates.Count != 0) + { + var processed = new List(); + foreach (Alias alias in _aliases.GetAliases().Where(item => item.TenantId == tenant.TenantId)) + { + if (!processed.Exists(item => item.SiteId == alias.SiteId)) + { + using (var scope = _serviceScopeFactory.CreateScope()) + { + var siteState = scope.ServiceProvider.GetRequiredService(); + siteState.Alias = alias; + var sites = scope.ServiceProvider.GetRequiredService(); + var site = sites.GetSite(alias.SiteId); + if (site != null) + { + sites.CreatePages(site, pageTemplates); + } + processed.Add(site); + } + } + } + } + } + } +} diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 347e9fc0..43102d3c 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -4,7 +4,7 @@ netcoreapp3.1 7.3 Debug;Release - 0.0.9 + 0.9.0 Oqtane Shaun Walker .NET Foundation @@ -20,10 +20,8 @@ - - - - + + diff --git a/Oqtane.Server/Repository/Context/InstallationContext.cs b/Oqtane.Server/Repository/Context/InstallationContext.cs index 18694939..29bfc281 100644 --- a/Oqtane.Server/Repository/Context/InstallationContext.cs +++ b/Oqtane.Server/Repository/Context/InstallationContext.cs @@ -21,7 +21,5 @@ namespace Oqtane.Repository public virtual DbSet Tenant { get; set; } public virtual DbSet ModuleDefinition { get; set; } public virtual DbSet Job { get; set; } - - public virtual DbSet ApplicationVersion { get; set; } } } diff --git a/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs b/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs index 5e05dccf..c8cfec23 100644 --- a/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs @@ -10,5 +10,6 @@ namespace Oqtane.Repository Site UpdateSite(Site site); Site GetSite(int siteId); void DeleteSite(int siteId); + void CreatePages(Site site, List pageTemplates); } } diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 0e39a6bf..3dea955c 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -731,7 +731,7 @@ namespace Oqtane.Repository CreatePages(site, CreateAdminPages()); } - private void CreatePages(Site site, List pageTemplates) + public void CreatePages(Site site, List pageTemplates) { List moduledefinitions = _moduleDefinitionRepository.GetModuleDefinitions(site.SiteId).ToList(); foreach (PageTemplate pagetemplate in pageTemplates) diff --git a/Oqtane.Server/Scripts/Master.00.00.00.sql b/Oqtane.Server/Scripts/Master.0.9.0.sql similarity index 90% rename from Oqtane.Server/Scripts/Master.00.00.00.sql rename to Oqtane.Server/Scripts/Master.0.9.0.sql index fcfdd3ef..8f569dbb 100644 --- a/Oqtane.Server/Scripts/Master.00.00.00.sql +++ b/Oqtane.Server/Scripts/Master.0.9.0.sql @@ -7,6 +7,7 @@ CREATE TABLE [dbo].[Tenant]( [TenantId] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](100) NOT NULL, [DBConnectionString] [nvarchar](1024) NOT NULL, + [Version] [nvarchar](50) NULL, [CreatedBy] [nvarchar](256) NOT NULL, [CreatedOn] [datetime] NOT NULL, [ModifiedBy] [nvarchar](256) NOT NULL, @@ -91,17 +92,6 @@ CREATE TABLE [dbo].[JobLog] ( ) GO -CREATE TABLE [dbo].[ApplicationVersion]( - [ApplicationVersionId] [int] IDENTITY(1,1) NOT NULL, - [Version] [nvarchar](50) NOT NULL, - [CreatedOn] [datetime] NOT NULL - CONSTRAINT [PK_ApplicationVersion] PRIMARY KEY CLUSTERED - ( - [ApplicationVersionId] ASC - ) -) -GO - /* Create foreign key relationships diff --git a/Oqtane.Server/Scripts/Master.00.00.01.sql b/Oqtane.Server/Scripts/Master.00.00.01.sql deleted file mode 100644 index 49ec882c..00000000 --- a/Oqtane.Server/Scripts/Master.00.00.01.sql +++ /dev/null @@ -1,5 +0,0 @@ -/* - -schema updates - -*/ diff --git a/Oqtane.Server/Scripts/Tenant.00.00.00.sql b/Oqtane.Server/Scripts/Tenant.0.9.0.sql similarity index 86% rename from Oqtane.Server/Scripts/Tenant.00.00.00.sql rename to Oqtane.Server/Scripts/Tenant.0.9.0.sql index 1d0e540f..81a0df24 100644 --- a/Oqtane.Server/Scripts/Tenant.00.00.00.sql +++ b/Oqtane.Server/Scripts/Tenant.0.9.0.sql @@ -457,3 +457,72 @@ CREATE UNIQUE NONCLUSTERED INDEX IX_Folder ON [dbo].Folder [Path] ) ON [PRIMARY] GO + +/* + +ASP.NET Identity Minimal Schema + +*/ + +CREATE TABLE [dbo].[AspNetUsers]( + [Id] [nvarchar](450) NOT NULL, + [UserName] [nvarchar](256) NULL, + [NormalizedUserName] [nvarchar](256) NULL, + [Email] [nvarchar](256) NULL, + [NormalizedEmail] [nvarchar](256) NULL, + [EmailConfirmed] [bit] NOT NULL, + [PasswordHash] [nvarchar](max) NULL, + [SecurityStamp] [nvarchar](max) NULL, + [ConcurrencyStamp] [nvarchar](max) NULL, + [PhoneNumber] [nvarchar](max) NULL, + [PhoneNumberConfirmed] [bit] NOT NULL, + [TwoFactorEnabled] [bit] NOT NULL, + [LockoutEnd] [datetimeoffset](7) NULL, + [LockoutEnabled] [bit] NOT NULL, + [AccessFailedCount] [int] NOT NULL, + CONSTRAINT [PK_AspNetUsers] PRIMARY KEY CLUSTERED +( + [Id] ASC +) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE TABLE [dbo].[AspNetUserClaims]( + [Id] [int] IDENTITY(1,1) NOT NULL, + [UserId] [nvarchar](450) NOT NULL, + [ClaimType] [nvarchar](max) NULL, + [ClaimValue] [nvarchar](max) NULL, + CONSTRAINT [PK_AspNetUserClaims] PRIMARY KEY CLUSTERED +( + [Id] ASC +) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE NONCLUSTERED INDEX [IX_AspNetUserClaims_UserId] ON [dbo].[AspNetUserClaims] +( + [UserId] ASC +) ON [PRIMARY] +GO + +CREATE NONCLUSTERED INDEX [EmailIndex] ON [dbo].[AspNetUsers] +( + [NormalizedEmail] ASC +) ON [PRIMARY] +GO + +CREATE UNIQUE NONCLUSTERED INDEX [UserNameIndex] ON [dbo].[AspNetUsers] +( + [NormalizedUserName] ASC +) +WHERE ([NormalizedUserName] IS NOT NULL) +ON [PRIMARY] +GO + +ALTER TABLE [dbo].[AspNetUserClaims] WITH CHECK ADD CONSTRAINT [FK_AspNetUserClaims_AspNetUsers_UserId] FOREIGN KEY([UserId]) +REFERENCES [dbo].[AspNetUsers] ([Id]) +ON DELETE CASCADE +GO + +ALTER TABLE [dbo].[AspNetUserClaims] CHECK CONSTRAINT [FK_AspNetUserClaims_AspNetUsers_UserId] +GO diff --git a/Oqtane.Server/Scripts/Tenant.00.00.01.sql b/Oqtane.Server/Scripts/Tenant.00.00.01.sql deleted file mode 100644 index 90118490..00000000 --- a/Oqtane.Server/Scripts/Tenant.00.00.01.sql +++ /dev/null @@ -1,68 +0,0 @@ -/* - -ASP.NET Identity Minimal Schema - -*/ - -CREATE TABLE [dbo].[AspNetUsers]( - [Id] [nvarchar](450) NOT NULL, - [UserName] [nvarchar](256) NULL, - [NormalizedUserName] [nvarchar](256) NULL, - [Email] [nvarchar](256) NULL, - [NormalizedEmail] [nvarchar](256) NULL, - [EmailConfirmed] [bit] NOT NULL, - [PasswordHash] [nvarchar](max) NULL, - [SecurityStamp] [nvarchar](max) NULL, - [ConcurrencyStamp] [nvarchar](max) NULL, - [PhoneNumber] [nvarchar](max) NULL, - [PhoneNumberConfirmed] [bit] NOT NULL, - [TwoFactorEnabled] [bit] NOT NULL, - [LockoutEnd] [datetimeoffset](7) NULL, - [LockoutEnabled] [bit] NOT NULL, - [AccessFailedCount] [int] NOT NULL, - CONSTRAINT [PK_AspNetUsers] PRIMARY KEY CLUSTERED -( - [Id] ASC -) ON [PRIMARY] -) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] -GO - -CREATE TABLE [dbo].[AspNetUserClaims]( - [Id] [int] IDENTITY(1,1) NOT NULL, - [UserId] [nvarchar](450) NOT NULL, - [ClaimType] [nvarchar](max) NULL, - [ClaimValue] [nvarchar](max) NULL, - CONSTRAINT [PK_AspNetUserClaims] PRIMARY KEY CLUSTERED -( - [Id] ASC -) ON [PRIMARY] -) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] -GO - -CREATE NONCLUSTERED INDEX [IX_AspNetUserClaims_UserId] ON [dbo].[AspNetUserClaims] -( - [UserId] ASC -) ON [PRIMARY] -GO - -CREATE NONCLUSTERED INDEX [EmailIndex] ON [dbo].[AspNetUsers] -( - [NormalizedEmail] ASC -) ON [PRIMARY] -GO - -CREATE UNIQUE NONCLUSTERED INDEX [UserNameIndex] ON [dbo].[AspNetUsers] -( - [NormalizedUserName] ASC -) -WHERE ([NormalizedUserName] IS NOT NULL) -ON [PRIMARY] -GO - -ALTER TABLE [dbo].[AspNetUserClaims] WITH CHECK ADD CONSTRAINT [FK_AspNetUserClaims_AspNetUsers_UserId] FOREIGN KEY([UserId]) -REFERENCES [dbo].[AspNetUsers] ([Id]) -ON DELETE CASCADE -GO - -ALTER TABLE [dbo].[AspNetUserClaims] CHECK CONSTRAINT [FK_AspNetUserClaims_AspNetUsers_UserId] -GO diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 9e24a6fa..00b5f188 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -186,6 +185,7 @@ namespace Oqtane services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); // load the external assemblies into the app domain services.AddOqtaneModules(); diff --git a/Oqtane.Shared/Models/ApplicationVersion.cs b/Oqtane.Shared/Models/ApplicationVersion.cs deleted file mode 100644 index 9babb7cd..00000000 --- a/Oqtane.Shared/Models/ApplicationVersion.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Oqtane.Models -{ - public class ApplicationVersion - { - public int ApplicationVersionId { get; set; } - public string Version { get; set; } - public DateTime CreatedOn { get; set; } - } -} diff --git a/Oqtane.Shared/Models/Tenant.cs b/Oqtane.Shared/Models/Tenant.cs index ed392544..645f1e97 100644 --- a/Oqtane.Shared/Models/Tenant.cs +++ b/Oqtane.Shared/Models/Tenant.cs @@ -7,6 +7,7 @@ namespace Oqtane.Models public int TenantId { get; set; } public string Name { get; set; } public string DBConnectionString { get; set; } + public string Version { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } public string ModifiedBy { get; set; } diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index 6a0c0d87..131d6055 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -4,7 +4,7 @@ netstandard2.1 7.3 Debug;Release - 0.0.9 + 0.9.0 Oqtane Shaun Walker .NET Foundation diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index f4edd1ce..847e7a9e 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -3,7 +3,8 @@ public class Constants { public const string PackageId = "Oqtane.Framework"; - public const string Version = "0.0.9"; + public const string Version = "0.9.0"; + public const string ReleaseVersions = "0.9.0"; public const string PageComponent = "Oqtane.UI.ThemeBuilder, Oqtane.Client"; public const string ContainerComponent = "Oqtane.UI.ContainerBuilder, Oqtane.Client"; diff --git a/Oqtane.Shared/Shared/EntityNames.cs b/Oqtane.Shared/Shared/EntityNames.cs index 569e0603..345267f3 100644 --- a/Oqtane.Shared/Shared/EntityNames.cs +++ b/Oqtane.Shared/Shared/EntityNames.cs @@ -5,7 +5,7 @@ public const string Module = "Module"; public const string ModuleDefinition = "ModuleDefinition"; public const string PageModule = "PageModule"; - public const string Host = "Host"; + public const string Tenant = "Tenant"; public const string Site = "Site"; public const string Page = "Page"; public const string Folder = "Folder"; diff --git a/Oqtane.Upgrade/Oqtane.Upgrade.csproj b/Oqtane.Upgrade/Oqtane.Upgrade.csproj index dad498df..f4437db3 100644 --- a/Oqtane.Upgrade/Oqtane.Upgrade.csproj +++ b/Oqtane.Upgrade/Oqtane.Upgrade.csproj @@ -4,7 +4,7 @@ netcoreapp3.1 7.3 Exe - 0.0.9 + 0.9.0 Oqtane Shaun Walker .NET Foundation From 3afa489f223dfb0eb5cc314a78205767ba8c2e09 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 1 May 2020 11:32:41 -0400 Subject: [PATCH 190/265] upgrade to .NET Core 3.2 RC --- .../External/Client/[Owner].[Module]s.Module.Client.csproj | 6 +++--- .../External/Package/[Owner].[Module]s.Module.nuspec | 3 +++ .../External/Server/[Owner].[Module]s.Module.Server.csproj | 2 +- Oqtane.Client/Oqtane.Client.csproj | 6 +++--- Oqtane.Server/Oqtane.Server.csproj | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj index 757eb45c..cc286603 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj @@ -12,9 +12,9 @@ - - - + + + diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.nuspec b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.nuspec index e8c00e60..607f2ff3 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.nuspec +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.nuspec @@ -21,8 +21,11 @@ + + + \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj index 02a34494..96d4154f 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj @@ -18,7 +18,7 @@
    - + diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index 2e86a59a..f19dac81 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -27,10 +27,10 @@ - - + + - + diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 43102d3c..e115b73a 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -26,7 +26,7 @@ - + From 45aeb1702003e36837bbf71ac6903931c67fb17d Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 1 May 2020 15:58:34 -0400 Subject: [PATCH 191/265] fixed module action import/export, improved module installation --- Oqtane.Client/Themes/Controls/ModuleActionsBase.cs | 3 +-- Oqtane.Server/Infrastructure/DatabaseManager.cs | 2 +- Oqtane.Server/Repository/ModuleDefinitionRepository.cs | 9 ++++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Oqtane.Client/Themes/Controls/ModuleActionsBase.cs b/Oqtane.Client/Themes/Controls/ModuleActionsBase.cs index 9dd748e6..c5254428 100644 --- a/Oqtane.Client/Themes/Controls/ModuleActionsBase.cs +++ b/Oqtane.Client/Themes/Controls/ModuleActionsBase.cs @@ -75,8 +75,7 @@ namespace Oqtane.Themes.Controls private async Task EditUrlAsync(string url, int moduleId, string import) { await Task.Yield(); - EditUrl(moduleId, import); - return url; + return EditUrl(moduleId, import); } protected async Task ModuleAction(ActionViewModel action) diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 61b62589..57551bbb 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -367,7 +367,7 @@ namespace Oqtane.Infrastructure } } } - if (moduledefinition.Version != versions[versions.Length - 1]) + if (string.IsNullOrEmpty(result.Message) && moduledefinition.Version != versions[versions.Length - 1]) { moduledefinition.Version = versions[versions.Length - 1]; db.Entry(moduledefinition).State = EntityState.Modified; diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index 117c7ae7..0e968076 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -129,7 +129,14 @@ namespace Oqtane.Repository } else { - moduledefinition.Permissions = permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).EncodePermissions(); + if (permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).Any()) + { + moduledefinition.Permissions = permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).EncodePermissions(); + } + else + { + _permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions); + } } } // remove module definition from list as it is already synced From bf84f1247114abec4fc152f9fdca6ac945087bfd Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sun, 3 May 2020 11:39:34 -0400 Subject: [PATCH 192/265] fixed theme/layout handling in Add/Edit Page --- Oqtane.Client/Modules/Admin/Pages/Add.razor | 20 +++++++-------- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 27 ++++++++++++-------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 7c53755a..f7e3c20f 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -101,7 +101,7 @@
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    From a02cfea6c9f2be548374391a981ff7c468d3ffec Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Tue, 5 May 2020 09:15:36 -0400 Subject: [PATCH 194/265] improve performance of alias handling and allow aliases to be an unlimited number of subfolders in depth --- .../Templates/External/Client/Edit.razor | 2 +- .../Templates/External/Client/Index.razor | 2 +- .../Client/Services/[Module]Service.cs | 20 +++---- .../Oqtane.Client/Modules/[Module]/Edit.razor | 2 +- .../Modules/[Module]/Index.razor | 2 +- .../[Module]/Services/[Module]Service.cs | 20 +++---- Oqtane.Client/Modules/Admin/Site/Index.razor | 6 +- Oqtane.Client/Modules/Admin/Sites/Edit.razor | 20 ++++--- Oqtane.Client/Modules/Admin/Sites/Index.razor | 3 +- Oqtane.Client/Modules/HtmlText/Edit.razor | 6 +- Oqtane.Client/Modules/HtmlText/Index.razor | 2 +- .../HtmlText/Services/HtmlTextService.cs | 34 +++-------- Oqtane.Client/Modules/ModuleBase.cs | 2 +- .../IdentityAuthenticationStateProvider.cs | 3 +- Oqtane.Client/Services/AliasService.cs | 40 +++---------- Oqtane.Client/Services/FileService.cs | 21 +++---- Oqtane.Client/Services/FolderService.cs | 17 +++--- Oqtane.Client/Services/InstallationService.cs | 12 +--- .../Services/Interfaces/ISiteService.cs | 12 ++-- .../Services/Interfaces/IUserService.cs | 2 - Oqtane.Client/Services/JobLogService.cs | 23 ++------ Oqtane.Client/Services/JobService.cs | 28 +++------ Oqtane.Client/Services/LogService.cs | 12 ++-- .../Services/ModuleDefinitionService.cs | 18 ++---- Oqtane.Client/Services/ModuleService.cs | 19 ++----- Oqtane.Client/Services/NotificationService.cs | 18 ++---- Oqtane.Client/Services/PackageService.cs | 20 +------ Oqtane.Client/Services/PageModuleService.cs | 23 +++----- Oqtane.Client/Services/PageService.cs | 26 ++++----- Oqtane.Client/Services/ProfileService.cs | 21 +++---- Oqtane.Client/Services/RoleService.cs | 18 ++---- Oqtane.Client/Services/ServiceBase.cs | 41 +++++++------ Oqtane.Client/Services/SettingService.cs | 24 +++----- Oqtane.Client/Services/SiteService.cs | 31 +++++----- Oqtane.Client/Services/SiteTemplateService.cs | 18 +----- Oqtane.Client/Services/SqlService.cs | 20 +------ Oqtane.Client/Services/SystemService.cs | 18 +----- Oqtane.Client/Services/TenantService.cs | 24 ++------ Oqtane.Client/Services/ThemeService.cs | 12 +--- Oqtane.Client/Services/UserRoleService.cs | 21 +++---- Oqtane.Client/Services/UserService.cs | 15 +---- Oqtane.Client/Themes/ThemeControlBase.cs | 2 +- Oqtane.Client/UI/SiteRouter.razor | 57 ++++++++----------- Oqtane.Client/UI/ThemeBuilder.razor | 6 +- Oqtane.Server/Controllers/AliasController.cs | 35 ++++++++---- Oqtane.Server/Controllers/PageController.cs | 14 +++-- .../Controllers/PageModuleController.cs | 12 ++-- Oqtane.Server/Controllers/SiteController.cs | 2 +- Oqtane.Server/Controllers/UserController.cs | 21 ++++--- .../Controllers/UserRoleController.cs | 10 ++-- .../Infrastructure/Interfaces/ISyncManager.cs | 4 +- Oqtane.Server/Infrastructure/SyncManager.cs | 27 ++------- Oqtane.Server/Repository/TenantResolver.cs | 31 ++-------- Oqtane.Shared/Shared/Utilities.cs | 7 ++- 54 files changed, 320 insertions(+), 586 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Edit.razor index d23ab2e0..84e83aae 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Edit.razor @@ -43,7 +43,7 @@ { try { - [Module]Service = new [Module]Service(http, sitestate, NavigationManager); + [Module]Service = new [Module]Service(http, sitestate); if (PageState.Action == "Edit") { _id = Int32.Parse(PageState.QueryString["id"]); diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor index 15d660be..78e7c079 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor @@ -78,7 +78,7 @@ else { try { - [Module]Service = new [Module]Service(http, sitestate, NavigationManager); + [Module]Service = new [Module]Service(http, sitestate); _[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId); } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Services/[Module]Service.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Services/[Module]Service.cs index 5e559c3b..f95c41b9 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Services/[Module]Service.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Services/[Module]Service.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using Microsoft.AspNetCore.Components; using Oqtane.Modules; using Oqtane.Services; using Oqtane.Shared; @@ -12,44 +11,39 @@ namespace [Owner].[Module]s.Services { public class [Module]Service : ServiceBase, I[Module]Service, IService { - private readonly NavigationManager _navigationManager; private readonly SiteState _siteState; - public [Module]Service(HttpClient http, SiteState siteState, NavigationManager navigationManager) : base(http) + public [Module]Service(HttpClient http, SiteState siteState) : base(http) { _siteState = siteState; - _navigationManager = navigationManager; } - private string Apiurl - { - get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "[Module]"); } - } + private string Apiurl=> CreateApiUrl(_siteState.Alias, "[Module]"); public async Task> Get[Module]sAsync(int ModuleId) { - List<[Module]> [Module]s = await GetJsonAsync>(Apiurl + "?moduleid=" + ModuleId.ToString()); + List<[Module]> [Module]s = await GetJsonAsync>($"{Apiurl}?moduleid={ModuleId}"); return [Module]s.OrderBy(item => item.Name).ToList(); } public async Task<[Module]> Get[Module]Async(int [Module]Id) { - return await GetJsonAsync<[Module]>(Apiurl + "/" + [Module]Id.ToString()); + return await GetJsonAsync<[Module]>($"{Apiurl}/{[Module]Id}"); } public async Task<[Module]> Add[Module]Async([Module] [Module]) { - return await PostJsonAsync<[Module]>(Apiurl + "?entityid=" + [Module].ModuleId, [Module]); + return await PostJsonAsync<[Module]>($"{Apiurl}?entityid={[Module].ModuleId}", [Module]); } public async Task<[Module]> Update[Module]Async([Module] [Module]) { - return await PutJsonAsync<[Module]>(Apiurl + "/" + [Module].[Module]Id + "?entityid=" + [Module].ModuleId, [Module]); + return await PutJsonAsync<[Module]>($"{Apiurl}/{[Module].[Module]Id}?entityid={[Module].ModuleId}", [Module]); } public async Task Delete[Module]Async(int [Module]Id) { - await DeleteAsync(Apiurl + "/" + [Module]Id.ToString()); + await DeleteAsync($"{Apiurl}/{[Module]Id}"); } } } diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor index d23ab2e0..84e83aae 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor @@ -43,7 +43,7 @@ { try { - [Module]Service = new [Module]Service(http, sitestate, NavigationManager); + [Module]Service = new [Module]Service(http, sitestate); if (PageState.Action == "Edit") { _id = Int32.Parse(PageState.QueryString["id"]); diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor index 2c9f1764..fdb11b98 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor @@ -69,7 +69,7 @@ else { try { - [Module]Service = new [Module]Service(http, sitestate, NavigationManager); + [Module]Service = new [Module]Service(http, sitestate); _[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId); } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs index 5e559c3b..f95c41b9 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using Microsoft.AspNetCore.Components; using Oqtane.Modules; using Oqtane.Services; using Oqtane.Shared; @@ -12,44 +11,39 @@ namespace [Owner].[Module]s.Services { public class [Module]Service : ServiceBase, I[Module]Service, IService { - private readonly NavigationManager _navigationManager; private readonly SiteState _siteState; - public [Module]Service(HttpClient http, SiteState siteState, NavigationManager navigationManager) : base(http) + public [Module]Service(HttpClient http, SiteState siteState) : base(http) { _siteState = siteState; - _navigationManager = navigationManager; } - private string Apiurl - { - get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "[Module]"); } - } + private string Apiurl=> CreateApiUrl(_siteState.Alias, "[Module]"); public async Task> Get[Module]sAsync(int ModuleId) { - List<[Module]> [Module]s = await GetJsonAsync>(Apiurl + "?moduleid=" + ModuleId.ToString()); + List<[Module]> [Module]s = await GetJsonAsync>($"{Apiurl}?moduleid={ModuleId}"); return [Module]s.OrderBy(item => item.Name).ToList(); } public async Task<[Module]> Get[Module]Async(int [Module]Id) { - return await GetJsonAsync<[Module]>(Apiurl + "/" + [Module]Id.ToString()); + return await GetJsonAsync<[Module]>($"{Apiurl}/{[Module]Id}"); } public async Task<[Module]> Add[Module]Async([Module] [Module]) { - return await PostJsonAsync<[Module]>(Apiurl + "?entityid=" + [Module].ModuleId, [Module]); + return await PostJsonAsync<[Module]>($"{Apiurl}?entityid={[Module].ModuleId}", [Module]); } public async Task<[Module]> Update[Module]Async([Module] [Module]) { - return await PutJsonAsync<[Module]>(Apiurl + "/" + [Module].[Module]Id + "?entityid=" + [Module].ModuleId, [Module]); + return await PutJsonAsync<[Module]>($"{Apiurl}/{[Module].[Module]Id}?entityid={[Module].ModuleId}", [Module]); } public async Task Delete[Module]Async(int [Module]Id) { - await DeleteAsync(Apiurl + "/" + [Module]Id.ToString()); + await DeleteAsync($"{Apiurl}/{[Module]Id}"); } } } diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index a4069b67..7774b379 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -251,7 +251,7 @@ { _themeList = await ThemeService.GetThemesAsync(); _aliasList = await AliasService.GetAliasesAsync(); - Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId, PageState.Alias); + Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId); if (site != null) { _name = site.Name; @@ -364,7 +364,7 @@ if (unique) { - var site = await SiteService.GetSiteAsync(PageState.Site.SiteId, PageState.Alias); + var site = await SiteService.GetSiteAsync(PageState.Site.SiteId); if (site != null) { site.Name = _name; @@ -395,7 +395,7 @@ site.PwaSplashIconFileId = pwasplashiconfileid; } - site = await SiteService.UpdateSiteAsync(site, PageState.Alias); + site = await SiteService.UpdateSiteAsync(site); _urls = _urls.Replace("\n", ","); var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor index 2eb3110d..698892a7 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor @@ -134,18 +134,19 @@ _aliasList = await AliasService.GetAliasesAsync(); _alias = _aliasList.Find(item => item.AliasId == Int32.Parse(PageState.QueryString["id"])); - var site = await SiteService.GetSiteAsync(_alias.SiteId, _alias); + SiteService.SetAlias(_alias); + var site = await SiteService.GetSiteAsync(_alias.SiteId); if (site != null) { _name = site.Name; _tenantList = await TenantService.GetTenantsAsync(); _tenant = _tenantList.Find(item => item.TenantId == site.TenantId).Name; - + foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList()) { _urls += alias.Name + "\n"; } - + _themetype = site.DefaultThemeType; _panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); _layouttype = site.DefaultLayoutType; @@ -182,7 +183,7 @@ { _panelayouts = new Dictionary(); } - + StateHasChanged(); } catch (Exception ex) @@ -206,10 +207,11 @@ unique = false; } } - + if (unique) { - var site = await SiteService.GetSiteAsync(_alias.SiteId, _alias); + SiteService.SetAlias(_alias); + var site = await SiteService.GetSiteAsync(_alias.SiteId); if (site != null) { site.Name = _name; @@ -219,11 +221,11 @@ site.DefaultContainerType = _containertype; site.IsDeleted = (_isdeleted == null || Boolean.Parse(_isdeleted)); - site = await SiteService.UpdateSiteAsync(site, _alias); + site = await SiteService.UpdateSiteAsync(site); _urls = _urls.Replace("\n", ","); var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - + foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList()) { if (!names.Contains(alias.Name)) @@ -231,7 +233,7 @@ await AliasService.DeleteAliasAsync(alias.AliasId); } } - + foreach (string name in names) { if (!_aliasList.Exists(item => item.Name == name)) diff --git a/Oqtane.Client/Modules/Admin/Sites/Index.razor b/Oqtane.Client/Modules/Admin/Sites/Index.razor index 978f7ad3..d8b79632 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Index.razor @@ -54,7 +54,8 @@ else { if (alias.SiteId != PageState.Site.SiteId || alias.TenantId != PageState.Site.TenantId) { - await SiteService.DeleteSiteAsync(alias.SiteId, alias); + SiteService.SetAlias(alias); + await SiteService.DeleteSiteAsync(alias.SiteId); await Log(alias, LogLevel.Information, "", null, "Site Deleted {SiteId}", alias.SiteId); var aliases = await AliasService.GetAliasesAsync(); diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index 41c55192..ab0f1feb 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -16,7 +16,7 @@ + + + +
    - +
    - + @@ -190,7 +190,7 @@
    - + diff --git a/Oqtane.Client/Themes/Controls/LoginBase.cs b/Oqtane.Client/Themes/Controls/LoginBase.cs index a4282c08..60bbb781 100644 --- a/Oqtane.Client/Themes/Controls/LoginBase.cs +++ b/Oqtane.Client/Themes/Controls/LoginBase.cs @@ -35,7 +35,7 @@ namespace Oqtane.Themes.Controls var interop = new Interop(jsRuntime); string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken"); var fields = new { __RequestVerificationToken = antiforgerytoken, returnurl = (PageState.Alias.Path + "/" + PageState.Page.Path) }; - await interop.SubmitForm("/pages/logout/", fields); + await interop.SubmitForm($"/{PageState.Alias.AliasId}/pages/logout/", fields); } else { diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 070e872f..a384fc58 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -471,7 +471,7 @@ } private Runtime GetRuntime() - => RuntimeInformation.IsOSPlatform(OSPlatform.Create("WEBASSEMBLY")) + => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")) ? Runtime.WebAssembly : Runtime.Server; } diff --git a/Oqtane.Server/Controllers/AliasController.cs b/Oqtane.Server/Controllers/AliasController.cs index 46dff2de..0bbdde97 100644 --- a/Oqtane.Server/Controllers/AliasController.cs +++ b/Oqtane.Server/Controllers/AliasController.cs @@ -14,7 +14,7 @@ using Microsoft.AspNetCore.Http; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class AliasController : Controller { private readonly IAliasRepository _aliases; diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index d6e94644..6c3223c9 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -21,7 +21,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class FileController : Controller { private readonly IWebHostEnvironment _environment; diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index d8c06445..68a01e23 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -14,7 +14,7 @@ using System.IO; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class FolderController : Controller { private readonly IFolderRepository _folders; diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index b930db48..b16f54f6 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -7,7 +7,7 @@ using Oqtane.Infrastructure; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class InstallationController : Controller { private readonly IConfigurationRoot _config; diff --git a/Oqtane.Server/Controllers/JobController.cs b/Oqtane.Server/Controllers/JobController.cs index 6d5c5c7b..8e1993aa 100644 --- a/Oqtane.Server/Controllers/JobController.cs +++ b/Oqtane.Server/Controllers/JobController.cs @@ -12,7 +12,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class JobController : Controller { private readonly IJobRepository _jobs; diff --git a/Oqtane.Server/Controllers/JobLogController.cs b/Oqtane.Server/Controllers/JobLogController.cs index 15b2714b..0c3f8bc5 100644 --- a/Oqtane.Server/Controllers/JobLogController.cs +++ b/Oqtane.Server/Controllers/JobLogController.cs @@ -9,7 +9,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class JobLogController : Controller { private readonly IJobLogRepository _jobLogs; diff --git a/Oqtane.Server/Controllers/LogController.cs b/Oqtane.Server/Controllers/LogController.cs index 9e62bd0c..8cccfd12 100644 --- a/Oqtane.Server/Controllers/LogController.cs +++ b/Oqtane.Server/Controllers/LogController.cs @@ -9,7 +9,7 @@ using Oqtane.Shared; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class LogController : Controller { private readonly ILogManager _logger; diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index e87a7a5a..1afc13d6 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -11,7 +11,7 @@ using Oqtane.Security; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class ModuleController : Controller { private readonly IModuleRepository _modules; diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 673e42b3..7ef9f7e1 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -17,7 +17,7 @@ using Microsoft.Extensions.DependencyInjection; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class ModuleDefinitionController : Controller { private readonly IModuleDefinitionRepository _moduleDefinitions; diff --git a/Oqtane.Server/Controllers/NotificationController.cs b/Oqtane.Server/Controllers/NotificationController.cs index ebf7489b..5310f5c7 100644 --- a/Oqtane.Server/Controllers/NotificationController.cs +++ b/Oqtane.Server/Controllers/NotificationController.cs @@ -10,7 +10,7 @@ using Oqtane.Security; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class NotificationController : Controller { private readonly INotificationRepository _notifications; diff --git a/Oqtane.Server/Controllers/PackageController.cs b/Oqtane.Server/Controllers/PackageController.cs index a6ea13ef..51194245 100644 --- a/Oqtane.Server/Controllers/PackageController.cs +++ b/Oqtane.Server/Controllers/PackageController.cs @@ -15,7 +15,7 @@ using Oqtane.Shared; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class PackageController : Controller { private readonly IWebHostEnvironment _environment; diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 09210bce..16afb06e 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -13,7 +13,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class PageController : Controller { private readonly IPageRepository _pages; diff --git a/Oqtane.Server/Controllers/PageModuleController.cs b/Oqtane.Server/Controllers/PageModuleController.cs index d4712be6..f2c0d489 100644 --- a/Oqtane.Server/Controllers/PageModuleController.cs +++ b/Oqtane.Server/Controllers/PageModuleController.cs @@ -11,7 +11,7 @@ using Oqtane.Security; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class PageModuleController : Controller { private readonly IPageModuleRepository _pageModules; diff --git a/Oqtane.Server/Controllers/ProfileController.cs b/Oqtane.Server/Controllers/ProfileController.cs index aad21208..aa1158ac 100644 --- a/Oqtane.Server/Controllers/ProfileController.cs +++ b/Oqtane.Server/Controllers/ProfileController.cs @@ -9,7 +9,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class ProfileController : Controller { private readonly IProfileRepository _profiles; diff --git a/Oqtane.Server/Controllers/RoleController.cs b/Oqtane.Server/Controllers/RoleController.cs index 956fad8f..418382c5 100644 --- a/Oqtane.Server/Controllers/RoleController.cs +++ b/Oqtane.Server/Controllers/RoleController.cs @@ -9,7 +9,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class RoleController : Controller { private readonly IRoleRepository _roles; diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index 44437344..e9be3798 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -10,7 +10,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class SettingController : Controller { private readonly ISettingRepository _settings; diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index e619e67f..c358ea3c 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -10,7 +10,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class SiteController : Controller { private readonly ISiteRepository _sites; diff --git a/Oqtane.Server/Controllers/SiteTemplateController.cs b/Oqtane.Server/Controllers/SiteTemplateController.cs index c74e69a4..2f709fb1 100644 --- a/Oqtane.Server/Controllers/SiteTemplateController.cs +++ b/Oqtane.Server/Controllers/SiteTemplateController.cs @@ -5,7 +5,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class SiteTemplateController : Controller { private readonly ISiteTemplateRepository _siteTemplates; diff --git a/Oqtane.Server/Controllers/SqlController.cs b/Oqtane.Server/Controllers/SqlController.cs index a884ab35..5bce5a92 100644 --- a/Oqtane.Server/Controllers/SqlController.cs +++ b/Oqtane.Server/Controllers/SqlController.cs @@ -14,7 +14,7 @@ using System; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class SqlController : Controller { private readonly ITenantRepository _tenants; diff --git a/Oqtane.Server/Controllers/SystemController.cs b/Oqtane.Server/Controllers/SystemController.cs index bfd8d97a..2f5d7339 100644 --- a/Oqtane.Server/Controllers/SystemController.cs +++ b/Oqtane.Server/Controllers/SystemController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Hosting; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class SystemController : Controller { private readonly IWebHostEnvironment _environment; diff --git a/Oqtane.Server/Controllers/TenantController.cs b/Oqtane.Server/Controllers/TenantController.cs index bfacbd1c..0f76fc78 100644 --- a/Oqtane.Server/Controllers/TenantController.cs +++ b/Oqtane.Server/Controllers/TenantController.cs @@ -9,7 +9,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class TenantController : Controller { private readonly ITenantRepository _tenants; diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index 5f516851..fa182dfa 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -15,7 +15,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class ThemeController : Controller { private readonly IThemeRepository _themes; diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 14ff6f60..fce08020 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -16,7 +16,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class UserController : Controller { private readonly IUserRepository _users; diff --git a/Oqtane.Server/Controllers/UserRoleController.cs b/Oqtane.Server/Controllers/UserRoleController.cs index ca1d1929..b4398aa9 100644 --- a/Oqtane.Server/Controllers/UserRoleController.cs +++ b/Oqtane.Server/Controllers/UserRoleController.cs @@ -9,7 +9,7 @@ using Oqtane.Repository; namespace Oqtane.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class UserRoleController : Controller { private readonly IUserRoleRepository _userRoles; diff --git a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs index 5eb6f37f..fc9495fc 100644 --- a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs +++ b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs @@ -11,7 +11,7 @@ using Oqtane.Infrastructure; namespace Oqtane.Modules.HtmlText.Controllers { - [Route("{site}/api/[controller]")] + [Route("{alias}/api/[controller]")] public class HtmlTextController : Controller { private readonly IHtmlTextRepository _htmlText; diff --git a/Oqtane.Server/Pages/Login.cshtml b/Oqtane.Server/Pages/Login.cshtml index f2eab15f..92433015 100644 --- a/Oqtane.Server/Pages/Login.cshtml +++ b/Oqtane.Server/Pages/Login.cshtml @@ -1,3 +1,3 @@ -@page "/pages/login" +@page "/{alias}/pages/login" @namespace Oqtane.Pages @model Oqtane.Pages.LoginModel diff --git a/Oqtane.Server/Pages/Logout.cshtml b/Oqtane.Server/Pages/Logout.cshtml index f114ee1e..d477046c 100644 --- a/Oqtane.Server/Pages/Logout.cshtml +++ b/Oqtane.Server/Pages/Logout.cshtml @@ -1,3 +1,3 @@ -@page "/pages/logout" +@page "/{alias}/pages/logout" @namespace Oqtane.Pages @model Oqtane.Pages.LogoutModel diff --git a/Oqtane.Server/Repository/TenantResolver.cs b/Oqtane.Server/Repository/TenantResolver.cs index 47f0e0bd..cd273b3a 100644 --- a/Oqtane.Server/Repository/TenantResolver.cs +++ b/Oqtane.Server/Repository/TenantResolver.cs @@ -27,7 +27,7 @@ namespace Oqtane.Repository if (accessor.HttpContext != null) { string[] segments = accessor.HttpContext.Request.Path.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); - if (segments.Length > 1 && segments[1] == "api" && segments[0] != "~") + if (segments.Length > 1 && (segments[1] == "api" || segments[1] == "pages") && segments[0] != "~") { aliasId = int.Parse(segments[0]); } From e78a5e090d83a24f9a1f0fad88beaab989765669 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Thu, 7 May 2020 12:17:07 +0200 Subject: [PATCH 197/265] Bootswatch themes in Oqtane.Themes.OqtaneTheme Theme is set in Oqtane.Client\Themes\OqtaneTheme\Default.razor ``` protected override async Task OnParametersSetAsync() { // go to https://www.bootstrapcdn.com/bootswatch/ and take your favorite theme // await LoadBootstrapTheme("https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css","sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM"); await IncludeCSS("Theme.css"); } ``` --- .../Themes/OqtaneTheme/Default.razor | 3 + Oqtane.Client/Themes/ThemeBase.cs | 9 +- .../Oqtane.Themes.OqtaneTheme/Theme.css | 10828 +--------------- Oqtane.Server/wwwroot/js/interop.js | 4 +- 4 files changed, 14 insertions(+), 10830 deletions(-) diff --git a/Oqtane.Client/Themes/OqtaneTheme/Default.razor b/Oqtane.Client/Themes/OqtaneTheme/Default.razor index 89623e29..8bcf08fc 100644 --- a/Oqtane.Client/Themes/OqtaneTheme/Default.razor +++ b/Oqtane.Client/Themes/OqtaneTheme/Default.razor @@ -18,6 +18,9 @@ protected override async Task OnParametersSetAsync() { + // go to https://www.bootstrapcdn.com/bootswatch/ and take your favorite theme + // + await LoadBootstrapTheme("https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css","sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM"); await IncludeCSS("Theme.css"); } } diff --git a/Oqtane.Client/Themes/ThemeBase.cs b/Oqtane.Client/Themes/ThemeBase.cs index 2ee51b39..83694766 100644 --- a/Oqtane.Client/Themes/ThemeBase.cs +++ b/Oqtane.Client/Themes/ThemeBase.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using Oqtane.Shared; using Oqtane.UI; @@ -30,6 +30,13 @@ namespace Oqtane.Themes await interop.IncludeCSS("Theme", Url); } + public async Task LoadBootstrapTheme(string url, string integrity = null) + { + var interop = new Interop(JSRuntime); + string crossorigin = string.IsNullOrEmpty(integrity) ? string.Empty : "anonymous"; + await interop.IncludeLink("bootstrap", "stylesheet", url, "text/css", integrity, crossorigin); + } + public string NavigateUrl() { return NavigateUrl(PageState.Page.Path); diff --git a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css index 1cf6d012..0b093b3f 100644 --- a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css +++ b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css @@ -1,10830 +1,4 @@ -/*! - * Bootswatch v4.3.1 - * Homepage: https://bootswatch.com - * Copyright 2012-2019 Thomas Park - * Licensed under MIT - * Based on Bootstrap -*/ -/*! - * Bootstrap v4.3.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -@import url("https://fonts.googleapis.com/css?family=Roboto:400,700"); -:root { - --blue: #2A9FD6; - --indigo: #6610f2; - --purple: #6f42c1; - --pink: #e83e8c; - --red: #CC0000; - --orange: #fd7e14; - --yellow: #FF8800; - --green: #77B300; - --teal: #20c997; - --cyan: #9933CC; - --white: #fff; - --gray: #555; - --gray-dark: #222; - --primary: #2A9FD6; - --secondary: #555; - --success: #77B300; - --info: #9933CC; - --warning: #FF8800; - --danger: #CC0000; - --light: #222; - --dark: #ADAFAE; - --breakpoint-xs: 0; - --breakpoint-sm: 576px; - --breakpoint-md: 768px; - --breakpoint-lg: 992px; - --breakpoint-xl: 1200px; - --font-family-sans-serif: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; -} - -*, -*::before, -*::after { - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - -html { - font-family: sans-serif; - line-height: 1.15; - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} - -article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { - display: block; -} - -body { - margin: 0; - font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - font-size: 0.875rem; - font-weight: 400; - line-height: 1.5; - color: #888; - text-align: left; - background-color: #060606; -} - -[tabindex="-1"]:focus { - outline: 0 !important; -} - -hr { - -webkit-box-sizing: content-box; - box-sizing: content-box; - height: 0; - overflow: visible; -} - -h1, h2, h3, h4, h5, h6 { - margin-top: 0; - margin-bottom: 0.5rem; -} - -p { - margin-top: 0; - margin-bottom: 1rem; -} - -abbr[title], -abbr[data-original-title] { - text-decoration: underline; - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - cursor: help; - border-bottom: 0; - text-decoration-skip-ink: none; -} - -address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; -} - -ol, -ul, -dl { - margin-top: 0; - margin-bottom: 1rem; -} - -ol ol, -ul ul, -ol ul, -ul ol { - margin-bottom: 0; -} - -dt { - font-weight: 700; -} - -dd { - margin-bottom: .5rem; - margin-left: 0; -} - -blockquote { - margin: 0 0 1rem; -} - -b, -strong { - font-weight: bolder; -} - -small { - font-size: 80%; -} - -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} - -sub { - bottom: -.25em; -} - -sup { - top: -.5em; -} - -a { - color: #2A9FD6; - text-decoration: none; - background-color: transparent; -} - -a:hover { - color: #1d7097; - text-decoration: underline; -} - -a:not([href]):not([tabindex]) { - color: inherit; - text-decoration: none; -} - -a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { - color: inherit; - text-decoration: none; -} - -a:not([href]):not([tabindex]):focus { - outline: 0; -} - -pre, -code, -kbd, -samp { - font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - font-size: 1em; -} - -pre { - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; -} - -figure { - margin: 0 0 1rem; -} - -img { - vertical-align: middle; - border-style: none; -} - -svg { - overflow: hidden; - vertical-align: middle; -} - -table { - border-collapse: collapse; -} - -caption { - padding-top: 0.75rem; - padding-bottom: 0.75rem; - color: #555; - text-align: left; - caption-side: bottom; -} - -th { - text-align: inherit; -} - -label { - display: inline-block; - margin-bottom: 0.5rem; -} - -button { - border-radius: 0; -} - -button:focus { - outline: 1px dotted; - outline: 5px auto -webkit-focus-ring-color; -} - -input, -button, -select, -optgroup, -textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; -} - -button, -input { - overflow: visible; -} - -button, -select { - text-transform: none; -} - -select { - word-wrap: normal; -} - -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -button:not(:disabled), -[type="button"]:not(:disabled), -[type="reset"]:not(:disabled), -[type="submit"]:not(:disabled) { - cursor: pointer; -} - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - padding: 0; - border-style: none; -} - -input[type="radio"], -input[type="checkbox"] { - -webkit-box-sizing: border-box; - box-sizing: border-box; - padding: 0; -} - -input[type="date"], -input[type="time"], -input[type="datetime-local"], -input[type="month"] { - -webkit-appearance: listbox; -} - -textarea { - overflow: auto; - resize: vertical; -} - -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} - -legend { - display: block; - width: 100%; - max-width: 100%; - padding: 0; - margin-bottom: .5rem; - font-size: 1.5rem; - line-height: inherit; - color: inherit; - white-space: normal; -} - -progress { - vertical-align: baseline; -} - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -[type="search"] { - outline-offset: -2px; - -webkit-appearance: none; -} - -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; -} - -output { - display: inline-block; -} - -summary { - display: list-item; - cursor: pointer; -} - -template { - display: none; -} - -[hidden] { - display: none !important; -} - -h1, h2, h3, h4, h5, h6, -.h1, .h2, .h3, .h4, .h5, .h6 { - margin-bottom: 0.5rem; - font-weight: 500; - line-height: 1.2; - color: #fff; -} - -h1, .h1 { - font-size: 4rem; -} - -h2, .h2 { - font-size: 3rem; -} - -h3, .h3 { - font-size: 2.5rem; -} - -h4, .h4 { - font-size: 2rem; -} - -h5, .h5 { - font-size: 1.5rem; -} - -h6, .h6 { - font-size: 0.875rem; -} - -.lead { - font-size: 1.09375rem; - font-weight: 300; -} - -.display-1 { - font-size: 6rem; - font-weight: 300; - line-height: 1.2; -} - -.display-2 { - font-size: 5.5rem; - font-weight: 300; - line-height: 1.2; -} - -.display-3 { - font-size: 4.5rem; - font-weight: 300; - line-height: 1.2; -} - -.display-4 { - font-size: 3.5rem; - font-weight: 300; - line-height: 1.2; -} - -hr { - margin-top: 1rem; - margin-bottom: 1rem; - border: 0; - border-top: 1px solid rgba(0, 0, 0, 0.1); -} - -small, -.small { - font-size: 80%; - font-weight: 400; -} - -mark, -.mark { - padding: 0.2em; - background-color: #fcf8e3; -} - -.list-unstyled { - padding-left: 0; - list-style: none; -} - -.list-inline { - padding-left: 0; - list-style: none; -} - -.list-inline-item { - display: inline-block; -} - -.list-inline-item:not(:last-child) { - margin-right: 0.5rem; -} - -.initialism { - font-size: 90%; - text-transform: uppercase; -} - -.blockquote { - margin-bottom: 1rem; - font-size: 1.09375rem; -} - -.blockquote-footer { - display: block; - font-size: 80%; - color: #555; -} - -.blockquote-footer::before { - content: "\2014\00A0"; -} - -.img-fluid { - max-width: 100%; - height: auto; -} - -.img-thumbnail { - padding: 0.25rem; - background-color: #060606; - border: 1px solid #dee2e6; - border-radius: 0.25rem; - max-width: 100%; - height: auto; -} - -.figure { - display: inline-block; -} - -.figure-img { - margin-bottom: 0.5rem; - line-height: 1; -} - -.figure-caption { - font-size: 90%; - color: #555; -} - -code { - font-size: 87.5%; - color: #e83e8c; - word-break: break-word; -} - -a > code { - color: inherit; -} - -kbd { - padding: 0.2rem 0.4rem; - font-size: 87.5%; - color: #fff; - background-color: #212529; - border-radius: 0.2rem; -} - -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: 700; -} - -pre { - display: block; - font-size: 87.5%; - color: inherit; -} - -pre code { - font-size: inherit; - color: inherit; - word-break: normal; -} - -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} - -.container { - width: 100%; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} - -@media (min-width: 576px) { - .container { - max-width: 540px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 720px; - } -} - -@media (min-width: 992px) { - .container { - max-width: 960px; - } -} - -@media (min-width: 1200px) { - .container { - max-width: 1140px; - } -} - -.container-fluid { - width: 100%; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} - -.row { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - margin-right: -15px; - margin-left: -15px; -} - -.no-gutters { - margin-right: 0; - margin-left: 0; -} - -.no-gutters > .col, -.no-gutters > [class*="col-"] { - padding-right: 0; - padding-left: 0; -} - -.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, -.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, -.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, -.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, -.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, -.col-xl-auto { - position: relative; - width: 100%; - padding-right: 15px; - padding-left: 15px; -} - -.col { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; -} - -.col-auto { - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; -} - -.col-1 { - -webkit-box-flex: 0; - -ms-flex: 0 0 8.3333333333%; - flex: 0 0 8.3333333333%; - max-width: 8.3333333333%; -} - -.col-2 { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.6666666667%; - flex: 0 0 16.6666666667%; - max-width: 16.6666666667%; -} - -.col-3 { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; -} - -.col-4 { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.3333333333%; - flex: 0 0 33.3333333333%; - max-width: 33.3333333333%; -} - -.col-5 { - -webkit-box-flex: 0; - -ms-flex: 0 0 41.6666666667%; - flex: 0 0 41.6666666667%; - max-width: 41.6666666667%; -} - -.col-6 { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; -} - -.col-7 { - -webkit-box-flex: 0; - -ms-flex: 0 0 58.3333333333%; - flex: 0 0 58.3333333333%; - max-width: 58.3333333333%; -} - -.col-8 { - -webkit-box-flex: 0; - -ms-flex: 0 0 66.6666666667%; - flex: 0 0 66.6666666667%; - max-width: 66.6666666667%; -} - -.col-9 { - -webkit-box-flex: 0; - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; -} - -.col-10 { - -webkit-box-flex: 0; - -ms-flex: 0 0 83.3333333333%; - flex: 0 0 83.3333333333%; - max-width: 83.3333333333%; -} - -.col-11 { - -webkit-box-flex: 0; - -ms-flex: 0 0 91.6666666667%; - flex: 0 0 91.6666666667%; - max-width: 91.6666666667%; -} - -.col-12 { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; -} - -.order-first { - -webkit-box-ordinal-group: 0; - -ms-flex-order: -1; - order: -1; -} - -.order-last { - -webkit-box-ordinal-group: 14; - -ms-flex-order: 13; - order: 13; -} - -.order-0 { - -webkit-box-ordinal-group: 1; - -ms-flex-order: 0; - order: 0; -} - -.order-1 { - -webkit-box-ordinal-group: 2; - -ms-flex-order: 1; - order: 1; -} - -.order-2 { - -webkit-box-ordinal-group: 3; - -ms-flex-order: 2; - order: 2; -} - -.order-3 { - -webkit-box-ordinal-group: 4; - -ms-flex-order: 3; - order: 3; -} - -.order-4 { - -webkit-box-ordinal-group: 5; - -ms-flex-order: 4; - order: 4; -} - -.order-5 { - -webkit-box-ordinal-group: 6; - -ms-flex-order: 5; - order: 5; -} - -.order-6 { - -webkit-box-ordinal-group: 7; - -ms-flex-order: 6; - order: 6; -} - -.order-7 { - -webkit-box-ordinal-group: 8; - -ms-flex-order: 7; - order: 7; -} - -.order-8 { - -webkit-box-ordinal-group: 9; - -ms-flex-order: 8; - order: 8; -} - -.order-9 { - -webkit-box-ordinal-group: 10; - -ms-flex-order: 9; - order: 9; -} - -.order-10 { - -webkit-box-ordinal-group: 11; - -ms-flex-order: 10; - order: 10; -} - -.order-11 { - -webkit-box-ordinal-group: 12; - -ms-flex-order: 11; - order: 11; -} - -.order-12 { - -webkit-box-ordinal-group: 13; - -ms-flex-order: 12; - order: 12; -} - -.offset-1 { - margin-left: 8.3333333333%; -} - -.offset-2 { - margin-left: 16.6666666667%; -} - -.offset-3 { - margin-left: 25%; -} - -.offset-4 { - margin-left: 33.3333333333%; -} - -.offset-5 { - margin-left: 41.6666666667%; -} - -.offset-6 { - margin-left: 50%; -} - -.offset-7 { - margin-left: 58.3333333333%; -} - -.offset-8 { - margin-left: 66.6666666667%; -} - -.offset-9 { - margin-left: 75%; -} - -.offset-10 { - margin-left: 83.3333333333%; -} - -.offset-11 { - margin-left: 91.6666666667%; -} - -@media (min-width: 576px) { - .col-sm { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .col-sm-auto { - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-sm-1 { - -webkit-box-flex: 0; - -ms-flex: 0 0 8.3333333333%; - flex: 0 0 8.3333333333%; - max-width: 8.3333333333%; - } - .col-sm-2 { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.6666666667%; - flex: 0 0 16.6666666667%; - max-width: 16.6666666667%; - } - .col-sm-3 { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-sm-4 { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.3333333333%; - flex: 0 0 33.3333333333%; - max-width: 33.3333333333%; - } - .col-sm-5 { - -webkit-box-flex: 0; - -ms-flex: 0 0 41.6666666667%; - flex: 0 0 41.6666666667%; - max-width: 41.6666666667%; - } - .col-sm-6 { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-sm-7 { - -webkit-box-flex: 0; - -ms-flex: 0 0 58.3333333333%; - flex: 0 0 58.3333333333%; - max-width: 58.3333333333%; - } - .col-sm-8 { - -webkit-box-flex: 0; - -ms-flex: 0 0 66.6666666667%; - flex: 0 0 66.6666666667%; - max-width: 66.6666666667%; - } - .col-sm-9 { - -webkit-box-flex: 0; - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-sm-10 { - -webkit-box-flex: 0; - -ms-flex: 0 0 83.3333333333%; - flex: 0 0 83.3333333333%; - max-width: 83.3333333333%; - } - .col-sm-11 { - -webkit-box-flex: 0; - -ms-flex: 0 0 91.6666666667%; - flex: 0 0 91.6666666667%; - max-width: 91.6666666667%; - } - .col-sm-12 { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-sm-first { - -webkit-box-ordinal-group: 0; - -ms-flex-order: -1; - order: -1; - } - .order-sm-last { - -webkit-box-ordinal-group: 14; - -ms-flex-order: 13; - order: 13; - } - .order-sm-0 { - -webkit-box-ordinal-group: 1; - -ms-flex-order: 0; - order: 0; - } - .order-sm-1 { - -webkit-box-ordinal-group: 2; - -ms-flex-order: 1; - order: 1; - } - .order-sm-2 { - -webkit-box-ordinal-group: 3; - -ms-flex-order: 2; - order: 2; - } - .order-sm-3 { - -webkit-box-ordinal-group: 4; - -ms-flex-order: 3; - order: 3; - } - .order-sm-4 { - -webkit-box-ordinal-group: 5; - -ms-flex-order: 4; - order: 4; - } - .order-sm-5 { - -webkit-box-ordinal-group: 6; - -ms-flex-order: 5; - order: 5; - } - .order-sm-6 { - -webkit-box-ordinal-group: 7; - -ms-flex-order: 6; - order: 6; - } - .order-sm-7 { - -webkit-box-ordinal-group: 8; - -ms-flex-order: 7; - order: 7; - } - .order-sm-8 { - -webkit-box-ordinal-group: 9; - -ms-flex-order: 8; - order: 8; - } - .order-sm-9 { - -webkit-box-ordinal-group: 10; - -ms-flex-order: 9; - order: 9; - } - .order-sm-10 { - -webkit-box-ordinal-group: 11; - -ms-flex-order: 10; - order: 10; - } - .order-sm-11 { - -webkit-box-ordinal-group: 12; - -ms-flex-order: 11; - order: 11; - } - .order-sm-12 { - -webkit-box-ordinal-group: 13; - -ms-flex-order: 12; - order: 12; - } - .offset-sm-0 { - margin-left: 0; - } - .offset-sm-1 { - margin-left: 8.3333333333%; - } - .offset-sm-2 { - margin-left: 16.6666666667%; - } - .offset-sm-3 { - margin-left: 25%; - } - .offset-sm-4 { - margin-left: 33.3333333333%; - } - .offset-sm-5 { - margin-left: 41.6666666667%; - } - .offset-sm-6 { - margin-left: 50%; - } - .offset-sm-7 { - margin-left: 58.3333333333%; - } - .offset-sm-8 { - margin-left: 66.6666666667%; - } - .offset-sm-9 { - margin-left: 75%; - } - .offset-sm-10 { - margin-left: 83.3333333333%; - } - .offset-sm-11 { - margin-left: 91.6666666667%; - } -} - -@media (min-width: 768px) { - .col-md { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .col-md-auto { - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-md-1 { - -webkit-box-flex: 0; - -ms-flex: 0 0 8.3333333333%; - flex: 0 0 8.3333333333%; - max-width: 8.3333333333%; - } - .col-md-2 { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.6666666667%; - flex: 0 0 16.6666666667%; - max-width: 16.6666666667%; - } - .col-md-3 { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-md-4 { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.3333333333%; - flex: 0 0 33.3333333333%; - max-width: 33.3333333333%; - } - .col-md-5 { - -webkit-box-flex: 0; - -ms-flex: 0 0 41.6666666667%; - flex: 0 0 41.6666666667%; - max-width: 41.6666666667%; - } - .col-md-6 { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-md-7 { - -webkit-box-flex: 0; - -ms-flex: 0 0 58.3333333333%; - flex: 0 0 58.3333333333%; - max-width: 58.3333333333%; - } - .col-md-8 { - -webkit-box-flex: 0; - -ms-flex: 0 0 66.6666666667%; - flex: 0 0 66.6666666667%; - max-width: 66.6666666667%; - } - .col-md-9 { - -webkit-box-flex: 0; - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-md-10 { - -webkit-box-flex: 0; - -ms-flex: 0 0 83.3333333333%; - flex: 0 0 83.3333333333%; - max-width: 83.3333333333%; - } - .col-md-11 { - -webkit-box-flex: 0; - -ms-flex: 0 0 91.6666666667%; - flex: 0 0 91.6666666667%; - max-width: 91.6666666667%; - } - .col-md-12 { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-md-first { - -webkit-box-ordinal-group: 0; - -ms-flex-order: -1; - order: -1; - } - .order-md-last { - -webkit-box-ordinal-group: 14; - -ms-flex-order: 13; - order: 13; - } - .order-md-0 { - -webkit-box-ordinal-group: 1; - -ms-flex-order: 0; - order: 0; - } - .order-md-1 { - -webkit-box-ordinal-group: 2; - -ms-flex-order: 1; - order: 1; - } - .order-md-2 { - -webkit-box-ordinal-group: 3; - -ms-flex-order: 2; - order: 2; - } - .order-md-3 { - -webkit-box-ordinal-group: 4; - -ms-flex-order: 3; - order: 3; - } - .order-md-4 { - -webkit-box-ordinal-group: 5; - -ms-flex-order: 4; - order: 4; - } - .order-md-5 { - -webkit-box-ordinal-group: 6; - -ms-flex-order: 5; - order: 5; - } - .order-md-6 { - -webkit-box-ordinal-group: 7; - -ms-flex-order: 6; - order: 6; - } - .order-md-7 { - -webkit-box-ordinal-group: 8; - -ms-flex-order: 7; - order: 7; - } - .order-md-8 { - -webkit-box-ordinal-group: 9; - -ms-flex-order: 8; - order: 8; - } - .order-md-9 { - -webkit-box-ordinal-group: 10; - -ms-flex-order: 9; - order: 9; - } - .order-md-10 { - -webkit-box-ordinal-group: 11; - -ms-flex-order: 10; - order: 10; - } - .order-md-11 { - -webkit-box-ordinal-group: 12; - -ms-flex-order: 11; - order: 11; - } - .order-md-12 { - -webkit-box-ordinal-group: 13; - -ms-flex-order: 12; - order: 12; - } - .offset-md-0 { - margin-left: 0; - } - .offset-md-1 { - margin-left: 8.3333333333%; - } - .offset-md-2 { - margin-left: 16.6666666667%; - } - .offset-md-3 { - margin-left: 25%; - } - .offset-md-4 { - margin-left: 33.3333333333%; - } - .offset-md-5 { - margin-left: 41.6666666667%; - } - .offset-md-6 { - margin-left: 50%; - } - .offset-md-7 { - margin-left: 58.3333333333%; - } - .offset-md-8 { - margin-left: 66.6666666667%; - } - .offset-md-9 { - margin-left: 75%; - } - .offset-md-10 { - margin-left: 83.3333333333%; - } - .offset-md-11 { - margin-left: 91.6666666667%; - } -} - -@media (min-width: 992px) { - .col-lg { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .col-lg-auto { - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-lg-1 { - -webkit-box-flex: 0; - -ms-flex: 0 0 8.3333333333%; - flex: 0 0 8.3333333333%; - max-width: 8.3333333333%; - } - .col-lg-2 { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.6666666667%; - flex: 0 0 16.6666666667%; - max-width: 16.6666666667%; - } - .col-lg-3 { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-lg-4 { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.3333333333%; - flex: 0 0 33.3333333333%; - max-width: 33.3333333333%; - } - .col-lg-5 { - -webkit-box-flex: 0; - -ms-flex: 0 0 41.6666666667%; - flex: 0 0 41.6666666667%; - max-width: 41.6666666667%; - } - .col-lg-6 { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-lg-7 { - -webkit-box-flex: 0; - -ms-flex: 0 0 58.3333333333%; - flex: 0 0 58.3333333333%; - max-width: 58.3333333333%; - } - .col-lg-8 { - -webkit-box-flex: 0; - -ms-flex: 0 0 66.6666666667%; - flex: 0 0 66.6666666667%; - max-width: 66.6666666667%; - } - .col-lg-9 { - -webkit-box-flex: 0; - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-lg-10 { - -webkit-box-flex: 0; - -ms-flex: 0 0 83.3333333333%; - flex: 0 0 83.3333333333%; - max-width: 83.3333333333%; - } - .col-lg-11 { - -webkit-box-flex: 0; - -ms-flex: 0 0 91.6666666667%; - flex: 0 0 91.6666666667%; - max-width: 91.6666666667%; - } - .col-lg-12 { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-lg-first { - -webkit-box-ordinal-group: 0; - -ms-flex-order: -1; - order: -1; - } - .order-lg-last { - -webkit-box-ordinal-group: 14; - -ms-flex-order: 13; - order: 13; - } - .order-lg-0 { - -webkit-box-ordinal-group: 1; - -ms-flex-order: 0; - order: 0; - } - .order-lg-1 { - -webkit-box-ordinal-group: 2; - -ms-flex-order: 1; - order: 1; - } - .order-lg-2 { - -webkit-box-ordinal-group: 3; - -ms-flex-order: 2; - order: 2; - } - .order-lg-3 { - -webkit-box-ordinal-group: 4; - -ms-flex-order: 3; - order: 3; - } - .order-lg-4 { - -webkit-box-ordinal-group: 5; - -ms-flex-order: 4; - order: 4; - } - .order-lg-5 { - -webkit-box-ordinal-group: 6; - -ms-flex-order: 5; - order: 5; - } - .order-lg-6 { - -webkit-box-ordinal-group: 7; - -ms-flex-order: 6; - order: 6; - } - .order-lg-7 { - -webkit-box-ordinal-group: 8; - -ms-flex-order: 7; - order: 7; - } - .order-lg-8 { - -webkit-box-ordinal-group: 9; - -ms-flex-order: 8; - order: 8; - } - .order-lg-9 { - -webkit-box-ordinal-group: 10; - -ms-flex-order: 9; - order: 9; - } - .order-lg-10 { - -webkit-box-ordinal-group: 11; - -ms-flex-order: 10; - order: 10; - } - .order-lg-11 { - -webkit-box-ordinal-group: 12; - -ms-flex-order: 11; - order: 11; - } - .order-lg-12 { - -webkit-box-ordinal-group: 13; - -ms-flex-order: 12; - order: 12; - } - .offset-lg-0 { - margin-left: 0; - } - .offset-lg-1 { - margin-left: 8.3333333333%; - } - .offset-lg-2 { - margin-left: 16.6666666667%; - } - .offset-lg-3 { - margin-left: 25%; - } - .offset-lg-4 { - margin-left: 33.3333333333%; - } - .offset-lg-5 { - margin-left: 41.6666666667%; - } - .offset-lg-6 { - margin-left: 50%; - } - .offset-lg-7 { - margin-left: 58.3333333333%; - } - .offset-lg-8 { - margin-left: 66.6666666667%; - } - .offset-lg-9 { - margin-left: 75%; - } - .offset-lg-10 { - margin-left: 83.3333333333%; - } - .offset-lg-11 { - margin-left: 91.6666666667%; - } -} - -@media (min-width: 1200px) { - .col-xl { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .col-xl-auto { - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-xl-1 { - -webkit-box-flex: 0; - -ms-flex: 0 0 8.3333333333%; - flex: 0 0 8.3333333333%; - max-width: 8.3333333333%; - } - .col-xl-2 { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.6666666667%; - flex: 0 0 16.6666666667%; - max-width: 16.6666666667%; - } - .col-xl-3 { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-xl-4 { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.3333333333%; - flex: 0 0 33.3333333333%; - max-width: 33.3333333333%; - } - .col-xl-5 { - -webkit-box-flex: 0; - -ms-flex: 0 0 41.6666666667%; - flex: 0 0 41.6666666667%; - max-width: 41.6666666667%; - } - .col-xl-6 { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-xl-7 { - -webkit-box-flex: 0; - -ms-flex: 0 0 58.3333333333%; - flex: 0 0 58.3333333333%; - max-width: 58.3333333333%; - } - .col-xl-8 { - -webkit-box-flex: 0; - -ms-flex: 0 0 66.6666666667%; - flex: 0 0 66.6666666667%; - max-width: 66.6666666667%; - } - .col-xl-9 { - -webkit-box-flex: 0; - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-xl-10 { - -webkit-box-flex: 0; - -ms-flex: 0 0 83.3333333333%; - flex: 0 0 83.3333333333%; - max-width: 83.3333333333%; - } - .col-xl-11 { - -webkit-box-flex: 0; - -ms-flex: 0 0 91.6666666667%; - flex: 0 0 91.6666666667%; - max-width: 91.6666666667%; - } - .col-xl-12 { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-xl-first { - -webkit-box-ordinal-group: 0; - -ms-flex-order: -1; - order: -1; - } - .order-xl-last { - -webkit-box-ordinal-group: 14; - -ms-flex-order: 13; - order: 13; - } - .order-xl-0 { - -webkit-box-ordinal-group: 1; - -ms-flex-order: 0; - order: 0; - } - .order-xl-1 { - -webkit-box-ordinal-group: 2; - -ms-flex-order: 1; - order: 1; - } - .order-xl-2 { - -webkit-box-ordinal-group: 3; - -ms-flex-order: 2; - order: 2; - } - .order-xl-3 { - -webkit-box-ordinal-group: 4; - -ms-flex-order: 3; - order: 3; - } - .order-xl-4 { - -webkit-box-ordinal-group: 5; - -ms-flex-order: 4; - order: 4; - } - .order-xl-5 { - -webkit-box-ordinal-group: 6; - -ms-flex-order: 5; - order: 5; - } - .order-xl-6 { - -webkit-box-ordinal-group: 7; - -ms-flex-order: 6; - order: 6; - } - .order-xl-7 { - -webkit-box-ordinal-group: 8; - -ms-flex-order: 7; - order: 7; - } - .order-xl-8 { - -webkit-box-ordinal-group: 9; - -ms-flex-order: 8; - order: 8; - } - .order-xl-9 { - -webkit-box-ordinal-group: 10; - -ms-flex-order: 9; - order: 9; - } - .order-xl-10 { - -webkit-box-ordinal-group: 11; - -ms-flex-order: 10; - order: 10; - } - .order-xl-11 { - -webkit-box-ordinal-group: 12; - -ms-flex-order: 11; - order: 11; - } - .order-xl-12 { - -webkit-box-ordinal-group: 13; - -ms-flex-order: 12; - order: 12; - } - .offset-xl-0 { - margin-left: 0; - } - .offset-xl-1 { - margin-left: 8.3333333333%; - } - .offset-xl-2 { - margin-left: 16.6666666667%; - } - .offset-xl-3 { - margin-left: 25%; - } - .offset-xl-4 { - margin-left: 33.3333333333%; - } - .offset-xl-5 { - margin-left: 41.6666666667%; - } - .offset-xl-6 { - margin-left: 50%; - } - .offset-xl-7 { - margin-left: 58.3333333333%; - } - .offset-xl-8 { - margin-left: 66.6666666667%; - } - .offset-xl-9 { - margin-left: 75%; - } - .offset-xl-10 { - margin-left: 83.3333333333%; - } - .offset-xl-11 { - margin-left: 91.6666666667%; - } -} - -.table { - width: 100%; - margin-bottom: 1rem; - color: #888; -} - -.table th, -.table td { - padding: 0.75rem; - vertical-align: top; - border-top: 1px solid #282828; -} - -.table thead th { - vertical-align: bottom; - border-bottom: 2px solid #282828; -} - -.table tbody + tbody { - border-top: 2px solid #282828; -} - -.table-sm th, -.table-sm td { - padding: 0.3rem; -} - -.table-bordered { - border: 1px solid #282828; -} - -.table-bordered th, -.table-bordered td { - border: 1px solid #282828; -} - -.table-bordered thead th, -.table-bordered thead td { - border-bottom-width: 2px; -} - -.table-borderless th, -.table-borderless td, -.table-borderless thead th, -.table-borderless tbody + tbody { - border: 0; -} - -.table-striped tbody tr:nth-of-type(odd) { - background-color: rgba(255, 255, 255, 0.05); -} - -.table-hover tbody tr:hover { - color: #888; - background-color: rgba(255, 255, 255, 0.075); -} - -.table-primary, -.table-primary > th, -.table-primary > td { - background-color: #c3e4f4; -} - -.table-primary th, -.table-primary td, -.table-primary thead th, -.table-primary tbody + tbody { - border-color: #90cdea; -} - -.table-hover .table-primary:hover { - background-color: #addaf0; -} - -.table-hover .table-primary:hover > td, -.table-hover .table-primary:hover > th { - background-color: #addaf0; -} - -.table-secondary, -.table-secondary > th, -.table-secondary > td { - background-color: #cfcfcf; -} - -.table-secondary th, -.table-secondary td, -.table-secondary thead th, -.table-secondary tbody + tbody { - border-color: #a7a7a7; -} - -.table-hover .table-secondary:hover { - background-color: #c2c2c2; -} - -.table-hover .table-secondary:hover > td, -.table-hover .table-secondary:hover > th { - background-color: #c2c2c2; -} - -.table-success, -.table-success > th, -.table-success > td { - background-color: #d9eab8; -} - -.table-success th, -.table-success td, -.table-success thead th, -.table-success tbody + tbody { - border-color: #b8d77a; -} - -.table-hover .table-success:hover { - background-color: #cee4a4; -} - -.table-hover .table-success:hover > td, -.table-hover .table-success:hover > th { - background-color: #cee4a4; -} - -.table-info, -.table-info > th, -.table-info > td { - background-color: #e2c6f1; -} - -.table-info th, -.table-info td, -.table-info thead th, -.table-info tbody + tbody { - border-color: #ca95e4; -} - -.table-hover .table-info:hover { - background-color: #d8b2ec; -} - -.table-hover .table-info:hover > td, -.table-hover .table-info:hover > th { - background-color: #d8b2ec; -} - -.table-warning, -.table-warning > th, -.table-warning > td { - background-color: #ffdeb8; -} - -.table-warning th, -.table-warning td, -.table-warning thead th, -.table-warning tbody + tbody { - border-color: #ffc17a; -} - -.table-hover .table-warning:hover { - background-color: #ffd29f; -} - -.table-hover .table-warning:hover > td, -.table-hover .table-warning:hover > th { - background-color: #ffd29f; -} - -.table-danger, -.table-danger > th, -.table-danger > td { - background-color: #f1b8b8; -} - -.table-danger th, -.table-danger td, -.table-danger thead th, -.table-danger tbody + tbody { - border-color: #e47a7a; -} - -.table-hover .table-danger:hover { - background-color: #eda3a3; -} - -.table-hover .table-danger:hover > td, -.table-hover .table-danger:hover > th { - background-color: #eda3a3; -} - -.table-light, -.table-light > th, -.table-light > td { - background-color: #c1c1c1; -} - -.table-light th, -.table-light td, -.table-light thead th, -.table-light tbody + tbody { - border-color: #8c8c8c; -} - -.table-hover .table-light:hover { - background-color: #b4b4b4; -} - -.table-hover .table-light:hover > td, -.table-hover .table-light:hover > th { - background-color: #b4b4b4; -} - -.table-dark, -.table-dark > th, -.table-dark > td { - background-color: #e8e9e8; -} - -.table-dark th, -.table-dark td, -.table-dark thead th, -.table-dark tbody + tbody { - border-color: #d4d5d5; -} - -.table-hover .table-dark:hover { - background-color: #dbdddb; -} - -.table-hover .table-dark:hover > td, -.table-hover .table-dark:hover > th { - background-color: #dbdddb; -} - -.table-active, -.table-active > th, -.table-active > td { - background-color: rgba(255, 255, 255, 0.075); -} - -.table-hover .table-active:hover { - background-color: rgba(242, 242, 242, 0.075); -} - -.table-hover .table-active:hover > td, -.table-hover .table-active:hover > th { - background-color: rgba(242, 242, 242, 0.075); -} - -.table .thead-dark th { - color: #060606; - background-color: #888; - border-color: #757575; -} - -.table .thead-light th { - color: #282828; - background-color: #e9ecef; - border-color: #282828; -} - -.table-dark { - color: #060606; - background-color: #888; -} - -.table-dark th, -.table-dark td, -.table-dark thead th { - border-color: #757575; -} - -.table-dark.table-bordered { - border: 0; -} - -.table-dark.table-striped tbody tr:nth-of-type(odd) { - background-color: rgba(255, 255, 255, 0.05); -} - -.table-dark.table-hover tbody tr:hover { - color: #060606; - background-color: rgba(255, 255, 255, 0.075); -} - -@media (max-width: 575.98px) { - .table-responsive-sm { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-sm > .table-bordered { - border: 0; - } -} - -@media (max-width: 767.98px) { - .table-responsive-md { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-md > .table-bordered { - border: 0; - } -} - -@media (max-width: 991.98px) { - .table-responsive-lg { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-lg > .table-bordered { - border: 0; - } -} - -@media (max-width: 1199.98px) { - .table-responsive-xl { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-xl > .table-bordered { - border: 0; - } -} - -.table-responsive { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; -} - -.table-responsive > .table-bordered { - border: 0; -} - -.form-control { - display: block; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - padding: 0.375rem 1rem; - font-size: 0.875rem; - font-weight: 400; - line-height: 1.5; - color: #282828; - background-color: #fff; - background-clip: padding-box; - border: 1px solid transparent; - border-radius: 0.25rem; - -webkit-transition: border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .form-control { - -webkit-transition: none; - transition: none; - } -} - -.form-control::-ms-expand { - background-color: transparent; - border: 0; -} - -.form-control:focus { - color: #282828; - background-color: #fff; - border-color: #95cfeb; - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); - box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); -} - -.form-control::-webkit-input-placeholder { - color: #555; - opacity: 1; -} - -.form-control::-ms-input-placeholder { - color: #555; - opacity: 1; -} - -.form-control::placeholder { - color: #555; - opacity: 1; -} - -.form-control:disabled, .form-control[readonly] { - background-color: #ADAFAE; - opacity: 1; -} - -select.form-control:focus::-ms-value { - color: #282828; - background-color: #fff; -} - -.form-control-file, -.form-control-range { - display: block; - width: 100%; -} - -.col-form-label { - padding-top: calc(0.375rem + 1px); - padding-bottom: calc(0.375rem + 1px); - margin-bottom: 0; - font-size: inherit; - line-height: 1.5; -} - -.col-form-label-lg { - padding-top: calc(0.5rem + 1px); - padding-bottom: calc(0.5rem + 1px); - font-size: 1.09375rem; - line-height: 1.5; -} - -.col-form-label-sm { - padding-top: calc(0.25rem + 1px); - padding-bottom: calc(0.25rem + 1px); - font-size: 0.765625rem; - line-height: 1.5; -} - -.form-control-plaintext { - display: block; - width: 100%; - padding-top: 0.375rem; - padding-bottom: 0.375rem; - margin-bottom: 0; - line-height: 1.5; - color: #888; - background-color: transparent; - border: solid transparent; - border-width: 1px 0; -} - -.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { - padding-right: 0; - padding-left: 0; -} - -.form-control-sm { - height: calc(1.5em + 0.5rem + 2px); - padding: 0.25rem 0.5rem; - font-size: 0.765625rem; - line-height: 1.5; - border-radius: 0.2rem; -} - -.form-control-lg { - height: calc(1.5em + 1rem + 2px); - padding: 0.5rem 1rem; - font-size: 1.09375rem; - line-height: 1.5; - border-radius: 0.3rem; -} - -select.form-control[size], select.form-control[multiple] { - height: auto; -} - -textarea.form-control { - height: auto; -} - -.form-group { - margin-bottom: 1rem; -} - -.form-text { - display: block; - margin-top: 0.25rem; -} - -.form-row { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - margin-right: -5px; - margin-left: -5px; -} - -.form-row > .col, -.form-row > [class*="col-"] { - padding-right: 5px; - padding-left: 5px; -} - -.form-check { - position: relative; - display: block; - padding-left: 1.25rem; -} - -.form-check-input { - position: absolute; - margin-top: 0.3rem; - margin-left: -1.25rem; -} - -.form-check-input:disabled ~ .form-check-label { - color: #555; -} - -.form-check-label { - margin-bottom: 0; -} - -.form-check-inline { - display: -webkit-inline-box; - display: -ms-inline-flexbox; - display: inline-flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - padding-left: 0; - margin-right: 0.75rem; -} - -.form-check-inline .form-check-input { - position: static; - margin-top: 0; - margin-right: 0.3125rem; - margin-left: 0; -} - -.valid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 80%; - color: #77B300; -} - -.valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: .1rem; - font-size: 0.765625rem; - line-height: 1.5; - color: #fff; - background-color: #77b300; - border-radius: 0.25rem; -} - -.was-validated .form-control:valid, .form-control.is-valid { - border-color: #77B300; - padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2377B300' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: center right calc(0.375em + 0.1875rem); - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} - -.was-validated .form-control:valid:focus, .form-control.is-valid:focus { - border-color: #77B300; - -webkit-box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.25); - box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.25); -} - -.was-validated .form-control:valid ~ .valid-feedback, -.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback, -.form-control.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated textarea.form-control:valid, textarea.form-control.is-valid { - padding-right: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); -} - -.was-validated .custom-select:valid, .custom-select.is-valid { - border-color: #77B300; - padding-right: calc((1em + 0.75rem) * 3 / 4 + 2rem); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23222' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 1rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2377B300' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 2rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} - -.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { - border-color: #77B300; - -webkit-box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.25); - box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.25); -} - -.was-validated .custom-select:valid ~ .valid-feedback, -.was-validated .custom-select:valid ~ .valid-tooltip, .custom-select.is-valid ~ .valid-feedback, -.custom-select.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .form-control-file:valid ~ .valid-feedback, -.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback, -.form-control-file.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { - color: #77B300; -} - -.was-validated .form-check-input:valid ~ .valid-feedback, -.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, -.form-check-input.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { - color: #77B300; -} - -.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { - border-color: #77B300; -} - -.was-validated .custom-control-input:valid ~ .valid-feedback, -.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback, -.custom-control-input.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { - border-color: #99e600; - background-color: #99e600; -} - -.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { - -webkit-box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.25); - box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.25); -} - -.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before { - border-color: #77B300; -} - -.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { - border-color: #77B300; -} - -.was-validated .custom-file-input:valid ~ .valid-feedback, -.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback, -.custom-file-input.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { - border-color: #77B300; - -webkit-box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.25); - box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.25); -} - -.invalid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 80%; - color: #CC0000; -} - -.invalid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: .1rem; - font-size: 0.765625rem; - line-height: 1.5; - color: #fff; - background-color: #cc0000; - border-radius: 0.25rem; -} - -.was-validated .form-control:invalid, .form-control.is-invalid { - border-color: #CC0000; - padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23CC0000' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23CC0000' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E"); - background-repeat: no-repeat; - background-position: center right calc(0.375em + 0.1875rem); - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} - -.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { - border-color: #CC0000; - -webkit-box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.25); - box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.25); -} - -.was-validated .form-control:invalid ~ .invalid-feedback, -.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback, -.form-control.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { - padding-right: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); -} - -.was-validated .custom-select:invalid, .custom-select.is-invalid { - border-color: #CC0000; - padding-right: calc((1em + 0.75rem) * 3 / 4 + 2rem); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23222' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 1rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23CC0000' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23CC0000' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 2rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} - -.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { - border-color: #CC0000; - -webkit-box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.25); - box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.25); -} - -.was-validated .custom-select:invalid ~ .invalid-feedback, -.was-validated .custom-select:invalid ~ .invalid-tooltip, .custom-select.is-invalid ~ .invalid-feedback, -.custom-select.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .form-control-file:invalid ~ .invalid-feedback, -.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback, -.form-control-file.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { - color: #CC0000; -} - -.was-validated .form-check-input:invalid ~ .invalid-feedback, -.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, -.form-check-input.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { - color: #CC0000; -} - -.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { - border-color: #CC0000; -} - -.was-validated .custom-control-input:invalid ~ .invalid-feedback, -.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback, -.custom-control-input.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { - border-color: red; - background-color: red; -} - -.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { - -webkit-box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.25); - box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.25); -} - -.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before { - border-color: #CC0000; -} - -.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { - border-color: #CC0000; -} - -.was-validated .custom-file-input:invalid ~ .invalid-feedback, -.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback, -.custom-file-input.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { - border-color: #CC0000; - -webkit-box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.25); - box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.25); -} - -.form-inline { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.form-inline .form-check { - width: 100%; -} - -@media (min-width: 576px) { - .form-inline label { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - margin-bottom: 0; - } - .form-inline .form-group { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - margin-bottom: 0; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-plaintext { - display: inline-block; - } - .form-inline .input-group, - .form-inline .custom-select { - width: auto; - } - .form-inline .form-check { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - width: auto; - padding-left: 0; - } - .form-inline .form-check-input { - position: relative; - -ms-flex-negative: 0; - flex-shrink: 0; - margin-top: 0; - margin-right: 0.25rem; - margin-left: 0; - } - .form-inline .custom-control { - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - } - .form-inline .custom-control-label { - margin-bottom: 0; - } -} - -.btn { - display: inline-block; - font-weight: 400; - color: #888; - text-align: center; - vertical-align: middle; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-color: transparent; - border: 1px solid transparent; - padding: 0.375rem 1rem; - font-size: 0.875rem; - line-height: 1.5; - border-radius: 0.25rem; - -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .btn { - -webkit-transition: none; - transition: none; - } -} - -.btn:hover { - color: #888; - text-decoration: none; -} - -.btn:focus, .btn.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); - box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); -} - -.btn.disabled, .btn:disabled { - opacity: 0.65; -} - -a.btn.disabled, -fieldset:disabled a.btn { - pointer-events: none; -} - -.btn-primary { - color: #fff; - background-color: #2A9FD6; - border-color: #2A9FD6; -} - -.btn-primary:hover { - color: #fff; - background-color: #2387b7; - border-color: #2180ac; -} - -.btn-primary:focus, .btn-primary.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(74, 173, 220, 0.5); - box-shadow: 0 0 0 0.2rem rgba(74, 173, 220, 0.5); -} - -.btn-primary.disabled, .btn-primary:disabled { - color: #fff; - background-color: #2A9FD6; - border-color: #2A9FD6; -} - -.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, -.show > .btn-primary.dropdown-toggle { - color: #fff; - background-color: #2180ac; - border-color: #1f78a1; -} - -.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, -.show > .btn-primary.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(74, 173, 220, 0.5); - box-shadow: 0 0 0 0.2rem rgba(74, 173, 220, 0.5); -} - -.btn-secondary { - color: #fff; - background-color: #555; - border-color: #555; -} - -.btn-secondary:hover { - color: #fff; - background-color: #424242; - border-color: #3c3b3b; -} - -.btn-secondary:focus, .btn-secondary.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(111, 111, 111, 0.5); - box-shadow: 0 0 0 0.2rem rgba(111, 111, 111, 0.5); -} - -.btn-secondary.disabled, .btn-secondary:disabled { - color: #fff; - background-color: #555; - border-color: #555; -} - -.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, -.show > .btn-secondary.dropdown-toggle { - color: #fff; - background-color: #3c3b3b; - border-color: #353535; -} - -.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, -.show > .btn-secondary.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(111, 111, 111, 0.5); - box-shadow: 0 0 0 0.2rem rgba(111, 111, 111, 0.5); -} - -.btn-success { - color: #fff; - background-color: #77B300; - border-color: #77B300; -} - -.btn-success:hover { - color: #fff; - background-color: #5e8d00; - border-color: #558000; -} - -.btn-success:focus, .btn-success.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(139, 190, 38, 0.5); - box-shadow: 0 0 0 0.2rem rgba(139, 190, 38, 0.5); -} - -.btn-success.disabled, .btn-success:disabled { - color: #fff; - background-color: #77B300; - border-color: #77B300; -} - -.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, -.show > .btn-success.dropdown-toggle { - color: #fff; - background-color: #558000; - border-color: #4d7300; -} - -.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, -.show > .btn-success.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(139, 190, 38, 0.5); - box-shadow: 0 0 0 0.2rem rgba(139, 190, 38, 0.5); -} - -.btn-info { - color: #fff; - background-color: #9933CC; - border-color: #9933CC; -} - -.btn-info:hover { - color: #fff; - background-color: #822bad; - border-color: #7a29a3; -} - -.btn-info:focus, .btn-info.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(168, 82, 212, 0.5); - box-shadow: 0 0 0 0.2rem rgba(168, 82, 212, 0.5); -} - -.btn-info.disabled, .btn-info:disabled { - color: #fff; - background-color: #9933CC; - border-color: #9933CC; -} - -.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, -.show > .btn-info.dropdown-toggle { - color: #fff; - background-color: #7a29a3; - border-color: #732699; -} - -.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, -.show > .btn-info.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(168, 82, 212, 0.5); - box-shadow: 0 0 0 0.2rem rgba(168, 82, 212, 0.5); -} - -.btn-warning { - color: #fff; - background-color: #FF8800; - border-color: #FF8800; -} - -.btn-warning:hover { - color: #fff; - background-color: #d97400; - border-color: #cc6d00; -} - -.btn-warning:focus, .btn-warning.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 154, 38, 0.5); - box-shadow: 0 0 0 0.2rem rgba(255, 154, 38, 0.5); -} - -.btn-warning.disabled, .btn-warning:disabled { - color: #fff; - background-color: #FF8800; - border-color: #FF8800; -} - -.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, -.show > .btn-warning.dropdown-toggle { - color: #fff; - background-color: #cc6d00; - border-color: #bf6600; -} - -.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, -.show > .btn-warning.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 154, 38, 0.5); - box-shadow: 0 0 0 0.2rem rgba(255, 154, 38, 0.5); -} - -.btn-danger { - color: #fff; - background-color: #CC0000; - border-color: #CC0000; -} - -.btn-danger:hover { - color: #fff; - background-color: #a60000; - border-color: #990000; -} - -.btn-danger:focus, .btn-danger.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(212, 38, 38, 0.5); - box-shadow: 0 0 0 0.2rem rgba(212, 38, 38, 0.5); -} - -.btn-danger.disabled, .btn-danger:disabled { - color: #fff; - background-color: #CC0000; - border-color: #CC0000; -} - -.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, -.show > .btn-danger.dropdown-toggle { - color: #fff; - background-color: #990000; - border-color: #8c0000; -} - -.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, -.show > .btn-danger.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(212, 38, 38, 0.5); - box-shadow: 0 0 0 0.2rem rgba(212, 38, 38, 0.5); -} - -.btn-light { - color: #fff; - background-color: #222; - border-color: #222; -} - -.btn-light:hover { - color: #fff; - background-color: #0f0f0f; - border-color: #090808; -} - -.btn-light:focus, .btn-light.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(67, 67, 67, 0.5); - box-shadow: 0 0 0 0.2rem rgba(67, 67, 67, 0.5); -} - -.btn-light.disabled, .btn-light:disabled { - color: #fff; - background-color: #222; - border-color: #222; -} - -.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, -.show > .btn-light.dropdown-toggle { - color: #fff; - background-color: #090808; - border-color: #020202; -} - -.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, -.show > .btn-light.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(67, 67, 67, 0.5); - box-shadow: 0 0 0 0.2rem rgba(67, 67, 67, 0.5); -} - -.btn-dark { - color: #fff; - background-color: #ADAFAE; - border-color: #ADAFAE; -} - -.btn-dark:hover { - color: #fff; - background-color: #9a9c9b; - border-color: #939695; -} - -.btn-dark:focus, .btn-dark.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(185, 187, 186, 0.5); - box-shadow: 0 0 0 0.2rem rgba(185, 187, 186, 0.5); -} - -.btn-dark.disabled, .btn-dark:disabled { - color: #fff; - background-color: #ADAFAE; - border-color: #ADAFAE; -} - -.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, -.show > .btn-dark.dropdown-toggle { - color: #fff; - background-color: #939695; - border-color: #8d908e; -} - -.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, -.show > .btn-dark.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(185, 187, 186, 0.5); - box-shadow: 0 0 0 0.2rem rgba(185, 187, 186, 0.5); -} - -.btn-outline-primary { - color: #2A9FD6; - border-color: #2A9FD6; -} - -.btn-outline-primary:hover { - color: #fff; - background-color: #2A9FD6; - border-color: #2A9FD6; -} - -.btn-outline-primary:focus, .btn-outline-primary.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.5); - box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.5); -} - -.btn-outline-primary.disabled, .btn-outline-primary:disabled { - color: #2A9FD6; - background-color: transparent; -} - -.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, -.show > .btn-outline-primary.dropdown-toggle { - color: #fff; - background-color: #2A9FD6; - border-color: #2A9FD6; -} - -.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-primary.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.5); - box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.5); -} - -.btn-outline-secondary { - color: #555; - border-color: #555; -} - -.btn-outline-secondary:hover { - color: #fff; - background-color: #555; - border-color: #555; -} - -.btn-outline-secondary:focus, .btn-outline-secondary.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(85, 85, 85, 0.5); - box-shadow: 0 0 0 0.2rem rgba(85, 85, 85, 0.5); -} - -.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { - color: #555; - background-color: transparent; -} - -.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, -.show > .btn-outline-secondary.dropdown-toggle { - color: #fff; - background-color: #555; - border-color: #555; -} - -.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-secondary.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(85, 85, 85, 0.5); - box-shadow: 0 0 0 0.2rem rgba(85, 85, 85, 0.5); -} - -.btn-outline-success { - color: #77B300; - border-color: #77B300; -} - -.btn-outline-success:hover { - color: #fff; - background-color: #77B300; - border-color: #77B300; -} - -.btn-outline-success:focus, .btn-outline-success.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.5); - box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.5); -} - -.btn-outline-success.disabled, .btn-outline-success:disabled { - color: #77B300; - background-color: transparent; -} - -.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, -.show > .btn-outline-success.dropdown-toggle { - color: #fff; - background-color: #77B300; - border-color: #77B300; -} - -.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-success.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.5); - box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.5); -} - -.btn-outline-info { - color: #9933CC; - border-color: #9933CC; -} - -.btn-outline-info:hover { - color: #fff; - background-color: #9933CC; - border-color: #9933CC; -} - -.btn-outline-info:focus, .btn-outline-info.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(153, 51, 204, 0.5); - box-shadow: 0 0 0 0.2rem rgba(153, 51, 204, 0.5); -} - -.btn-outline-info.disabled, .btn-outline-info:disabled { - color: #9933CC; - background-color: transparent; -} - -.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, -.show > .btn-outline-info.dropdown-toggle { - color: #fff; - background-color: #9933CC; - border-color: #9933CC; -} - -.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-info.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(153, 51, 204, 0.5); - box-shadow: 0 0 0 0.2rem rgba(153, 51, 204, 0.5); -} - -.btn-outline-warning { - color: #FF8800; - border-color: #FF8800; -} - -.btn-outline-warning:hover { - color: #fff; - background-color: #FF8800; - border-color: #FF8800; -} - -.btn-outline-warning:focus, .btn-outline-warning.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 136, 0, 0.5); - box-shadow: 0 0 0 0.2rem rgba(255, 136, 0, 0.5); -} - -.btn-outline-warning.disabled, .btn-outline-warning:disabled { - color: #FF8800; - background-color: transparent; -} - -.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, -.show > .btn-outline-warning.dropdown-toggle { - color: #fff; - background-color: #FF8800; - border-color: #FF8800; -} - -.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-warning.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 136, 0, 0.5); - box-shadow: 0 0 0 0.2rem rgba(255, 136, 0, 0.5); -} - -.btn-outline-danger { - color: #CC0000; - border-color: #CC0000; -} - -.btn-outline-danger:hover { - color: #fff; - background-color: #CC0000; - border-color: #CC0000; -} - -.btn-outline-danger:focus, .btn-outline-danger.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.5); - box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.5); -} - -.btn-outline-danger.disabled, .btn-outline-danger:disabled { - color: #CC0000; - background-color: transparent; -} - -.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, -.show > .btn-outline-danger.dropdown-toggle { - color: #fff; - background-color: #CC0000; - border-color: #CC0000; -} - -.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-danger.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.5); - box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.5); -} - -.btn-outline-light { - color: #222; - border-color: #222; -} - -.btn-outline-light:hover { - color: #fff; - background-color: #222; - border-color: #222; -} - -.btn-outline-light:focus, .btn-outline-light.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(34, 34, 34, 0.5); - box-shadow: 0 0 0 0.2rem rgba(34, 34, 34, 0.5); -} - -.btn-outline-light.disabled, .btn-outline-light:disabled { - color: #222; - background-color: transparent; -} - -.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, -.show > .btn-outline-light.dropdown-toggle { - color: #fff; - background-color: #222; - border-color: #222; -} - -.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-light.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(34, 34, 34, 0.5); - box-shadow: 0 0 0 0.2rem rgba(34, 34, 34, 0.5); -} - -.btn-outline-dark { - color: #ADAFAE; - border-color: #ADAFAE; -} - -.btn-outline-dark:hover { - color: #fff; - background-color: #ADAFAE; - border-color: #ADAFAE; -} - -.btn-outline-dark:focus, .btn-outline-dark.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(173, 175, 174, 0.5); - box-shadow: 0 0 0 0.2rem rgba(173, 175, 174, 0.5); -} - -.btn-outline-dark.disabled, .btn-outline-dark:disabled { - color: #ADAFAE; - background-color: transparent; -} - -.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, -.show > .btn-outline-dark.dropdown-toggle { - color: #fff; - background-color: #ADAFAE; - border-color: #ADAFAE; -} - -.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-dark.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(173, 175, 174, 0.5); - box-shadow: 0 0 0 0.2rem rgba(173, 175, 174, 0.5); -} - -.btn-link { - font-weight: 400; - color: #2A9FD6; - text-decoration: none; -} - -.btn-link:hover { - color: #1d7097; - text-decoration: underline; -} - -.btn-link:focus, .btn-link.focus { - text-decoration: underline; - -webkit-box-shadow: none; - box-shadow: none; -} - -.btn-link:disabled, .btn-link.disabled { - color: #555; - pointer-events: none; -} - -.btn-lg, .btn-group-lg > .btn { - padding: 0.5rem 1rem; - font-size: 1.09375rem; - line-height: 1.5; - border-radius: 0.3rem; -} - -.btn-sm, .btn-group-sm > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.765625rem; - line-height: 1.5; - border-radius: 0.2rem; -} - -.btn-block { - display: block; - width: 100%; -} - -.btn-block + .btn-block { - margin-top: 0.5rem; -} - -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} - -.fade { - -webkit-transition: opacity 0.15s linear; - transition: opacity 0.15s linear; -} - -@media (prefers-reduced-motion: reduce) { - .fade { - -webkit-transition: none; - transition: none; - } -} - -.fade:not(.show) { - opacity: 0; -} - -.collapse:not(.show) { - display: none; -} - -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition: height 0.35s ease; - transition: height 0.35s ease; -} - -@media (prefers-reduced-motion: reduce) { - .collapsing { - -webkit-transition: none; - transition: none; - } -} - -.dropup, -.dropright, -.dropdown, -.dropleft { - position: relative; -} - -.dropdown-toggle { - white-space: nowrap; -} - -.dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid; - border-right: 0.3em solid transparent; - border-bottom: 0; - border-left: 0.3em solid transparent; -} - -.dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 10rem; - padding: 0.5rem 0; - margin: 0.125rem 0 0; - font-size: 0.875rem; - color: #888; - text-align: left; - list-style: none; - background-color: #282828; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 0.25rem; -} - -.dropdown-menu-left { - right: auto; - left: 0; -} - -.dropdown-menu-right { - right: 0; - left: auto; -} - -@media (min-width: 576px) { - .dropdown-menu-sm-left { - right: auto; - left: 0; - } - .dropdown-menu-sm-right { - right: 0; - left: auto; - } -} - -@media (min-width: 768px) { - .dropdown-menu-md-left { - right: auto; - left: 0; - } - .dropdown-menu-md-right { - right: 0; - left: auto; - } -} - -@media (min-width: 992px) { - .dropdown-menu-lg-left { - right: auto; - left: 0; - } - .dropdown-menu-lg-right { - right: 0; - left: auto; - } -} - -@media (min-width: 1200px) { - .dropdown-menu-xl-left { - right: auto; - left: 0; - } - .dropdown-menu-xl-right { - right: 0; - left: auto; - } -} - -.dropup .dropdown-menu { - top: auto; - bottom: 100%; - margin-top: 0; - margin-bottom: 0.125rem; -} - -.dropup .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0; - border-right: 0.3em solid transparent; - border-bottom: 0.3em solid; - border-left: 0.3em solid transparent; -} - -.dropup .dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropright .dropdown-menu { - top: 0; - right: auto; - left: 100%; - margin-top: 0; - margin-left: 0.125rem; -} - -.dropright .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-right: 0; - border-bottom: 0.3em solid transparent; - border-left: 0.3em solid; -} - -.dropright .dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropright .dropdown-toggle::after { - vertical-align: 0; -} - -.dropleft .dropdown-menu { - top: 0; - right: 100%; - left: auto; - margin-top: 0; - margin-right: 0.125rem; -} - -.dropleft .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; -} - -.dropleft .dropdown-toggle::after { - display: none; -} - -.dropleft .dropdown-toggle::before { - display: inline-block; - margin-right: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-right: 0.3em solid; - border-bottom: 0.3em solid transparent; -} - -.dropleft .dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropleft .dropdown-toggle::before { - vertical-align: 0; -} - -.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] { - right: auto; - bottom: auto; -} - -.dropdown-divider { - height: 0; - margin: 0.5rem 0; - overflow: hidden; - border-top: 1px solid #222; -} - -.dropdown-item { - display: block; - width: 100%; - padding: 0.25rem 1.5rem; - clear: both; - font-weight: 400; - color: #fff; - text-align: inherit; - white-space: nowrap; - background-color: transparent; - border: 0; -} - -.dropdown-item:hover, .dropdown-item:focus { - color: #fff; - text-decoration: none; - background-color: #2A9FD6; -} - -.dropdown-item.active, .dropdown-item:active { - color: #fff; - text-decoration: none; - background-color: #2A9FD6; -} - -.dropdown-item.disabled, .dropdown-item:disabled { - color: #555; - pointer-events: none; - background-color: transparent; -} - -.dropdown-menu.show { - display: block; -} - -.dropdown-header { - display: block; - padding: 0.5rem 1.5rem; - margin-bottom: 0; - font-size: 0.765625rem; - color: #555; - white-space: nowrap; -} - -.dropdown-item-text { - display: block; - padding: 0.25rem 1.5rem; - color: #fff; -} - -.btn-group, -.btn-group-vertical { - position: relative; - display: -webkit-inline-box; - display: -ms-inline-flexbox; - display: inline-flex; - vertical-align: middle; -} - -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; -} - -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover { - z-index: 1; -} - -.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, -.btn-group-vertical > .btn:focus, -.btn-group-vertical > .btn:active, -.btn-group-vertical > .btn.active { - z-index: 1; -} - -.btn-toolbar { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.btn-toolbar .input-group { - width: auto; -} - -.btn-group > .btn:not(:first-child), -.btn-group > .btn-group:not(:first-child) { - margin-left: -1px; -} - -.btn-group > .btn:not(:last-child):not(.dropdown-toggle), -.btn-group > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.btn-group > .btn:not(:first-child), -.btn-group > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.dropdown-toggle-split { - padding-right: 0.75rem; - padding-left: 0.75rem; -} - -.dropdown-toggle-split::after, -.dropup .dropdown-toggle-split::after, -.dropright .dropdown-toggle-split::after { - margin-left: 0; -} - -.dropleft .dropdown-toggle-split::before { - margin-right: 0; -} - -.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { - padding-right: 0.375rem; - padding-left: 0.375rem; -} - -.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { - padding-right: 0.75rem; - padding-left: 0.75rem; -} - -.btn-group-vertical { - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: flex-start; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; -} - -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group { - width: 100%; -} - -.btn-group-vertical > .btn:not(:first-child), -.btn-group-vertical > .btn-group:not(:first-child) { - margin-top: -1px; -} - -.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), -.btn-group-vertical > .btn-group:not(:last-child) > .btn { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} - -.btn-group-vertical > .btn:not(:first-child), -.btn-group-vertical > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.btn-group-toggle > .btn, -.btn-group-toggle > .btn-group > .btn { - margin-bottom: 0; -} - -.btn-group-toggle > .btn input[type="radio"], -.btn-group-toggle > .btn input[type="checkbox"], -.btn-group-toggle > .btn-group > .btn input[type="radio"], -.btn-group-toggle > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} - -.input-group { - position: relative; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-align: stretch; - -ms-flex-align: stretch; - align-items: stretch; - width: 100%; -} - -.input-group > .form-control, -.input-group > .form-control-plaintext, -.input-group > .custom-select, -.input-group > .custom-file { - position: relative; - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - width: 1%; - margin-bottom: 0; -} - -.input-group > .form-control + .form-control, -.input-group > .form-control + .custom-select, -.input-group > .form-control + .custom-file, -.input-group > .form-control-plaintext + .form-control, -.input-group > .form-control-plaintext + .custom-select, -.input-group > .form-control-plaintext + .custom-file, -.input-group > .custom-select + .form-control, -.input-group > .custom-select + .custom-select, -.input-group > .custom-select + .custom-file, -.input-group > .custom-file + .form-control, -.input-group > .custom-file + .custom-select, -.input-group > .custom-file + .custom-file { - margin-left: -1px; -} - -.input-group > .form-control:focus, -.input-group > .custom-select:focus, -.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label { - z-index: 3; -} - -.input-group > .custom-file .custom-file-input:focus { - z-index: 4; -} - -.input-group > .form-control:not(:last-child), -.input-group > .custom-select:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.input-group > .form-control:not(:first-child), -.input-group > .custom-select:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.input-group > .custom-file { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.input-group > .custom-file:not(:last-child) .custom-file-label, -.input-group > .custom-file:not(:last-child) .custom-file-label::after { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.input-group > .custom-file:not(:first-child) .custom-file-label { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.input-group-prepend, -.input-group-append { - display: -webkit-box; - display: -ms-flexbox; - display: flex; -} - -.input-group-prepend .btn, -.input-group-append .btn { - position: relative; - z-index: 2; -} - -.input-group-prepend .btn:focus, -.input-group-append .btn:focus { - z-index: 3; -} - -.input-group-prepend .btn + .btn, -.input-group-prepend .btn + .input-group-text, -.input-group-prepend .input-group-text + .input-group-text, -.input-group-prepend .input-group-text + .btn, -.input-group-append .btn + .btn, -.input-group-append .btn + .input-group-text, -.input-group-append .input-group-text + .input-group-text, -.input-group-append .input-group-text + .btn { - margin-left: -1px; -} - -.input-group-prepend { - margin-right: -1px; -} - -.input-group-append { - margin-left: -1px; -} - -.input-group-text { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - padding: 0.375rem 1rem; - margin-bottom: 0; - font-size: 0.875rem; - font-weight: 400; - line-height: 1.5; - color: #fff; - text-align: center; - white-space: nowrap; - background-color: #282828; - border: 1px solid transparent; - border-radius: 0.25rem; -} - -.input-group-text input[type="radio"], -.input-group-text input[type="checkbox"] { - margin-top: 0; -} - -.input-group-lg > .form-control:not(textarea), -.input-group-lg > .custom-select { - height: calc(1.5em + 1rem + 2px); -} - -.input-group-lg > .form-control, -.input-group-lg > .custom-select, -.input-group-lg > .input-group-prepend > .input-group-text, -.input-group-lg > .input-group-append > .input-group-text, -.input-group-lg > .input-group-prepend > .btn, -.input-group-lg > .input-group-append > .btn { - padding: 0.5rem 1rem; - font-size: 1.09375rem; - line-height: 1.5; - border-radius: 0.3rem; -} - -.input-group-sm > .form-control:not(textarea), -.input-group-sm > .custom-select { - height: calc(1.5em + 0.5rem + 2px); -} - -.input-group-sm > .form-control, -.input-group-sm > .custom-select, -.input-group-sm > .input-group-prepend > .input-group-text, -.input-group-sm > .input-group-append > .input-group-text, -.input-group-sm > .input-group-prepend > .btn, -.input-group-sm > .input-group-append > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.765625rem; - line-height: 1.5; - border-radius: 0.2rem; -} - -.input-group-lg > .custom-select, -.input-group-sm > .custom-select { - padding-right: 2rem; -} - -.input-group > .input-group-prepend > .btn, -.input-group > .input-group-prepend > .input-group-text, -.input-group > .input-group-append:not(:last-child) > .btn, -.input-group > .input-group-append:not(:last-child) > .input-group-text, -.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.input-group > .input-group-append > .btn, -.input-group > .input-group-append > .input-group-text, -.input-group > .input-group-prepend:not(:first-child) > .btn, -.input-group > .input-group-prepend:not(:first-child) > .input-group-text, -.input-group > .input-group-prepend:first-child > .btn:not(:first-child), -.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.custom-control { - position: relative; - display: block; - min-height: 1.3125rem; - padding-left: 1.5rem; -} - -.custom-control-inline { - display: -webkit-inline-box; - display: -ms-inline-flexbox; - display: inline-flex; - margin-right: 1rem; -} - -.custom-control-input { - position: absolute; - z-index: -1; - opacity: 0; -} - -.custom-control-input:checked ~ .custom-control-label::before { - color: #fff; - border-color: #2A9FD6; - background-color: #2A9FD6; -} - -.custom-control-input:focus ~ .custom-control-label::before { - -webkit-box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); - box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); -} - -.custom-control-input:focus:not(:checked) ~ .custom-control-label::before { - border-color: #95cfeb; -} - -.custom-control-input:not(:disabled):active ~ .custom-control-label::before { - color: #fff; - background-color: #c0e2f3; - border-color: #c0e2f3; -} - -.custom-control-input:disabled ~ .custom-control-label { - color: #555; -} - -.custom-control-input:disabled ~ .custom-control-label::before { - background-color: #ADAFAE; -} - -.custom-control-label { - position: relative; - margin-bottom: 0; - vertical-align: top; -} - -.custom-control-label::before { - position: absolute; - top: 0.15625rem; - left: -1.5rem; - display: block; - width: 1rem; - height: 1rem; - pointer-events: none; - content: ""; - background-color: #fff; - border: #888 solid 1px; -} - -.custom-control-label::after { - position: absolute; - top: 0.15625rem; - left: -1.5rem; - display: block; - width: 1rem; - height: 1rem; - content: ""; - background: no-repeat 50% / 50% 50%; -} - -.custom-checkbox .custom-control-label::before { - border-radius: 0.25rem; -} - -.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e"); -} - -.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { - border-color: #2A9FD6; - background-color: #2A9FD6; -} - -.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); -} - -.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { - background-color: rgba(42, 159, 214, 0.5); -} - -.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { - background-color: rgba(42, 159, 214, 0.5); -} - -.custom-radio .custom-control-label::before { - border-radius: 50%; -} - -.custom-radio .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); -} - -.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { - background-color: rgba(42, 159, 214, 0.5); -} - -.custom-switch { - padding-left: 2.25rem; -} - -.custom-switch .custom-control-label::before { - left: -2.25rem; - width: 1.75rem; - pointer-events: all; - border-radius: 0.5rem; -} - -.custom-switch .custom-control-label::after { - top: calc(0.15625rem + 2px); - left: calc(-2.25rem + 2px); - width: calc(1rem - 4px); - height: calc(1rem - 4px); - background-color: #888; - border-radius: 0.5rem; - -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .custom-switch .custom-control-label::after { - -webkit-transition: none; - transition: none; - } -} - -.custom-switch .custom-control-input:checked ~ .custom-control-label::after { - background-color: #fff; - -webkit-transform: translateX(0.75rem); - transform: translateX(0.75rem); -} - -.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before { - background-color: rgba(42, 159, 214, 0.5); -} - -.custom-select { - display: inline-block; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - padding: 0.375rem 2rem 0.375rem 1rem; - font-size: 0.875rem; - font-weight: 400; - line-height: 1.5; - color: #282828; - vertical-align: middle; - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23222' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 1rem center/8px 10px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 0.25rem; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -.custom-select:focus { - border-color: #95cfeb; - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); - box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); -} - -.custom-select:focus::-ms-value { - color: #282828; - background-color: #fff; -} - -.custom-select[multiple], .custom-select[size]:not([size="1"]) { - height: auto; - padding-right: 1rem; - background-image: none; -} - -.custom-select:disabled { - color: #555; - background-color: #e9ecef; -} - -.custom-select::-ms-expand { - display: none; -} - -.custom-select-sm { - height: calc(1.5em + 0.5rem + 2px); - padding-top: 0.25rem; - padding-bottom: 0.25rem; - padding-left: 0.5rem; - font-size: 0.765625rem; -} - -.custom-select-lg { - height: calc(1.5em + 1rem + 2px); - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-left: 1rem; - font-size: 1.09375rem; -} - -.custom-file { - position: relative; - display: inline-block; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - margin-bottom: 0; -} - -.custom-file-input { - position: relative; - z-index: 2; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - margin: 0; - opacity: 0; -} - -.custom-file-input:focus ~ .custom-file-label { - border-color: #95cfeb; - -webkit-box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); - box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); -} - -.custom-file-input:disabled ~ .custom-file-label { - background-color: #ADAFAE; -} - -.custom-file-input:lang(en) ~ .custom-file-label::after { - content: "Browse"; -} - -.custom-file-input ~ .custom-file-label[data-browse]::after { - content: attr(data-browse); -} - -.custom-file-label { - position: absolute; - top: 0; - right: 0; - left: 0; - z-index: 1; - height: calc(1.5em + 0.75rem + 2px); - padding: 0.375rem 1rem; - font-weight: 400; - line-height: 1.5; - color: #fff; - background-color: #fff; - border: 1px solid #282828; - border-radius: 0.25rem; -} - -.custom-file-label::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - z-index: 3; - display: block; - height: calc(1.5em + 0.75rem); - padding: 0.375rem 1rem; - line-height: 1.5; - color: #fff; - content: "Browse"; - background-color: #282828; - border-left: inherit; - border-radius: 0 0.25rem 0.25rem 0; -} - -.custom-range { - width: 100%; - height: calc(1rem + 0.4rem); - padding: 0; - background-color: transparent; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -.custom-range:focus { - outline: none; -} - -.custom-range:focus::-webkit-slider-thumb { - -webkit-box-shadow: 0 0 0 1px #060606, 0 0 0 0.2rem rgba(42, 159, 214, 0.25); - box-shadow: 0 0 0 1px #060606, 0 0 0 0.2rem rgba(42, 159, 214, 0.25); -} - -.custom-range:focus::-moz-range-thumb { - box-shadow: 0 0 0 1px #060606, 0 0 0 0.2rem rgba(42, 159, 214, 0.25); -} - -.custom-range:focus::-ms-thumb { - box-shadow: 0 0 0 1px #060606, 0 0 0 0.2rem rgba(42, 159, 214, 0.25); -} - -.custom-range::-moz-focus-outer { - border: 0; -} - -.custom-range::-webkit-slider-thumb { - width: 1rem; - height: 1rem; - margin-top: -0.25rem; - background-color: #2A9FD6; - border: 0; - border-radius: 1rem; - -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - -webkit-appearance: none; - appearance: none; -} - -@media (prefers-reduced-motion: reduce) { - .custom-range::-webkit-slider-thumb { - -webkit-transition: none; - transition: none; - } -} - -.custom-range::-webkit-slider-thumb:active { - background-color: #c0e2f3; -} - -.custom-range::-webkit-slider-runnable-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: #dee2e6; - border-color: transparent; - border-radius: 1rem; -} - -.custom-range::-moz-range-thumb { - width: 1rem; - height: 1rem; - background-color: #2A9FD6; - border: 0; - border-radius: 1rem; - -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - -moz-appearance: none; - appearance: none; -} - -@media (prefers-reduced-motion: reduce) { - .custom-range::-moz-range-thumb { - -webkit-transition: none; - transition: none; - } -} - -.custom-range::-moz-range-thumb:active { - background-color: #c0e2f3; -} - -.custom-range::-moz-range-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: #dee2e6; - border-color: transparent; - border-radius: 1rem; -} - -.custom-range::-ms-thumb { - width: 1rem; - height: 1rem; - margin-top: 0; - margin-right: 0.2rem; - margin-left: 0.2rem; - background-color: #2A9FD6; - border: 0; - border-radius: 1rem; - -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - appearance: none; -} - -@media (prefers-reduced-motion: reduce) { - .custom-range::-ms-thumb { - -webkit-transition: none; - transition: none; - } -} - -.custom-range::-ms-thumb:active { - background-color: #c0e2f3; -} - -.custom-range::-ms-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: transparent; - border-color: transparent; - border-width: 0.5rem; -} - -.custom-range::-ms-fill-lower { - background-color: #dee2e6; - border-radius: 1rem; -} - -.custom-range::-ms-fill-upper { - margin-right: 15px; - background-color: #dee2e6; - border-radius: 1rem; -} - -.custom-range:disabled::-webkit-slider-thumb { - background-color: #888; -} - -.custom-range:disabled::-webkit-slider-runnable-track { - cursor: default; -} - -.custom-range:disabled::-moz-range-thumb { - background-color: #888; -} - -.custom-range:disabled::-moz-range-track { - cursor: default; -} - -.custom-range:disabled::-ms-thumb { - background-color: #888; -} - -.custom-control-label::before, -.custom-file-label, -.custom-select { - -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .custom-control-label::before, - .custom-file-label, - .custom-select { - -webkit-transition: none; - transition: none; - } -} - -.nav { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - padding-left: 0; - margin-bottom: 0; - list-style: none; -} - -.nav-link { - display: block; - padding: 0.5rem 1rem; -} - -.nav-link:hover, .nav-link:focus { - text-decoration: none; -} - -.nav-link.disabled { - color: #555; - pointer-events: none; - cursor: default; -} - -.nav-tabs { - border-bottom: 1px solid #282828; -} - -.nav-tabs .nav-item { - margin-bottom: -1px; -} - -.nav-tabs .nav-link { - border: 1px solid transparent; - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} - -.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { - border-color: #282828; -} - -.nav-tabs .nav-link.disabled { - color: #555; - background-color: transparent; - border-color: transparent; -} - -.nav-tabs .nav-link.active, -.nav-tabs .nav-item.show .nav-link { - color: #fff; - background-color: #282828; - border-color: #282828; -} - -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.nav-pills .nav-link { - border-radius: 0.25rem; -} - -.nav-pills .nav-link.active, -.nav-pills .show > .nav-link { - color: #fff; - background-color: #2A9FD6; -} - -.nav-fill .nav-item { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - text-align: center; -} - -.nav-justified .nav-item { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - text-align: center; -} - -.tab-content > .tab-pane { - display: none; -} - -.tab-content > .active { - display: block; -} - -.navbar { - position: relative; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: justify; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 0.5rem 1rem; -} - -.navbar > .container, -.navbar > .container-fluid { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: justify; - -ms-flex-pack: justify; - justify-content: space-between; -} - -.navbar-brand { - display: inline-block; - padding-top: 0.3359375rem; - padding-bottom: 0.3359375rem; - margin-right: 1rem; - font-size: 1.09375rem; - line-height: inherit; - white-space: nowrap; -} - -.navbar-brand:hover, .navbar-brand:focus { - text-decoration: none; -} - -.navbar-nav { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; - list-style: none; -} - -.navbar-nav .nav-link { - padding-right: 0; - padding-left: 0; -} - -.navbar-nav .dropdown-menu { - position: static; - float: none; -} - -.navbar-text { - display: inline-block; - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.navbar-collapse { - -ms-flex-preferred-size: 100%; - flex-basis: 100%; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.navbar-toggler { - padding: 0.25rem 0.75rem; - font-size: 1.09375rem; - line-height: 1; - background-color: transparent; - border: 1px solid transparent; - border-radius: 0.25rem; -} - -.navbar-toggler:hover, .navbar-toggler:focus { - text-decoration: none; -} - -.navbar-toggler-icon { - display: inline-block; - width: 1.5em; - height: 1.5em; - vertical-align: middle; - content: ""; - background: no-repeat center center; - background-size: 100% 100%; -} - -@media (max-width: 575.98px) { - .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 576px) { - .navbar-expand-sm { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-sm .navbar-nav { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-sm .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-sm .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-sm .navbar-collapse { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-sm .navbar-toggler { - display: none; - } -} - -@media (max-width: 767.98px) { - .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 768px) { - .navbar-expand-md { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-md .navbar-nav { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-md .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-md .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-md .navbar-collapse { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-md .navbar-toggler { - display: none; - } -} - -@media (max-width: 991.98px) { - .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 992px) { - .navbar-expand-lg { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-lg .navbar-nav { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-lg .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-lg .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-lg .navbar-collapse { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-lg .navbar-toggler { - display: none; - } -} - -@media (max-width: 1199.98px) { - .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 1200px) { - .navbar-expand-xl { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-xl .navbar-nav { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-xl .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-xl .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-xl .navbar-collapse { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-xl .navbar-toggler { - display: none; - } -} - -.navbar-expand { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.navbar-expand > .container, -.navbar-expand > .container-fluid { - padding-right: 0; - padding-left: 0; -} - -.navbar-expand .navbar-nav { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; -} - -.navbar-expand .navbar-nav .dropdown-menu { - position: absolute; -} - -.navbar-expand .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; -} - -.navbar-expand > .container, -.navbar-expand > .container-fluid { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; -} - -.navbar-expand .navbar-collapse { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; -} - -.navbar-expand .navbar-toggler { - display: none; -} - -.navbar-light .navbar-brand { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-nav .nav-link { - color: rgba(0, 0, 0, 0.5); -} - -.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { - color: rgba(0, 0, 0, 0.7); -} - -.navbar-light .navbar-nav .nav-link.disabled { - color: rgba(0, 0, 0, 0.3); -} - -.navbar-light .navbar-nav .show > .nav-link, -.navbar-light .navbar-nav .active > .nav-link, -.navbar-light .navbar-nav .nav-link.show, -.navbar-light .navbar-nav .nav-link.active { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-toggler { - color: rgba(0, 0, 0, 0.5); - border-color: rgba(0, 0, 0, 0.1); -} - -.navbar-light .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); -} - -.navbar-light .navbar-text { - color: rgba(0, 0, 0, 0.5); -} - -.navbar-light .navbar-text a { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-dark .navbar-brand { - color: #fff; -} - -.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { - color: #fff; -} - -.navbar-dark .navbar-nav .nav-link { - color: rgba(255, 255, 255, 0.5); -} - -.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { - color: #fff; -} - -.navbar-dark .navbar-nav .nav-link.disabled { - color: rgba(255, 255, 255, 0.25); -} - -.navbar-dark .navbar-nav .show > .nav-link, -.navbar-dark .navbar-nav .active > .nav-link, -.navbar-dark .navbar-nav .nav-link.show, -.navbar-dark .navbar-nav .nav-link.active { - color: #fff; -} - -.navbar-dark .navbar-toggler { - color: rgba(255, 255, 255, 0.5); - border-color: rgba(255, 255, 255, 0.1); -} - -.navbar-dark .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); -} - -.navbar-dark .navbar-text { - color: rgba(255, 255, 255, 0.5); -} - -.navbar-dark .navbar-text a { - color: #fff; -} - -.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { - color: #fff; -} - -.card { - position: relative; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - min-width: 0; - word-wrap: break-word; - background-color: #282828; - background-clip: border-box; - border: 1px solid rgba(0, 0, 0, 0.125); - border-radius: 0.25rem; -} - -.card > hr { - margin-right: 0; - margin-left: 0; -} - -.card > .list-group:first-child .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} - -.card > .list-group:last-child .list-group-item:last-child { - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} - -.card-body { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - padding: 1.25rem; -} - -.card-title { - margin-bottom: 0.75rem; -} - -.card-subtitle { - margin-top: -0.375rem; - margin-bottom: 0; -} - -.card-text:last-child { - margin-bottom: 0; -} - -.card-link:hover { - text-decoration: none; -} - -.card-link + .card-link { - margin-left: 1.25rem; -} - -.card-header { - padding: 0.75rem 1.25rem; - margin-bottom: 0; - background-color: rgba(0, 0, 0, 0.03); - border-bottom: 1px solid rgba(0, 0, 0, 0.125); -} - -.card-header:first-child { - border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; -} - -.card-header + .list-group .list-group-item:first-child { - border-top: 0; -} - -.card-footer { - padding: 0.75rem 1.25rem; - background-color: rgba(0, 0, 0, 0.03); - border-top: 1px solid rgba(0, 0, 0, 0.125); -} - -.card-footer:last-child { - border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); -} - -.card-header-tabs { - margin-right: -0.625rem; - margin-bottom: -0.75rem; - margin-left: -0.625rem; - border-bottom: 0; -} - -.card-header-pills { - margin-right: -0.625rem; - margin-left: -0.625rem; -} - -.card-img-overlay { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - padding: 1.25rem; -} - -.card-img { - width: 100%; - border-radius: calc(0.25rem - 1px); -} - -.card-img-top { - width: 100%; - border-top-left-radius: calc(0.25rem - 1px); - border-top-right-radius: calc(0.25rem - 1px); -} - -.card-img-bottom { - width: 100%; - border-bottom-right-radius: calc(0.25rem - 1px); - border-bottom-left-radius: calc(0.25rem - 1px); -} - -.card-deck { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; -} - -.card-deck .card { - margin-bottom: 15px; -} - -@media (min-width: 576px) { - .card-deck { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - margin-right: -15px; - margin-left: -15px; - } - .card-deck .card { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-flex: 1; - -ms-flex: 1 0 0%; - flex: 1 0 0%; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - margin-right: 15px; - margin-bottom: 0; - margin-left: 15px; - } -} - -.card-group { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; -} - -.card-group > .card { - margin-bottom: 15px; -} - -@media (min-width: 576px) { - .card-group { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - } - .card-group > .card { - -webkit-box-flex: 1; - -ms-flex: 1 0 0%; - flex: 1 0 0%; - margin-bottom: 0; - } - .card-group > .card + .card { - margin-left: 0; - border-left: 0; - } - .card-group > .card:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-top, - .card-group > .card:not(:last-child) .card-header { - border-top-right-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-bottom, - .card-group > .card:not(:last-child) .card-footer { - border-bottom-right-radius: 0; - } - .card-group > .card:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-top, - .card-group > .card:not(:first-child) .card-header { - border-top-left-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-bottom, - .card-group > .card:not(:first-child) .card-footer { - border-bottom-left-radius: 0; - } -} - -.card-columns .card { - margin-bottom: 0.75rem; -} - -@media (min-width: 576px) { - .card-columns { - -webkit-column-count: 3; - column-count: 3; - -webkit-column-gap: 1.25rem; - column-gap: 1.25rem; - orphans: 1; - widows: 1; - } - .card-columns .card { - display: inline-block; - width: 100%; - } -} - -.accordion > .card { - overflow: hidden; -} - -.accordion > .card:not(:first-of-type) .card-header:first-child { - border-radius: 0; -} - -.accordion > .card:not(:first-of-type):not(:last-of-type) { - border-bottom: 0; - border-radius: 0; -} - -.accordion > .card:first-of-type { - border-bottom: 0; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} - -.accordion > .card:last-of-type { - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.accordion > .card .card-header { - margin-bottom: -1px; -} - -.breadcrumb { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - padding: 0.75rem 1rem; - margin-bottom: 1rem; - list-style: none; - background-color: #282828; - border-radius: 0.25rem; -} - -.breadcrumb-item + .breadcrumb-item { - padding-left: 0.5rem; -} - -.breadcrumb-item + .breadcrumb-item::before { - display: inline-block; - padding-right: 0.5rem; - color: #555; - content: "/"; -} - -.breadcrumb-item + .breadcrumb-item:hover::before { - text-decoration: underline; -} - -.breadcrumb-item + .breadcrumb-item:hover::before { - text-decoration: none; -} - -.breadcrumb-item.active { - color: #555; -} - -.pagination { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - padding-left: 0; - list-style: none; - border-radius: 0.25rem; -} - -.page-link { - position: relative; - display: block; - padding: 0.5rem 0.75rem; - margin-left: -1px; - line-height: 1.25; - color: #fff; - background-color: #282828; - border: 1px solid transparent; -} - -.page-link:hover { - z-index: 2; - color: #fff; - text-decoration: none; - background-color: #2A9FD6; - border-color: transparent; -} - -.page-link:focus { - z-index: 2; - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); - box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.25); -} - -.page-item:first-child .page-link { - margin-left: 0; - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} - -.page-item:last-child .page-link { - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; -} - -.page-item.active .page-link { - z-index: 1; - color: #fff; - background-color: #2A9FD6; - border-color: #2A9FD6; -} - -.page-item.disabled .page-link { - color: #555; - pointer-events: none; - cursor: auto; - background-color: #282828; - border-color: transparent; -} - -.pagination-lg .page-link { - padding: 0.75rem 1.5rem; - font-size: 1.09375rem; - line-height: 1.5; -} - -.pagination-lg .page-item:first-child .page-link { - border-top-left-radius: 0.3rem; - border-bottom-left-radius: 0.3rem; -} - -.pagination-lg .page-item:last-child .page-link { - border-top-right-radius: 0.3rem; - border-bottom-right-radius: 0.3rem; -} - -.pagination-sm .page-link { - padding: 0.25rem 0.5rem; - font-size: 0.765625rem; - line-height: 1.5; -} - -.pagination-sm .page-item:first-child .page-link { - border-top-left-radius: 0.2rem; - border-bottom-left-radius: 0.2rem; -} - -.pagination-sm .page-item:last-child .page-link { - border-top-right-radius: 0.2rem; - border-bottom-right-radius: 0.2rem; -} - -.badge { - display: inline-block; - padding: 0.25em 0.4em; - font-size: 75%; - font-weight: 700; - line-height: 1; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: 0.25rem; - -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .badge { - -webkit-transition: none; - transition: none; - } -} - -a.badge:hover, a.badge:focus { - text-decoration: none; -} - -.badge:empty { - display: none; -} - -.btn .badge { - position: relative; - top: -1px; -} - -.badge-pill { - padding-right: 0.6em; - padding-left: 0.6em; - border-radius: 10rem; -} - -.badge-primary { - color: #fff; - background-color: #2A9FD6; -} - -a.badge-primary:hover, a.badge-primary:focus { - color: #fff; - background-color: #2180ac; -} - -a.badge-primary:focus, a.badge-primary.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.5); - box-shadow: 0 0 0 0.2rem rgba(42, 159, 214, 0.5); -} - -.badge-secondary { - color: #fff; - background-color: #555; -} - -a.badge-secondary:hover, a.badge-secondary:focus { - color: #fff; - background-color: #3c3b3b; -} - -a.badge-secondary:focus, a.badge-secondary.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(85, 85, 85, 0.5); - box-shadow: 0 0 0 0.2rem rgba(85, 85, 85, 0.5); -} - -.badge-success { - color: #fff; - background-color: #77B300; -} - -a.badge-success:hover, a.badge-success:focus { - color: #fff; - background-color: #558000; -} - -a.badge-success:focus, a.badge-success.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.5); - box-shadow: 0 0 0 0.2rem rgba(119, 179, 0, 0.5); -} - -.badge-info { - color: #fff; - background-color: #9933CC; -} - -a.badge-info:hover, a.badge-info:focus { - color: #fff; - background-color: #7a29a3; -} - -a.badge-info:focus, a.badge-info.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(153, 51, 204, 0.5); - box-shadow: 0 0 0 0.2rem rgba(153, 51, 204, 0.5); -} - -.badge-warning { - color: #fff; - background-color: #FF8800; -} - -a.badge-warning:hover, a.badge-warning:focus { - color: #fff; - background-color: #cc6d00; -} - -a.badge-warning:focus, a.badge-warning.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 136, 0, 0.5); - box-shadow: 0 0 0 0.2rem rgba(255, 136, 0, 0.5); -} - -.badge-danger { - color: #fff; - background-color: #CC0000; -} - -a.badge-danger:hover, a.badge-danger:focus { - color: #fff; - background-color: #990000; -} - -a.badge-danger:focus, a.badge-danger.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.5); - box-shadow: 0 0 0 0.2rem rgba(204, 0, 0, 0.5); -} - -.badge-light { - color: #fff; - background-color: #222; -} - -a.badge-light:hover, a.badge-light:focus { - color: #fff; - background-color: #090808; -} - -a.badge-light:focus, a.badge-light.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(34, 34, 34, 0.5); - box-shadow: 0 0 0 0.2rem rgba(34, 34, 34, 0.5); -} - -.badge-dark { - color: #fff; - background-color: #ADAFAE; -} - -a.badge-dark:hover, a.badge-dark:focus { - color: #fff; - background-color: #939695; -} - -a.badge-dark:focus, a.badge-dark.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(173, 175, 174, 0.5); - box-shadow: 0 0 0 0.2rem rgba(173, 175, 174, 0.5); -} - -.jumbotron { - padding: 2rem 1rem; - margin-bottom: 2rem; - background-color: #282828; - border-radius: 0.3rem; -} - -@media (min-width: 576px) { - .jumbotron { - padding: 4rem 2rem; - } -} - -.jumbotron-fluid { - padding-right: 0; - padding-left: 0; - border-radius: 0; -} - -.alert { - position: relative; - padding: 0.75rem 1.25rem; - margin-bottom: 1rem; - border: 1px solid transparent; - border-radius: 0.25rem; -} - -.alert-heading { - color: inherit; -} - -.alert-link { - font-weight: 700; -} - -.alert-dismissible { - padding-right: 3.8125rem; -} - -.alert-dismissible .close { - position: absolute; - top: 0; - right: 0; - padding: 0.75rem 1.25rem; - color: inherit; -} - -.alert-primary { - color: #16536f; - background-color: #d4ecf7; - border-color: #c3e4f4; -} - -.alert-primary hr { - border-top-color: #addaf0; -} - -.alert-primary .alert-link { - color: #0e3344; -} - -.alert-secondary { - color: #2c2c2c; - background-color: #dddddd; - border-color: #cfcfcf; -} - -.alert-secondary hr { - border-top-color: #c2c2c2; -} - -.alert-secondary .alert-link { - color: #131212; -} - -.alert-success { - color: #3e5d00; - background-color: #e4f0cc; - border-color: #d9eab8; -} - -.alert-success hr { - border-top-color: #cee4a4; -} - -.alert-success .alert-link { - color: #1c2a00; -} - -.alert-info { - color: #501b6a; - background-color: #ebd6f5; - border-color: #e2c6f1; -} - -.alert-info hr { - border-top-color: #d8b2ec; -} - -.alert-info .alert-link { - color: #311141; -} - -.alert-warning { - color: #854700; - background-color: #ffe7cc; - border-color: #ffdeb8; -} - -.alert-warning hr { - border-top-color: #ffd29f; -} - -.alert-warning .alert-link { - color: #522c00; -} - -.alert-danger { - color: #6a0000; - background-color: #f5cccc; - border-color: #f1b8b8; -} - -.alert-danger hr { - border-top-color: #eda3a3; -} - -.alert-danger .alert-link { - color: #370000; -} - -.alert-light { - color: #121212; - background-color: lightgray; - border-color: #c1c1c1; -} - -.alert-light hr { - border-top-color: #b4b4b4; -} - -.alert-light .alert-link { - color: black; -} - -.alert-dark { - color: #5a5b5a; - background-color: #efefef; - border-color: #e8e9e8; -} - -.alert-dark hr { - border-top-color: #dbdddb; -} - -.alert-dark .alert-link { - color: #414141; -} - -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 1rem 0; - } - to { - background-position: 0 0; - } -} - -@keyframes progress-bar-stripes { - from { - background-position: 1rem 0; - } - to { - background-position: 0 0; - } -} - -.progress { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - height: 1rem; - overflow: hidden; - font-size: 0.65625rem; - background-color: #282828; - border-radius: 0.25rem; -} - -.progress-bar { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - color: #fff; - text-align: center; - white-space: nowrap; - background-color: #2A9FD6; - -webkit-transition: width 0.6s ease; - transition: width 0.6s ease; -} - -@media (prefers-reduced-motion: reduce) { - .progress-bar { - -webkit-transition: none; - transition: none; - } -} - -.progress-bar-striped { - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-size: 1rem 1rem; -} - -.progress-bar-animated { - -webkit-animation: progress-bar-stripes 1s linear infinite; - animation: progress-bar-stripes 1s linear infinite; -} - -@media (prefers-reduced-motion: reduce) { - .progress-bar-animated { - -webkit-animation: none; - animation: none; - } -} - -.media { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: flex-start; -} - -.media-body { - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; -} - -.list-group { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; -} - -.list-group-item-action { - width: 100%; - color: #282828; - text-align: inherit; -} - -.list-group-item-action:hover, .list-group-item-action:focus { - z-index: 1; - color: #282828; - text-decoration: none; - background-color: #2A9FD6; -} - -.list-group-item-action:active { - color: #888; - background-color: #2A9FD6; -} - -.list-group-item { - position: relative; - display: block; - padding: 0.75rem 1.25rem; - margin-bottom: -1px; - background-color: #222; - border: 1px solid #282828; -} - -.list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} - -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} - -.list-group-item.disabled, .list-group-item:disabled { - color: #555; - pointer-events: none; - background-color: #282828; -} - -.list-group-item.active { - z-index: 2; - color: #fff; - background-color: #2A9FD6; - border-color: #2A9FD6; -} - -.list-group-horizontal { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; -} - -.list-group-horizontal .list-group-item { - margin-right: -1px; - margin-bottom: 0; -} - -.list-group-horizontal .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; -} - -.list-group-horizontal .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; -} - -@media (min-width: 576px) { - .list-group-horizontal-sm { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-sm .list-group-item { - margin-right: -1px; - margin-bottom: 0; - } - .list-group-horizontal-sm .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-sm .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } -} - -@media (min-width: 768px) { - .list-group-horizontal-md { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-md .list-group-item { - margin-right: -1px; - margin-bottom: 0; - } - .list-group-horizontal-md .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-md .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } -} - -@media (min-width: 992px) { - .list-group-horizontal-lg { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-lg .list-group-item { - margin-right: -1px; - margin-bottom: 0; - } - .list-group-horizontal-lg .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-lg .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } -} - -@media (min-width: 1200px) { - .list-group-horizontal-xl { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-xl .list-group-item { - margin-right: -1px; - margin-bottom: 0; - } - .list-group-horizontal-xl .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-xl .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } -} - -.list-group-flush .list-group-item { - border-right: 0; - border-left: 0; - border-radius: 0; -} - -.list-group-flush .list-group-item:last-child { - margin-bottom: -1px; -} - -.list-group-flush:first-child .list-group-item:first-child { - border-top: 0; -} - -.list-group-flush:last-child .list-group-item:last-child { - margin-bottom: 0; - border-bottom: 0; -} - -.list-group-item-primary { - color: #16536f; - background-color: #c3e4f4; -} - -.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { - color: #16536f; - background-color: #addaf0; -} - -.list-group-item-primary.list-group-item-action.active { - color: #fff; - background-color: #16536f; - border-color: #16536f; -} - -.list-group-item-secondary { - color: #2c2c2c; - background-color: #cfcfcf; -} - -.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { - color: #2c2c2c; - background-color: #c2c2c2; -} - -.list-group-item-secondary.list-group-item-action.active { - color: #fff; - background-color: #2c2c2c; - border-color: #2c2c2c; -} - -.list-group-item-success { - color: #3e5d00; - background-color: #d9eab8; -} - -.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { - color: #3e5d00; - background-color: #cee4a4; -} - -.list-group-item-success.list-group-item-action.active { - color: #fff; - background-color: #3e5d00; - border-color: #3e5d00; -} - -.list-group-item-info { - color: #501b6a; - background-color: #e2c6f1; -} - -.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { - color: #501b6a; - background-color: #d8b2ec; -} - -.list-group-item-info.list-group-item-action.active { - color: #fff; - background-color: #501b6a; - border-color: #501b6a; -} - -.list-group-item-warning { - color: #854700; - background-color: #ffdeb8; -} - -.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { - color: #854700; - background-color: #ffd29f; -} - -.list-group-item-warning.list-group-item-action.active { - color: #fff; - background-color: #854700; - border-color: #854700; -} - -.list-group-item-danger { - color: #6a0000; - background-color: #f1b8b8; -} - -.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { - color: #6a0000; - background-color: #eda3a3; -} - -.list-group-item-danger.list-group-item-action.active { - color: #fff; - background-color: #6a0000; - border-color: #6a0000; -} - -.list-group-item-light { - color: #121212; - background-color: #c1c1c1; -} - -.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { - color: #121212; - background-color: #b4b4b4; -} - -.list-group-item-light.list-group-item-action.active { - color: #fff; - background-color: #121212; - border-color: #121212; -} - -.list-group-item-dark { - color: #5a5b5a; - background-color: #e8e9e8; -} - -.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { - color: #5a5b5a; - background-color: #dbdddb; -} - -.list-group-item-dark.list-group-item-action.active { - color: #fff; - background-color: #5a5b5a; - border-color: #5a5b5a; -} - -.close { - float: right; - font-size: 1.3125rem; - font-weight: 700; - line-height: 1; - color: #fff; - text-shadow: none; - opacity: .5; -} - -.close:hover { - color: #fff; - text-decoration: none; -} - -.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus { - opacity: .75; -} - -button.close { - padding: 0; - background-color: transparent; - border: 0; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -a.close.disabled { - pointer-events: none; -} - -.toast { - max-width: 350px; - overflow: hidden; - font-size: 0.875rem; - background-color: rgba(255, 255, 255, 0.85); - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.1); - -webkit-box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); - box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); - -webkit-backdrop-filter: blur(10px); - backdrop-filter: blur(10px); - opacity: 0; - border-radius: 0.25rem; -} - -.toast:not(:last-child) { - margin-bottom: 0.75rem; -} - -.toast.showing { - opacity: 1; -} - -.toast.show { - display: block; - opacity: 1; -} - -.toast.hide { - display: none; -} - -.toast-header { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - padding: 0.25rem 0.75rem; - color: #555; - background-color: rgba(255, 255, 255, 0.85); - background-clip: padding-box; - border-bottom: 1px solid rgba(0, 0, 0, 0.05); -} - -.toast-body { - padding: 0.75rem; -} - -.modal-open { - overflow: hidden; -} - -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} - -.modal { - position: fixed; - top: 0; - left: 0; - z-index: 1050; - display: none; - width: 100%; - height: 100%; - overflow: hidden; - outline: 0; -} - -.modal-dialog { - position: relative; - width: auto; - margin: 0.5rem; - pointer-events: none; -} - -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform 0.3s ease-out; - transition: -webkit-transform 0.3s ease-out; - transition: transform 0.3s ease-out; - transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out; - -webkit-transform: translate(0, -50px); - transform: translate(0, -50px); -} - -@media (prefers-reduced-motion: reduce) { - .modal.fade .modal-dialog { - -webkit-transition: none; - transition: none; - } -} - -.modal.show .modal-dialog { - -webkit-transform: none; - transform: none; -} - -.modal-dialog-scrollable { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - max-height: calc(100% - 1rem); -} - -.modal-dialog-scrollable .modal-content { - max-height: calc(100vh - 1rem); - overflow: hidden; -} - -.modal-dialog-scrollable .modal-header, -.modal-dialog-scrollable .modal-footer { - -ms-flex-negative: 0; - flex-shrink: 0; -} - -.modal-dialog-scrollable .modal-body { - overflow-y: auto; -} - -.modal-dialog-centered { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - min-height: calc(100% - 1rem); -} - -.modal-dialog-centered::before { - display: block; - height: calc(100vh - 1rem); - content: ""; -} - -.modal-dialog-centered.modal-dialog-scrollable { - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - height: 100%; -} - -.modal-dialog-centered.modal-dialog-scrollable .modal-content { - max-height: none; -} - -.modal-dialog-centered.modal-dialog-scrollable::before { - content: none; -} - -.modal-content { - position: relative; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - width: 100%; - pointer-events: auto; - background-color: #222; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 0.3rem; - outline: 0; -} - -.modal-backdrop { - position: fixed; - top: 0; - left: 0; - z-index: 1040; - width: 100vw; - height: 100vh; - background-color: #000; -} - -.modal-backdrop.fade { - opacity: 0; -} - -.modal-backdrop.show { - opacity: 0.5; -} - -.modal-header { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: flex-start; - -webkit-box-pack: justify; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 1rem 1rem; - border-bottom: 1px solid #282828; - border-top-left-radius: 0.3rem; - border-top-right-radius: 0.3rem; -} - -.modal-header .close { - padding: 1rem 1rem; - margin: -1rem -1rem -1rem auto; -} - -.modal-title { - margin-bottom: 0; - line-height: 1.5; -} - -.modal-body { - position: relative; - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - padding: 1rem; -} - -.modal-footer { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: end; - -ms-flex-pack: end; - justify-content: flex-end; - padding: 1rem; - border-top: 1px solid #282828; - border-bottom-right-radius: 0.3rem; - border-bottom-left-radius: 0.3rem; -} - -.modal-footer > :not(:first-child) { - margin-left: .25rem; -} - -.modal-footer > :not(:last-child) { - margin-right: .25rem; -} - -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} - -@media (min-width: 576px) { - .modal-dialog { - max-width: 500px; - margin: 1.75rem auto; - } - .modal-dialog-scrollable { - max-height: calc(100% - 3.5rem); - } - .modal-dialog-scrollable .modal-content { - max-height: calc(100vh - 3.5rem); - } - .modal-dialog-centered { - min-height: calc(100% - 3.5rem); - } - .modal-dialog-centered::before { - height: calc(100vh - 3.5rem); - } - .modal-sm { - max-width: 300px; - } -} - -@media (min-width: 992px) { - .modal-lg, - .modal-xl { - max-width: 800px; - } -} - -@media (min-width: 1200px) { - .modal-xl { - max-width: 1140px; - } -} - -.tooltip { - position: absolute; - z-index: 1070; - display: block; - margin: 0; - font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: 0.765625rem; - word-wrap: break-word; - opacity: 0; -} - -.tooltip.show { - opacity: 1; -} - -.tooltip .arrow { - position: absolute; - display: block; - width: 0.8rem; - height: 0.4rem; -} - -.tooltip .arrow::before { - position: absolute; - content: ""; - border-color: transparent; - border-style: solid; -} - -.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] { - padding: 0.4rem 0; -} - -.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow { - bottom: 0; -} - -.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before { - top: 0; - border-width: 0.4rem 0.4rem 0; - border-top-color: #282828; -} - -.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] { - padding: 0 0.4rem; -} - -.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow { - left: 0; - width: 0.4rem; - height: 0.8rem; -} - -.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before { - right: 0; - border-width: 0.4rem 0.4rem 0.4rem 0; - border-right-color: #282828; -} - -.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] { - padding: 0.4rem 0; -} - -.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow { - top: 0; -} - -.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before { - bottom: 0; - border-width: 0 0.4rem 0.4rem; - border-bottom-color: #282828; -} - -.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] { - padding: 0 0.4rem; -} - -.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow { - right: 0; - width: 0.4rem; - height: 0.8rem; -} - -.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before { - left: 0; - border-width: 0.4rem 0 0.4rem 0.4rem; - border-left-color: #282828; -} - -.tooltip-inner { - max-width: 200px; - padding: 0.25rem 0.5rem; - color: #fff; - text-align: center; - background-color: #282828; - border-radius: 0.25rem; -} - -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: block; - max-width: 276px; - font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: 0.765625rem; - word-wrap: break-word; - background-color: #282828; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 0.3rem; -} - -.popover .arrow { - position: absolute; - display: block; - width: 1rem; - height: 0.5rem; - margin: 0 0.3rem; -} - -.popover .arrow::before, .popover .arrow::after { - position: absolute; - display: block; - content: ""; - border-color: transparent; - border-style: solid; -} - -.bs-popover-top, .bs-popover-auto[x-placement^="top"] { - margin-bottom: 0.5rem; -} - -.bs-popover-top > .arrow, .bs-popover-auto[x-placement^="top"] > .arrow { - bottom: calc((0.5rem + 1px) * -1); -} - -.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^="top"] > .arrow::before { - bottom: 0; - border-width: 0.5rem 0.5rem 0; - border-top-color: rgba(0, 0, 0, 0.25); -} - -.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^="top"] > .arrow::after { - bottom: 1px; - border-width: 0.5rem 0.5rem 0; - border-top-color: #282828; -} - -.bs-popover-right, .bs-popover-auto[x-placement^="right"] { - margin-left: 0.5rem; -} - -.bs-popover-right > .arrow, .bs-popover-auto[x-placement^="right"] > .arrow { - left: calc((0.5rem + 1px) * -1); - width: 0.5rem; - height: 1rem; - margin: 0.3rem 0; -} - -.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^="right"] > .arrow::before { - left: 0; - border-width: 0.5rem 0.5rem 0.5rem 0; - border-right-color: rgba(0, 0, 0, 0.25); -} - -.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^="right"] > .arrow::after { - left: 1px; - border-width: 0.5rem 0.5rem 0.5rem 0; - border-right-color: #282828; -} - -.bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { - margin-top: 0.5rem; -} - -.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^="bottom"] > .arrow { - top: calc((0.5rem + 1px) * -1); -} - -.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^="bottom"] > .arrow::before { - top: 0; - border-width: 0 0.5rem 0.5rem 0.5rem; - border-bottom-color: rgba(0, 0, 0, 0.25); -} - -.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^="bottom"] > .arrow::after { - top: 1px; - border-width: 0 0.5rem 0.5rem 0.5rem; - border-bottom-color: #282828; -} - -.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before { - position: absolute; - top: 0; - left: 50%; - display: block; - width: 1rem; - margin-left: -0.5rem; - content: ""; - border-bottom: 1px solid #202020; -} - -.bs-popover-left, .bs-popover-auto[x-placement^="left"] { - margin-right: 0.5rem; -} - -.bs-popover-left > .arrow, .bs-popover-auto[x-placement^="left"] > .arrow { - right: calc((0.5rem + 1px) * -1); - width: 0.5rem; - height: 1rem; - margin: 0.3rem 0; -} - -.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^="left"] > .arrow::before { - right: 0; - border-width: 0.5rem 0 0.5rem 0.5rem; - border-left-color: rgba(0, 0, 0, 0.25); -} - -.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^="left"] > .arrow::after { - right: 1px; - border-width: 0.5rem 0 0.5rem 0.5rem; - border-left-color: #282828; -} - -.popover-header { - padding: 0.5rem 0.75rem; - margin-bottom: 0; - font-size: 0.875rem; - color: #fff; - background-color: #202020; - border-bottom: 1px solid #141414; - border-top-left-radius: calc(0.3rem - 1px); - border-top-right-radius: calc(0.3rem - 1px); -} - -.popover-header:empty { - display: none; -} - -.popover-body { - padding: 0.5rem 0.75rem; - color: #888; -} - -.carousel { - position: relative; -} - -.carousel.pointer-event { - -ms-touch-action: pan-y; - touch-action: pan-y; -} - -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} - -.carousel-inner::after { - display: block; - clear: both; - content: ""; -} - -.carousel-item { - position: relative; - display: none; - float: left; - width: 100%; - margin-right: -100%; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-transition: -webkit-transform 0.6s ease-in-out; - transition: -webkit-transform 0.6s ease-in-out; - transition: transform 0.6s ease-in-out; - transition: transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .carousel-item { - -webkit-transition: none; - transition: none; - } -} - -.carousel-item.active, -.carousel-item-next, -.carousel-item-prev { - display: block; -} - -.carousel-item-next:not(.carousel-item-left), -.active.carousel-item-right { - -webkit-transform: translateX(100%); - transform: translateX(100%); -} - -.carousel-item-prev:not(.carousel-item-right), -.active.carousel-item-left { - -webkit-transform: translateX(-100%); - transform: translateX(-100%); -} - -.carousel-fade .carousel-item { - opacity: 0; - -webkit-transition-property: opacity; - transition-property: opacity; - -webkit-transform: none; - transform: none; -} - -.carousel-fade .carousel-item.active, -.carousel-fade .carousel-item-next.carousel-item-left, -.carousel-fade .carousel-item-prev.carousel-item-right { - z-index: 1; - opacity: 1; -} - -.carousel-fade .active.carousel-item-left, -.carousel-fade .active.carousel-item-right { - z-index: 0; - opacity: 0; - -webkit-transition: 0s 0.6s opacity; - transition: 0s 0.6s opacity; -} - -@media (prefers-reduced-motion: reduce) { - .carousel-fade .active.carousel-item-left, - .carousel-fade .active.carousel-item-right { - -webkit-transition: none; - transition: none; - } -} - -.carousel-control-prev, -.carousel-control-next { - position: absolute; - top: 0; - bottom: 0; - z-index: 1; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - width: 15%; - color: #fff; - text-align: center; - opacity: 0.5; - -webkit-transition: opacity 0.15s ease; - transition: opacity 0.15s ease; -} - -@media (prefers-reduced-motion: reduce) { - .carousel-control-prev, - .carousel-control-next { - -webkit-transition: none; - transition: none; - } -} - -.carousel-control-prev:hover, .carousel-control-prev:focus, -.carousel-control-next:hover, -.carousel-control-next:focus { - color: #fff; - text-decoration: none; - outline: 0; - opacity: 0.9; -} - -.carousel-control-prev { - left: 0; -} - -.carousel-control-next { - right: 0; -} - -.carousel-control-prev-icon, -.carousel-control-next-icon { - display: inline-block; - width: 20px; - height: 20px; - background: no-repeat 50% / 100% 100%; -} - -.carousel-control-prev-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e"); -} - -.carousel-control-next-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e"); -} - -.carousel-indicators { - position: absolute; - right: 0; - bottom: 0; - left: 0; - z-index: 15; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - padding-left: 0; - margin-right: 15%; - margin-left: 15%; - list-style: none; -} - -.carousel-indicators li { - -webkit-box-sizing: content-box; - box-sizing: content-box; - -webkit-box-flex: 0; - -ms-flex: 0 1 auto; - flex: 0 1 auto; - width: 30px; - height: 3px; - margin-right: 3px; - margin-left: 3px; - text-indent: -999px; - cursor: pointer; - background-color: #fff; - background-clip: padding-box; - border-top: 10px solid transparent; - border-bottom: 10px solid transparent; - opacity: .5; - -webkit-transition: opacity 0.6s ease; - transition: opacity 0.6s ease; -} - -@media (prefers-reduced-motion: reduce) { - .carousel-indicators li { - -webkit-transition: none; - transition: none; - } -} - -.carousel-indicators .active { - opacity: 1; -} - -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; -} - -@-webkit-keyframes spinner-border { - to { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - -@keyframes spinner-border { - to { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - -.spinner-border { - display: inline-block; - width: 2rem; - height: 2rem; - vertical-align: text-bottom; - border: 0.25em solid currentColor; - border-right-color: transparent; - border-radius: 50%; - -webkit-animation: spinner-border .75s linear infinite; - animation: spinner-border .75s linear infinite; -} - -.spinner-border-sm { - width: 1rem; - height: 1rem; - border-width: 0.2em; -} - -@-webkit-keyframes spinner-grow { - 0% { - -webkit-transform: scale(0); - transform: scale(0); - } - 50% { - opacity: 1; - } -} - -@keyframes spinner-grow { - 0% { - -webkit-transform: scale(0); - transform: scale(0); - } - 50% { - opacity: 1; - } -} - -.spinner-grow { - display: inline-block; - width: 2rem; - height: 2rem; - vertical-align: text-bottom; - background-color: currentColor; - border-radius: 50%; - opacity: 0; - -webkit-animation: spinner-grow .75s linear infinite; - animation: spinner-grow .75s linear infinite; -} - -.spinner-grow-sm { - width: 1rem; - height: 1rem; -} - -.align-baseline { - vertical-align: baseline !important; -} - -.align-top { - vertical-align: top !important; -} - -.align-middle { - vertical-align: middle !important; -} - -.align-bottom { - vertical-align: bottom !important; -} - -.align-text-bottom { - vertical-align: text-bottom !important; -} - -.align-text-top { - vertical-align: text-top !important; -} - -.bg-primary { - background-color: #2A9FD6 !important; -} - -a.bg-primary:hover, a.bg-primary:focus, -button.bg-primary:hover, -button.bg-primary:focus { - background-color: #2180ac !important; -} - -.bg-secondary { - background-color: #555 !important; -} - -a.bg-secondary:hover, a.bg-secondary:focus, -button.bg-secondary:hover, -button.bg-secondary:focus { - background-color: #3c3b3b !important; -} - -.bg-success { - background-color: #77B300 !important; -} - -a.bg-success:hover, a.bg-success:focus, -button.bg-success:hover, -button.bg-success:focus { - background-color: #558000 !important; -} - -.bg-info { - background-color: #9933CC !important; -} - -a.bg-info:hover, a.bg-info:focus, -button.bg-info:hover, -button.bg-info:focus { - background-color: #7a29a3 !important; -} - -.bg-warning { - background-color: #FF8800 !important; -} - -a.bg-warning:hover, a.bg-warning:focus, -button.bg-warning:hover, -button.bg-warning:focus { - background-color: #cc6d00 !important; -} - -.bg-danger { - background-color: #CC0000 !important; -} - -a.bg-danger:hover, a.bg-danger:focus, -button.bg-danger:hover, -button.bg-danger:focus { - background-color: #990000 !important; -} - -.bg-light { - background-color: #222 !important; -} - -a.bg-light:hover, a.bg-light:focus, -button.bg-light:hover, -button.bg-light:focus { - background-color: #090808 !important; -} - -.bg-dark { - background-color: #ADAFAE !important; -} - -a.bg-dark:hover, a.bg-dark:focus, -button.bg-dark:hover, -button.bg-dark:focus { - background-color: #939695 !important; -} - -.bg-white { - background-color: #fff !important; -} - -.bg-transparent { - background-color: transparent !important; -} - -.border { - border: 1px solid #dee2e6 !important; -} - -.border-top { - border-top: 1px solid #dee2e6 !important; -} - -.border-right { - border-right: 1px solid #dee2e6 !important; -} - -.border-bottom { - border-bottom: 1px solid #dee2e6 !important; -} - -.border-left { - border-left: 1px solid #dee2e6 !important; -} - -.border-0 { - border: 0 !important; -} - -.border-top-0 { - border-top: 0 !important; -} - -.border-right-0 { - border-right: 0 !important; -} - -.border-bottom-0 { - border-bottom: 0 !important; -} - -.border-left-0 { - border-left: 0 !important; -} - -.border-primary { - border-color: #2A9FD6 !important; -} - -.border-secondary { - border-color: #555 !important; -} - -.border-success { - border-color: #77B300 !important; -} - -.border-info { - border-color: #9933CC !important; -} - -.border-warning { - border-color: #FF8800 !important; -} - -.border-danger { - border-color: #CC0000 !important; -} - -.border-light { - border-color: #222 !important; -} - -.border-dark { - border-color: #ADAFAE !important; -} - -.border-white { - border-color: #fff !important; -} - -.rounded-sm { - border-radius: 0.2rem !important; -} - -.rounded { - border-radius: 0.25rem !important; -} - -.rounded-top { - border-top-left-radius: 0.25rem !important; - border-top-right-radius: 0.25rem !important; -} - -.rounded-right { - border-top-right-radius: 0.25rem !important; - border-bottom-right-radius: 0.25rem !important; -} - -.rounded-bottom { - border-bottom-right-radius: 0.25rem !important; - border-bottom-left-radius: 0.25rem !important; -} - -.rounded-left { - border-top-left-radius: 0.25rem !important; - border-bottom-left-radius: 0.25rem !important; -} - -.rounded-lg { - border-radius: 0.3rem !important; -} - -.rounded-circle { - border-radius: 50% !important; -} - -.rounded-pill { - border-radius: 50rem !important; -} - -.rounded-0 { - border-radius: 0 !important; -} - -.clearfix::after { - display: block; - clear: both; - content: ""; -} - -.d-none { - display: none !important; -} - -.d-inline { - display: inline !important; -} - -.d-inline-block { - display: inline-block !important; -} - -.d-block { - display: block !important; -} - -.d-table { - display: table !important; -} - -.d-table-row { - display: table-row !important; -} - -.d-table-cell { - display: table-cell !important; -} - -.d-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; -} - -.d-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; -} - -@media (min-width: 576px) { - .d-sm-none { - display: none !important; - } - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - } - .d-sm-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 768px) { - .d-md-none { - display: none !important; - } - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - } - .d-md-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 992px) { - .d-lg-none { - display: none !important; - } - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - } - .d-lg-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 1200px) { - .d-xl-none { - display: none !important; - } - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - } - .d-xl-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media print { - .d-print-none { - display: none !important; - } - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - } - .d-print-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -.embed-responsive { - position: relative; - display: block; - width: 100%; - padding: 0; - overflow: hidden; -} - -.embed-responsive::before { - display: block; - content: ""; -} - -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object, -.embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} - -.embed-responsive-21by9::before { - padding-top: 42.8571428571%; -} - -.embed-responsive-16by9::before { - padding-top: 56.25%; -} - -.embed-responsive-4by3::before { - padding-top: 75%; -} - -.embed-responsive-1by1::before { - padding-top: 100%; -} - -.flex-row { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: row !important; - flex-direction: row !important; -} - -.flex-column { - -webkit-box-orient: vertical !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: column !important; - flex-direction: column !important; -} - -.flex-row-reverse { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; -} - -.flex-column-reverse { - -webkit-box-orient: vertical !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; -} - -.flex-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; -} - -.flex-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; -} - -.flex-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; -} - -.flex-fill { - -webkit-box-flex: 1 !important; - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; -} - -.flex-grow-0 { - -webkit-box-flex: 0 !important; - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; -} - -.flex-grow-1 { - -webkit-box-flex: 1 !important; - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; -} - -.flex-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; -} - -.flex-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; -} - -.justify-content-start { - -webkit-box-pack: start !important; - -ms-flex-pack: start !important; - justify-content: flex-start !important; -} - -.justify-content-end { - -webkit-box-pack: end !important; - -ms-flex-pack: end !important; - justify-content: flex-end !important; -} - -.justify-content-center { - -webkit-box-pack: center !important; - -ms-flex-pack: center !important; - justify-content: center !important; -} - -.justify-content-between { - -webkit-box-pack: justify !important; - -ms-flex-pack: justify !important; - justify-content: space-between !important; -} - -.justify-content-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; -} - -.align-items-start { - -webkit-box-align: start !important; - -ms-flex-align: start !important; - align-items: flex-start !important; -} - -.align-items-end { - -webkit-box-align: end !important; - -ms-flex-align: end !important; - align-items: flex-end !important; -} - -.align-items-center { - -webkit-box-align: center !important; - -ms-flex-align: center !important; - align-items: center !important; -} - -.align-items-baseline { - -webkit-box-align: baseline !important; - -ms-flex-align: baseline !important; - align-items: baseline !important; -} - -.align-items-stretch { - -webkit-box-align: stretch !important; - -ms-flex-align: stretch !important; - align-items: stretch !important; -} - -.align-content-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; -} - -.align-content-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; -} - -.align-content-center { - -ms-flex-line-pack: center !important; - align-content: center !important; -} - -.align-content-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; -} - -.align-content-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; -} - -.align-content-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; -} - -.align-self-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; -} - -.align-self-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; -} - -.align-self-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; -} - -.align-self-center { - -ms-flex-item-align: center !important; - align-self: center !important; -} - -.align-self-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; -} - -.align-self-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; -} - -@media (min-width: 576px) { - .flex-sm-row { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-sm-column { - -webkit-box-orient: vertical !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-sm-row-reverse { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - -webkit-box-orient: vertical !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-sm-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-sm-fill { - -webkit-box-flex: 1 !important; - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-sm-grow-0 { - -webkit-box-flex: 0 !important; - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - -webkit-box-flex: 1 !important; - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-sm-start { - -webkit-box-pack: start !important; - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-sm-end { - -webkit-box-pack: end !important; - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-sm-center { - -webkit-box-pack: center !important; - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-sm-between { - -webkit-box-pack: justify !important; - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-sm-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-sm-start { - -webkit-box-align: start !important; - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-sm-end { - -webkit-box-align: end !important; - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-sm-center { - -webkit-box-align: center !important; - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-sm-baseline { - -webkit-box-align: baseline !important; - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-sm-stretch { - -webkit-box-align: stretch !important; - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-sm-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-sm-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-sm-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-sm-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-sm-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-sm-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-sm-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-sm-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-sm-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-sm-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-sm-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-sm-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 768px) { - .flex-md-row { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-md-column { - -webkit-box-orient: vertical !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-md-row-reverse { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - -webkit-box-orient: vertical !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-md-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-md-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-md-fill { - -webkit-box-flex: 1 !important; - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-md-grow-0 { - -webkit-box-flex: 0 !important; - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-md-grow-1 { - -webkit-box-flex: 1 !important; - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-md-start { - -webkit-box-pack: start !important; - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-md-end { - -webkit-box-pack: end !important; - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-md-center { - -webkit-box-pack: center !important; - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-md-between { - -webkit-box-pack: justify !important; - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-md-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-md-start { - -webkit-box-align: start !important; - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-md-end { - -webkit-box-align: end !important; - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-md-center { - -webkit-box-align: center !important; - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-md-baseline { - -webkit-box-align: baseline !important; - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-md-stretch { - -webkit-box-align: stretch !important; - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-md-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-md-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-md-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-md-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-md-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-md-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-md-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-md-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-md-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-md-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-md-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-md-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 992px) { - .flex-lg-row { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-lg-column { - -webkit-box-orient: vertical !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-lg-row-reverse { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - -webkit-box-orient: vertical !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-lg-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-lg-fill { - -webkit-box-flex: 1 !important; - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-lg-grow-0 { - -webkit-box-flex: 0 !important; - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - -webkit-box-flex: 1 !important; - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-lg-start { - -webkit-box-pack: start !important; - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-lg-end { - -webkit-box-pack: end !important; - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-lg-center { - -webkit-box-pack: center !important; - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-lg-between { - -webkit-box-pack: justify !important; - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-lg-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-lg-start { - -webkit-box-align: start !important; - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-lg-end { - -webkit-box-align: end !important; - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-lg-center { - -webkit-box-align: center !important; - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-lg-baseline { - -webkit-box-align: baseline !important; - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-lg-stretch { - -webkit-box-align: stretch !important; - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-lg-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-lg-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-lg-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-lg-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-lg-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-lg-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-lg-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-lg-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-lg-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-lg-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-lg-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-lg-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 1200px) { - .flex-xl-row { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-xl-column { - -webkit-box-orient: vertical !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-xl-row-reverse { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - -webkit-box-orient: vertical !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-xl-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-xl-fill { - -webkit-box-flex: 1 !important; - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-xl-grow-0 { - -webkit-box-flex: 0 !important; - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - -webkit-box-flex: 1 !important; - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-xl-start { - -webkit-box-pack: start !important; - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-xl-end { - -webkit-box-pack: end !important; - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-xl-center { - -webkit-box-pack: center !important; - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-xl-between { - -webkit-box-pack: justify !important; - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-xl-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-xl-start { - -webkit-box-align: start !important; - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-xl-end { - -webkit-box-align: end !important; - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-xl-center { - -webkit-box-align: center !important; - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-xl-baseline { - -webkit-box-align: baseline !important; - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-xl-stretch { - -webkit-box-align: stretch !important; - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-xl-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-xl-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-xl-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-xl-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-xl-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-xl-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-xl-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-xl-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-xl-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-xl-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-xl-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-xl-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -.float-left { - float: left !important; -} - -.float-right { - float: right !important; -} - -.float-none { - float: none !important; -} - -@media (min-width: 576px) { - .float-sm-left { - float: left !important; - } - .float-sm-right { - float: right !important; - } - .float-sm-none { - float: none !important; - } -} - -@media (min-width: 768px) { - .float-md-left { - float: left !important; - } - .float-md-right { - float: right !important; - } - .float-md-none { - float: none !important; - } -} - -@media (min-width: 992px) { - .float-lg-left { - float: left !important; - } - .float-lg-right { - float: right !important; - } - .float-lg-none { - float: none !important; - } -} - -@media (min-width: 1200px) { - .float-xl-left { - float: left !important; - } - .float-xl-right { - float: right !important; - } - .float-xl-none { - float: none !important; - } -} - -.overflow-auto { - overflow: auto !important; -} - -.overflow-hidden { - overflow: hidden !important; -} - -.position-static { - position: static !important; -} - -.position-relative { - position: relative !important; -} - -.position-absolute { - position: absolute !important; -} - -.position-fixed { - position: fixed !important; -} - -.position-sticky { - position: -webkit-sticky !important; - position: sticky !important; -} - -.fixed-top { - position: fixed; - top: 0; - right: 0; - left: 0; - z-index: 1030; -} - -.fixed-bottom { - position: fixed; - right: 0; - bottom: 0; - left: 0; - z-index: 1030; -} - -@supports ((position: -webkit-sticky) or (position: sticky)) { - .sticky-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } -} - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - -.sr-only-focusable:active, .sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - overflow: visible; - clip: auto; - white-space: normal; -} - -.shadow-sm { - -webkit-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; -} - -.shadow { - -webkit-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; - box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; -} - -.shadow-lg { - -webkit-box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; - box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; -} - -.shadow-none { - -webkit-box-shadow: none !important; - box-shadow: none !important; -} - -.w-25 { - width: 25% !important; -} - -.w-50 { - width: 50% !important; -} - -.w-75 { - width: 75% !important; -} - -.w-100 { - width: 100% !important; -} - -.w-auto { - width: auto !important; -} - -.h-25 { - height: 25% !important; -} - -.h-50 { - height: 50% !important; -} - -.h-75 { - height: 75% !important; -} - -.h-100 { - height: 100% !important; -} - -.h-auto { - height: auto !important; -} - -.mw-100 { - max-width: 100% !important; -} - -.mh-100 { - max-height: 100% !important; -} - -.min-vw-100 { - min-width: 100vw !important; -} - -.min-vh-100 { - min-height: 100vh !important; -} - -.vw-100 { - width: 100vw !important; -} - -.vh-100 { - height: 100vh !important; -} - -.stretched-link::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1; - pointer-events: auto; - content: ""; - background-color: rgba(0, 0, 0, 0); -} - -.m-0 { - margin: 0 !important; -} - -.mt-0, -.my-0 { - margin-top: 0 !important; -} - -.mr-0, -.mx-0 { - margin-right: 0 !important; -} - -.mb-0, -.my-0 { - margin-bottom: 0 !important; -} - -.ml-0, -.mx-0 { - margin-left: 0 !important; -} - -.m-1 { - margin: 0.25rem !important; -} - -.mt-1, -.my-1 { - margin-top: 0.25rem !important; -} - -.mr-1, -.mx-1 { - margin-right: 0.25rem !important; -} - -.mb-1, -.my-1 { - margin-bottom: 0.25rem !important; -} - -.ml-1, -.mx-1 { - margin-left: 0.25rem !important; -} - -.m-2 { - margin: 0.5rem !important; -} - -.mt-2, -.my-2 { - margin-top: 0.5rem !important; -} - -.mr-2, -.mx-2 { - margin-right: 0.5rem !important; -} - -.mb-2, -.my-2 { - margin-bottom: 0.5rem !important; -} - -.ml-2, -.mx-2 { - margin-left: 0.5rem !important; -} - -.m-3 { - margin: 1rem !important; -} - -.mt-3, -.my-3 { - margin-top: 1rem !important; -} - -.mr-3, -.mx-3 { - margin-right: 1rem !important; -} - -.mb-3, -.my-3 { - margin-bottom: 1rem !important; -} - -.ml-3, -.mx-3 { - margin-left: 1rem !important; -} - -.m-4 { - margin: 1.5rem !important; -} - -.mt-4, -.my-4 { - margin-top: 1.5rem !important; -} - -.mr-4, -.mx-4 { - margin-right: 1.5rem !important; -} - -.mb-4, -.my-4 { - margin-bottom: 1.5rem !important; -} - -.ml-4, -.mx-4 { - margin-left: 1.5rem !important; -} - -.m-5 { - margin: 3rem !important; -} - -.mt-5, -.my-5 { - margin-top: 3rem !important; -} - -.mr-5, -.mx-5 { - margin-right: 3rem !important; -} - -.mb-5, -.my-5 { - margin-bottom: 3rem !important; -} - -.ml-5, -.mx-5 { - margin-left: 3rem !important; -} - -.p-0 { - padding: 0 !important; -} - -.pt-0, -.py-0 { - padding-top: 0 !important; -} - -.pr-0, -.px-0 { - padding-right: 0 !important; -} - -.pb-0, -.py-0 { - padding-bottom: 0 !important; -} - -.pl-0, -.px-0 { - padding-left: 0 !important; -} - -.p-1 { - padding: 0.25rem !important; -} - -.pt-1, -.py-1 { - padding-top: 0.25rem !important; -} - -.pr-1, -.px-1 { - padding-right: 0.25rem !important; -} - -.pb-1, -.py-1 { - padding-bottom: 0.25rem !important; -} - -.pl-1, -.px-1 { - padding-left: 0.25rem !important; -} - -.p-2 { - padding: 0.5rem !important; -} - -.pt-2, -.py-2 { - padding-top: 0.5rem !important; -} - -.pr-2, -.px-2 { - padding-right: 0.5rem !important; -} - -.pb-2, -.py-2 { - padding-bottom: 0.5rem !important; -} - -.pl-2, -.px-2 { - padding-left: 0.5rem !important; -} - -.p-3 { - padding: 1rem !important; -} - -.pt-3, -.py-3 { - padding-top: 1rem !important; -} - -.pr-3, -.px-3 { - padding-right: 1rem !important; -} - -.pb-3, -.py-3 { - padding-bottom: 1rem !important; -} - -.pl-3, -.px-3 { - padding-left: 1rem !important; -} - -.p-4 { - padding: 1.5rem !important; -} - -.pt-4, -.py-4 { - padding-top: 1.5rem !important; -} - -.pr-4, -.px-4 { - padding-right: 1.5rem !important; -} - -.pb-4, -.py-4 { - padding-bottom: 1.5rem !important; -} - -.pl-4, -.px-4 { - padding-left: 1.5rem !important; -} - -.p-5 { - padding: 3rem !important; -} - -.pt-5, -.py-5 { - padding-top: 3rem !important; -} - -.pr-5, -.px-5 { - padding-right: 3rem !important; -} - -.pb-5, -.py-5 { - padding-bottom: 3rem !important; -} - -.pl-5, -.px-5 { - padding-left: 3rem !important; -} - -.m-n1 { - margin: -0.25rem !important; -} - -.mt-n1, -.my-n1 { - margin-top: -0.25rem !important; -} - -.mr-n1, -.mx-n1 { - margin-right: -0.25rem !important; -} - -.mb-n1, -.my-n1 { - margin-bottom: -0.25rem !important; -} - -.ml-n1, -.mx-n1 { - margin-left: -0.25rem !important; -} - -.m-n2 { - margin: -0.5rem !important; -} - -.mt-n2, -.my-n2 { - margin-top: -0.5rem !important; -} - -.mr-n2, -.mx-n2 { - margin-right: -0.5rem !important; -} - -.mb-n2, -.my-n2 { - margin-bottom: -0.5rem !important; -} - -.ml-n2, -.mx-n2 { - margin-left: -0.5rem !important; -} - -.m-n3 { - margin: -1rem !important; -} - -.mt-n3, -.my-n3 { - margin-top: -1rem !important; -} - -.mr-n3, -.mx-n3 { - margin-right: -1rem !important; -} - -.mb-n3, -.my-n3 { - margin-bottom: -1rem !important; -} - -.ml-n3, -.mx-n3 { - margin-left: -1rem !important; -} - -.m-n4 { - margin: -1.5rem !important; -} - -.mt-n4, -.my-n4 { - margin-top: -1.5rem !important; -} - -.mr-n4, -.mx-n4 { - margin-right: -1.5rem !important; -} - -.mb-n4, -.my-n4 { - margin-bottom: -1.5rem !important; -} - -.ml-n4, -.mx-n4 { - margin-left: -1.5rem !important; -} - -.m-n5 { - margin: -3rem !important; -} - -.mt-n5, -.my-n5 { - margin-top: -3rem !important; -} - -.mr-n5, -.mx-n5 { - margin-right: -3rem !important; -} - -.mb-n5, -.my-n5 { - margin-bottom: -3rem !important; -} - -.ml-n5, -.mx-n5 { - margin-left: -3rem !important; -} - -.m-auto { - margin: auto !important; -} - -.mt-auto, -.my-auto { - margin-top: auto !important; -} - -.mr-auto, -.mx-auto { - margin-right: auto !important; -} - -.mb-auto, -.my-auto { - margin-bottom: auto !important; -} - -.ml-auto, -.mx-auto { - margin-left: auto !important; -} - -@media (min-width: 576px) { - .m-sm-0 { - margin: 0 !important; - } - .mt-sm-0, - .my-sm-0 { - margin-top: 0 !important; - } - .mr-sm-0, - .mx-sm-0 { - margin-right: 0 !important; - } - .mb-sm-0, - .my-sm-0 { - margin-bottom: 0 !important; - } - .ml-sm-0, - .mx-sm-0 { - margin-left: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .mt-sm-1, - .my-sm-1 { - margin-top: 0.25rem !important; - } - .mr-sm-1, - .mx-sm-1 { - margin-right: 0.25rem !important; - } - .mb-sm-1, - .my-sm-1 { - margin-bottom: 0.25rem !important; - } - .ml-sm-1, - .mx-sm-1 { - margin-left: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .mt-sm-2, - .my-sm-2 { - margin-top: 0.5rem !important; - } - .mr-sm-2, - .mx-sm-2 { - margin-right: 0.5rem !important; - } - .mb-sm-2, - .my-sm-2 { - margin-bottom: 0.5rem !important; - } - .ml-sm-2, - .mx-sm-2 { - margin-left: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .mt-sm-3, - .my-sm-3 { - margin-top: 1rem !important; - } - .mr-sm-3, - .mx-sm-3 { - margin-right: 1rem !important; - } - .mb-sm-3, - .my-sm-3 { - margin-bottom: 1rem !important; - } - .ml-sm-3, - .mx-sm-3 { - margin-left: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .mt-sm-4, - .my-sm-4 { - margin-top: 1.5rem !important; - } - .mr-sm-4, - .mx-sm-4 { - margin-right: 1.5rem !important; - } - .mb-sm-4, - .my-sm-4 { - margin-bottom: 1.5rem !important; - } - .ml-sm-4, - .mx-sm-4 { - margin-left: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .mt-sm-5, - .my-sm-5 { - margin-top: 3rem !important; - } - .mr-sm-5, - .mx-sm-5 { - margin-right: 3rem !important; - } - .mb-sm-5, - .my-sm-5 { - margin-bottom: 3rem !important; - } - .ml-sm-5, - .mx-sm-5 { - margin-left: 3rem !important; - } - .p-sm-0 { - padding: 0 !important; - } - .pt-sm-0, - .py-sm-0 { - padding-top: 0 !important; - } - .pr-sm-0, - .px-sm-0 { - padding-right: 0 !important; - } - .pb-sm-0, - .py-sm-0 { - padding-bottom: 0 !important; - } - .pl-sm-0, - .px-sm-0 { - padding-left: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .pt-sm-1, - .py-sm-1 { - padding-top: 0.25rem !important; - } - .pr-sm-1, - .px-sm-1 { - padding-right: 0.25rem !important; - } - .pb-sm-1, - .py-sm-1 { - padding-bottom: 0.25rem !important; - } - .pl-sm-1, - .px-sm-1 { - padding-left: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .pt-sm-2, - .py-sm-2 { - padding-top: 0.5rem !important; - } - .pr-sm-2, - .px-sm-2 { - padding-right: 0.5rem !important; - } - .pb-sm-2, - .py-sm-2 { - padding-bottom: 0.5rem !important; - } - .pl-sm-2, - .px-sm-2 { - padding-left: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .pt-sm-3, - .py-sm-3 { - padding-top: 1rem !important; - } - .pr-sm-3, - .px-sm-3 { - padding-right: 1rem !important; - } - .pb-sm-3, - .py-sm-3 { - padding-bottom: 1rem !important; - } - .pl-sm-3, - .px-sm-3 { - padding-left: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .pt-sm-4, - .py-sm-4 { - padding-top: 1.5rem !important; - } - .pr-sm-4, - .px-sm-4 { - padding-right: 1.5rem !important; - } - .pb-sm-4, - .py-sm-4 { - padding-bottom: 1.5rem !important; - } - .pl-sm-4, - .px-sm-4 { - padding-left: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .pt-sm-5, - .py-sm-5 { - padding-top: 3rem !important; - } - .pr-sm-5, - .px-sm-5 { - padding-right: 3rem !important; - } - .pb-sm-5, - .py-sm-5 { - padding-bottom: 3rem !important; - } - .pl-sm-5, - .px-sm-5 { - padding-left: 3rem !important; - } - .m-sm-n1 { - margin: -0.25rem !important; - } - .mt-sm-n1, - .my-sm-n1 { - margin-top: -0.25rem !important; - } - .mr-sm-n1, - .mx-sm-n1 { - margin-right: -0.25rem !important; - } - .mb-sm-n1, - .my-sm-n1 { - margin-bottom: -0.25rem !important; - } - .ml-sm-n1, - .mx-sm-n1 { - margin-left: -0.25rem !important; - } - .m-sm-n2 { - margin: -0.5rem !important; - } - .mt-sm-n2, - .my-sm-n2 { - margin-top: -0.5rem !important; - } - .mr-sm-n2, - .mx-sm-n2 { - margin-right: -0.5rem !important; - } - .mb-sm-n2, - .my-sm-n2 { - margin-bottom: -0.5rem !important; - } - .ml-sm-n2, - .mx-sm-n2 { - margin-left: -0.5rem !important; - } - .m-sm-n3 { - margin: -1rem !important; - } - .mt-sm-n3, - .my-sm-n3 { - margin-top: -1rem !important; - } - .mr-sm-n3, - .mx-sm-n3 { - margin-right: -1rem !important; - } - .mb-sm-n3, - .my-sm-n3 { - margin-bottom: -1rem !important; - } - .ml-sm-n3, - .mx-sm-n3 { - margin-left: -1rem !important; - } - .m-sm-n4 { - margin: -1.5rem !important; - } - .mt-sm-n4, - .my-sm-n4 { - margin-top: -1.5rem !important; - } - .mr-sm-n4, - .mx-sm-n4 { - margin-right: -1.5rem !important; - } - .mb-sm-n4, - .my-sm-n4 { - margin-bottom: -1.5rem !important; - } - .ml-sm-n4, - .mx-sm-n4 { - margin-left: -1.5rem !important; - } - .m-sm-n5 { - margin: -3rem !important; - } - .mt-sm-n5, - .my-sm-n5 { - margin-top: -3rem !important; - } - .mr-sm-n5, - .mx-sm-n5 { - margin-right: -3rem !important; - } - .mb-sm-n5, - .my-sm-n5 { - margin-bottom: -3rem !important; - } - .ml-sm-n5, - .mx-sm-n5 { - margin-left: -3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mt-sm-auto, - .my-sm-auto { - margin-top: auto !important; - } - .mr-sm-auto, - .mx-sm-auto { - margin-right: auto !important; - } - .mb-sm-auto, - .my-sm-auto { - margin-bottom: auto !important; - } - .ml-sm-auto, - .mx-sm-auto { - margin-left: auto !important; - } -} - -@media (min-width: 768px) { - .m-md-0 { - margin: 0 !important; - } - .mt-md-0, - .my-md-0 { - margin-top: 0 !important; - } - .mr-md-0, - .mx-md-0 { - margin-right: 0 !important; - } - .mb-md-0, - .my-md-0 { - margin-bottom: 0 !important; - } - .ml-md-0, - .mx-md-0 { - margin-left: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .mt-md-1, - .my-md-1 { - margin-top: 0.25rem !important; - } - .mr-md-1, - .mx-md-1 { - margin-right: 0.25rem !important; - } - .mb-md-1, - .my-md-1 { - margin-bottom: 0.25rem !important; - } - .ml-md-1, - .mx-md-1 { - margin-left: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .mt-md-2, - .my-md-2 { - margin-top: 0.5rem !important; - } - .mr-md-2, - .mx-md-2 { - margin-right: 0.5rem !important; - } - .mb-md-2, - .my-md-2 { - margin-bottom: 0.5rem !important; - } - .ml-md-2, - .mx-md-2 { - margin-left: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .mt-md-3, - .my-md-3 { - margin-top: 1rem !important; - } - .mr-md-3, - .mx-md-3 { - margin-right: 1rem !important; - } - .mb-md-3, - .my-md-3 { - margin-bottom: 1rem !important; - } - .ml-md-3, - .mx-md-3 { - margin-left: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .mt-md-4, - .my-md-4 { - margin-top: 1.5rem !important; - } - .mr-md-4, - .mx-md-4 { - margin-right: 1.5rem !important; - } - .mb-md-4, - .my-md-4 { - margin-bottom: 1.5rem !important; - } - .ml-md-4, - .mx-md-4 { - margin-left: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .mt-md-5, - .my-md-5 { - margin-top: 3rem !important; - } - .mr-md-5, - .mx-md-5 { - margin-right: 3rem !important; - } - .mb-md-5, - .my-md-5 { - margin-bottom: 3rem !important; - } - .ml-md-5, - .mx-md-5 { - margin-left: 3rem !important; - } - .p-md-0 { - padding: 0 !important; - } - .pt-md-0, - .py-md-0 { - padding-top: 0 !important; - } - .pr-md-0, - .px-md-0 { - padding-right: 0 !important; - } - .pb-md-0, - .py-md-0 { - padding-bottom: 0 !important; - } - .pl-md-0, - .px-md-0 { - padding-left: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .pt-md-1, - .py-md-1 { - padding-top: 0.25rem !important; - } - .pr-md-1, - .px-md-1 { - padding-right: 0.25rem !important; - } - .pb-md-1, - .py-md-1 { - padding-bottom: 0.25rem !important; - } - .pl-md-1, - .px-md-1 { - padding-left: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .pt-md-2, - .py-md-2 { - padding-top: 0.5rem !important; - } - .pr-md-2, - .px-md-2 { - padding-right: 0.5rem !important; - } - .pb-md-2, - .py-md-2 { - padding-bottom: 0.5rem !important; - } - .pl-md-2, - .px-md-2 { - padding-left: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .pt-md-3, - .py-md-3 { - padding-top: 1rem !important; - } - .pr-md-3, - .px-md-3 { - padding-right: 1rem !important; - } - .pb-md-3, - .py-md-3 { - padding-bottom: 1rem !important; - } - .pl-md-3, - .px-md-3 { - padding-left: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .pt-md-4, - .py-md-4 { - padding-top: 1.5rem !important; - } - .pr-md-4, - .px-md-4 { - padding-right: 1.5rem !important; - } - .pb-md-4, - .py-md-4 { - padding-bottom: 1.5rem !important; - } - .pl-md-4, - .px-md-4 { - padding-left: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .pt-md-5, - .py-md-5 { - padding-top: 3rem !important; - } - .pr-md-5, - .px-md-5 { - padding-right: 3rem !important; - } - .pb-md-5, - .py-md-5 { - padding-bottom: 3rem !important; - } - .pl-md-5, - .px-md-5 { - padding-left: 3rem !important; - } - .m-md-n1 { - margin: -0.25rem !important; - } - .mt-md-n1, - .my-md-n1 { - margin-top: -0.25rem !important; - } - .mr-md-n1, - .mx-md-n1 { - margin-right: -0.25rem !important; - } - .mb-md-n1, - .my-md-n1 { - margin-bottom: -0.25rem !important; - } - .ml-md-n1, - .mx-md-n1 { - margin-left: -0.25rem !important; - } - .m-md-n2 { - margin: -0.5rem !important; - } - .mt-md-n2, - .my-md-n2 { - margin-top: -0.5rem !important; - } - .mr-md-n2, - .mx-md-n2 { - margin-right: -0.5rem !important; - } - .mb-md-n2, - .my-md-n2 { - margin-bottom: -0.5rem !important; - } - .ml-md-n2, - .mx-md-n2 { - margin-left: -0.5rem !important; - } - .m-md-n3 { - margin: -1rem !important; - } - .mt-md-n3, - .my-md-n3 { - margin-top: -1rem !important; - } - .mr-md-n3, - .mx-md-n3 { - margin-right: -1rem !important; - } - .mb-md-n3, - .my-md-n3 { - margin-bottom: -1rem !important; - } - .ml-md-n3, - .mx-md-n3 { - margin-left: -1rem !important; - } - .m-md-n4 { - margin: -1.5rem !important; - } - .mt-md-n4, - .my-md-n4 { - margin-top: -1.5rem !important; - } - .mr-md-n4, - .mx-md-n4 { - margin-right: -1.5rem !important; - } - .mb-md-n4, - .my-md-n4 { - margin-bottom: -1.5rem !important; - } - .ml-md-n4, - .mx-md-n4 { - margin-left: -1.5rem !important; - } - .m-md-n5 { - margin: -3rem !important; - } - .mt-md-n5, - .my-md-n5 { - margin-top: -3rem !important; - } - .mr-md-n5, - .mx-md-n5 { - margin-right: -3rem !important; - } - .mb-md-n5, - .my-md-n5 { - margin-bottom: -3rem !important; - } - .ml-md-n5, - .mx-md-n5 { - margin-left: -3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mt-md-auto, - .my-md-auto { - margin-top: auto !important; - } - .mr-md-auto, - .mx-md-auto { - margin-right: auto !important; - } - .mb-md-auto, - .my-md-auto { - margin-bottom: auto !important; - } - .ml-md-auto, - .mx-md-auto { - margin-left: auto !important; - } -} - -@media (min-width: 992px) { - .m-lg-0 { - margin: 0 !important; - } - .mt-lg-0, - .my-lg-0 { - margin-top: 0 !important; - } - .mr-lg-0, - .mx-lg-0 { - margin-right: 0 !important; - } - .mb-lg-0, - .my-lg-0 { - margin-bottom: 0 !important; - } - .ml-lg-0, - .mx-lg-0 { - margin-left: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .mt-lg-1, - .my-lg-1 { - margin-top: 0.25rem !important; - } - .mr-lg-1, - .mx-lg-1 { - margin-right: 0.25rem !important; - } - .mb-lg-1, - .my-lg-1 { - margin-bottom: 0.25rem !important; - } - .ml-lg-1, - .mx-lg-1 { - margin-left: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .mt-lg-2, - .my-lg-2 { - margin-top: 0.5rem !important; - } - .mr-lg-2, - .mx-lg-2 { - margin-right: 0.5rem !important; - } - .mb-lg-2, - .my-lg-2 { - margin-bottom: 0.5rem !important; - } - .ml-lg-2, - .mx-lg-2 { - margin-left: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .mt-lg-3, - .my-lg-3 { - margin-top: 1rem !important; - } - .mr-lg-3, - .mx-lg-3 { - margin-right: 1rem !important; - } - .mb-lg-3, - .my-lg-3 { - margin-bottom: 1rem !important; - } - .ml-lg-3, - .mx-lg-3 { - margin-left: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .mt-lg-4, - .my-lg-4 { - margin-top: 1.5rem !important; - } - .mr-lg-4, - .mx-lg-4 { - margin-right: 1.5rem !important; - } - .mb-lg-4, - .my-lg-4 { - margin-bottom: 1.5rem !important; - } - .ml-lg-4, - .mx-lg-4 { - margin-left: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .mt-lg-5, - .my-lg-5 { - margin-top: 3rem !important; - } - .mr-lg-5, - .mx-lg-5 { - margin-right: 3rem !important; - } - .mb-lg-5, - .my-lg-5 { - margin-bottom: 3rem !important; - } - .ml-lg-5, - .mx-lg-5 { - margin-left: 3rem !important; - } - .p-lg-0 { - padding: 0 !important; - } - .pt-lg-0, - .py-lg-0 { - padding-top: 0 !important; - } - .pr-lg-0, - .px-lg-0 { - padding-right: 0 !important; - } - .pb-lg-0, - .py-lg-0 { - padding-bottom: 0 !important; - } - .pl-lg-0, - .px-lg-0 { - padding-left: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .pt-lg-1, - .py-lg-1 { - padding-top: 0.25rem !important; - } - .pr-lg-1, - .px-lg-1 { - padding-right: 0.25rem !important; - } - .pb-lg-1, - .py-lg-1 { - padding-bottom: 0.25rem !important; - } - .pl-lg-1, - .px-lg-1 { - padding-left: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .pt-lg-2, - .py-lg-2 { - padding-top: 0.5rem !important; - } - .pr-lg-2, - .px-lg-2 { - padding-right: 0.5rem !important; - } - .pb-lg-2, - .py-lg-2 { - padding-bottom: 0.5rem !important; - } - .pl-lg-2, - .px-lg-2 { - padding-left: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .pt-lg-3, - .py-lg-3 { - padding-top: 1rem !important; - } - .pr-lg-3, - .px-lg-3 { - padding-right: 1rem !important; - } - .pb-lg-3, - .py-lg-3 { - padding-bottom: 1rem !important; - } - .pl-lg-3, - .px-lg-3 { - padding-left: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .pt-lg-4, - .py-lg-4 { - padding-top: 1.5rem !important; - } - .pr-lg-4, - .px-lg-4 { - padding-right: 1.5rem !important; - } - .pb-lg-4, - .py-lg-4 { - padding-bottom: 1.5rem !important; - } - .pl-lg-4, - .px-lg-4 { - padding-left: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .pt-lg-5, - .py-lg-5 { - padding-top: 3rem !important; - } - .pr-lg-5, - .px-lg-5 { - padding-right: 3rem !important; - } - .pb-lg-5, - .py-lg-5 { - padding-bottom: 3rem !important; - } - .pl-lg-5, - .px-lg-5 { - padding-left: 3rem !important; - } - .m-lg-n1 { - margin: -0.25rem !important; - } - .mt-lg-n1, - .my-lg-n1 { - margin-top: -0.25rem !important; - } - .mr-lg-n1, - .mx-lg-n1 { - margin-right: -0.25rem !important; - } - .mb-lg-n1, - .my-lg-n1 { - margin-bottom: -0.25rem !important; - } - .ml-lg-n1, - .mx-lg-n1 { - margin-left: -0.25rem !important; - } - .m-lg-n2 { - margin: -0.5rem !important; - } - .mt-lg-n2, - .my-lg-n2 { - margin-top: -0.5rem !important; - } - .mr-lg-n2, - .mx-lg-n2 { - margin-right: -0.5rem !important; - } - .mb-lg-n2, - .my-lg-n2 { - margin-bottom: -0.5rem !important; - } - .ml-lg-n2, - .mx-lg-n2 { - margin-left: -0.5rem !important; - } - .m-lg-n3 { - margin: -1rem !important; - } - .mt-lg-n3, - .my-lg-n3 { - margin-top: -1rem !important; - } - .mr-lg-n3, - .mx-lg-n3 { - margin-right: -1rem !important; - } - .mb-lg-n3, - .my-lg-n3 { - margin-bottom: -1rem !important; - } - .ml-lg-n3, - .mx-lg-n3 { - margin-left: -1rem !important; - } - .m-lg-n4 { - margin: -1.5rem !important; - } - .mt-lg-n4, - .my-lg-n4 { - margin-top: -1.5rem !important; - } - .mr-lg-n4, - .mx-lg-n4 { - margin-right: -1.5rem !important; - } - .mb-lg-n4, - .my-lg-n4 { - margin-bottom: -1.5rem !important; - } - .ml-lg-n4, - .mx-lg-n4 { - margin-left: -1.5rem !important; - } - .m-lg-n5 { - margin: -3rem !important; - } - .mt-lg-n5, - .my-lg-n5 { - margin-top: -3rem !important; - } - .mr-lg-n5, - .mx-lg-n5 { - margin-right: -3rem !important; - } - .mb-lg-n5, - .my-lg-n5 { - margin-bottom: -3rem !important; - } - .ml-lg-n5, - .mx-lg-n5 { - margin-left: -3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mt-lg-auto, - .my-lg-auto { - margin-top: auto !important; - } - .mr-lg-auto, - .mx-lg-auto { - margin-right: auto !important; - } - .mb-lg-auto, - .my-lg-auto { - margin-bottom: auto !important; - } - .ml-lg-auto, - .mx-lg-auto { - margin-left: auto !important; - } -} - -@media (min-width: 1200px) { - .m-xl-0 { - margin: 0 !important; - } - .mt-xl-0, - .my-xl-0 { - margin-top: 0 !important; - } - .mr-xl-0, - .mx-xl-0 { - margin-right: 0 !important; - } - .mb-xl-0, - .my-xl-0 { - margin-bottom: 0 !important; - } - .ml-xl-0, - .mx-xl-0 { - margin-left: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .mt-xl-1, - .my-xl-1 { - margin-top: 0.25rem !important; - } - .mr-xl-1, - .mx-xl-1 { - margin-right: 0.25rem !important; - } - .mb-xl-1, - .my-xl-1 { - margin-bottom: 0.25rem !important; - } - .ml-xl-1, - .mx-xl-1 { - margin-left: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .mt-xl-2, - .my-xl-2 { - margin-top: 0.5rem !important; - } - .mr-xl-2, - .mx-xl-2 { - margin-right: 0.5rem !important; - } - .mb-xl-2, - .my-xl-2 { - margin-bottom: 0.5rem !important; - } - .ml-xl-2, - .mx-xl-2 { - margin-left: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .mt-xl-3, - .my-xl-3 { - margin-top: 1rem !important; - } - .mr-xl-3, - .mx-xl-3 { - margin-right: 1rem !important; - } - .mb-xl-3, - .my-xl-3 { - margin-bottom: 1rem !important; - } - .ml-xl-3, - .mx-xl-3 { - margin-left: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .mt-xl-4, - .my-xl-4 { - margin-top: 1.5rem !important; - } - .mr-xl-4, - .mx-xl-4 { - margin-right: 1.5rem !important; - } - .mb-xl-4, - .my-xl-4 { - margin-bottom: 1.5rem !important; - } - .ml-xl-4, - .mx-xl-4 { - margin-left: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .mt-xl-5, - .my-xl-5 { - margin-top: 3rem !important; - } - .mr-xl-5, - .mx-xl-5 { - margin-right: 3rem !important; - } - .mb-xl-5, - .my-xl-5 { - margin-bottom: 3rem !important; - } - .ml-xl-5, - .mx-xl-5 { - margin-left: 3rem !important; - } - .p-xl-0 { - padding: 0 !important; - } - .pt-xl-0, - .py-xl-0 { - padding-top: 0 !important; - } - .pr-xl-0, - .px-xl-0 { - padding-right: 0 !important; - } - .pb-xl-0, - .py-xl-0 { - padding-bottom: 0 !important; - } - .pl-xl-0, - .px-xl-0 { - padding-left: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .pt-xl-1, - .py-xl-1 { - padding-top: 0.25rem !important; - } - .pr-xl-1, - .px-xl-1 { - padding-right: 0.25rem !important; - } - .pb-xl-1, - .py-xl-1 { - padding-bottom: 0.25rem !important; - } - .pl-xl-1, - .px-xl-1 { - padding-left: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .pt-xl-2, - .py-xl-2 { - padding-top: 0.5rem !important; - } - .pr-xl-2, - .px-xl-2 { - padding-right: 0.5rem !important; - } - .pb-xl-2, - .py-xl-2 { - padding-bottom: 0.5rem !important; - } - .pl-xl-2, - .px-xl-2 { - padding-left: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .pt-xl-3, - .py-xl-3 { - padding-top: 1rem !important; - } - .pr-xl-3, - .px-xl-3 { - padding-right: 1rem !important; - } - .pb-xl-3, - .py-xl-3 { - padding-bottom: 1rem !important; - } - .pl-xl-3, - .px-xl-3 { - padding-left: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .pt-xl-4, - .py-xl-4 { - padding-top: 1.5rem !important; - } - .pr-xl-4, - .px-xl-4 { - padding-right: 1.5rem !important; - } - .pb-xl-4, - .py-xl-4 { - padding-bottom: 1.5rem !important; - } - .pl-xl-4, - .px-xl-4 { - padding-left: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .pt-xl-5, - .py-xl-5 { - padding-top: 3rem !important; - } - .pr-xl-5, - .px-xl-5 { - padding-right: 3rem !important; - } - .pb-xl-5, - .py-xl-5 { - padding-bottom: 3rem !important; - } - .pl-xl-5, - .px-xl-5 { - padding-left: 3rem !important; - } - .m-xl-n1 { - margin: -0.25rem !important; - } - .mt-xl-n1, - .my-xl-n1 { - margin-top: -0.25rem !important; - } - .mr-xl-n1, - .mx-xl-n1 { - margin-right: -0.25rem !important; - } - .mb-xl-n1, - .my-xl-n1 { - margin-bottom: -0.25rem !important; - } - .ml-xl-n1, - .mx-xl-n1 { - margin-left: -0.25rem !important; - } - .m-xl-n2 { - margin: -0.5rem !important; - } - .mt-xl-n2, - .my-xl-n2 { - margin-top: -0.5rem !important; - } - .mr-xl-n2, - .mx-xl-n2 { - margin-right: -0.5rem !important; - } - .mb-xl-n2, - .my-xl-n2 { - margin-bottom: -0.5rem !important; - } - .ml-xl-n2, - .mx-xl-n2 { - margin-left: -0.5rem !important; - } - .m-xl-n3 { - margin: -1rem !important; - } - .mt-xl-n3, - .my-xl-n3 { - margin-top: -1rem !important; - } - .mr-xl-n3, - .mx-xl-n3 { - margin-right: -1rem !important; - } - .mb-xl-n3, - .my-xl-n3 { - margin-bottom: -1rem !important; - } - .ml-xl-n3, - .mx-xl-n3 { - margin-left: -1rem !important; - } - .m-xl-n4 { - margin: -1.5rem !important; - } - .mt-xl-n4, - .my-xl-n4 { - margin-top: -1.5rem !important; - } - .mr-xl-n4, - .mx-xl-n4 { - margin-right: -1.5rem !important; - } - .mb-xl-n4, - .my-xl-n4 { - margin-bottom: -1.5rem !important; - } - .ml-xl-n4, - .mx-xl-n4 { - margin-left: -1.5rem !important; - } - .m-xl-n5 { - margin: -3rem !important; - } - .mt-xl-n5, - .my-xl-n5 { - margin-top: -3rem !important; - } - .mr-xl-n5, - .mx-xl-n5 { - margin-right: -3rem !important; - } - .mb-xl-n5, - .my-xl-n5 { - margin-bottom: -3rem !important; - } - .ml-xl-n5, - .mx-xl-n5 { - margin-left: -3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mt-xl-auto, - .my-xl-auto { - margin-top: auto !important; - } - .mr-xl-auto, - .mx-xl-auto { - margin-right: auto !important; - } - .mb-xl-auto, - .my-xl-auto { - margin-bottom: auto !important; - } - .ml-xl-auto, - .mx-xl-auto { - margin-left: auto !important; - } -} - -.text-monospace { - font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; -} - -.text-justify { - text-align: justify !important; -} - -.text-wrap { - white-space: normal !important; -} - -.text-nowrap { - white-space: nowrap !important; -} - -.text-truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.text-left { - text-align: left !important; -} - -.text-right { - text-align: right !important; -} - -.text-center { - text-align: center !important; -} - -@media (min-width: 576px) { - .text-sm-left { - text-align: left !important; - } - .text-sm-right { - text-align: right !important; - } - .text-sm-center { - text-align: center !important; - } -} - -@media (min-width: 768px) { - .text-md-left { - text-align: left !important; - } - .text-md-right { - text-align: right !important; - } - .text-md-center { - text-align: center !important; - } -} - -@media (min-width: 992px) { - .text-lg-left { - text-align: left !important; - } - .text-lg-right { - text-align: right !important; - } - .text-lg-center { - text-align: center !important; - } -} - -@media (min-width: 1200px) { - .text-xl-left { - text-align: left !important; - } - .text-xl-right { - text-align: right !important; - } - .text-xl-center { - text-align: center !important; - } -} - -.text-lowercase { - text-transform: lowercase !important; -} - -.text-uppercase { - text-transform: uppercase !important; -} - -.text-capitalize { - text-transform: capitalize !important; -} - -.font-weight-light { - font-weight: 300 !important; -} - -.font-weight-lighter { - font-weight: lighter !important; -} - -.font-weight-normal { - font-weight: 400 !important; -} - -.font-weight-bold { - font-weight: 700 !important; -} - -.font-weight-bolder { - font-weight: bolder !important; -} - -.font-italic { - font-style: italic !important; -} - -.text-white { - color: #fff !important; -} - -.text-primary { - color: #2A9FD6 !important; -} - -a.text-primary:hover, a.text-primary:focus { - color: #1d7097 !important; -} - -.text-secondary { - color: #555 !important; -} - -a.text-secondary:hover, a.text-secondary:focus { - color: #2f2f2f !important; -} - -.text-success { - color: #77B300 !important; -} - -a.text-success:hover, a.text-success:focus { - color: #446700 !important; -} - -.text-info { - color: #9933CC !important; -} - -a.text-info:hover, a.text-info:focus { - color: #6b248f !important; -} - -.text-warning { - color: #FF8800 !important; -} - -a.text-warning:hover, a.text-warning:focus { - color: #b35f00 !important; -} - -.text-danger { - color: #CC0000 !important; -} - -a.text-danger:hover, a.text-danger:focus { - color: maroon !important; -} - -.text-light { - color: #222 !important; -} - -a.text-light:hover, a.text-light:focus { - color: black !important; -} - -.text-dark { - color: #ADAFAE !important; -} - -a.text-dark:hover, a.text-dark:focus { - color: #868988 !important; -} - -.text-body { - color: #888 !important; -} - -.text-muted { - color: #555 !important; -} - -.text-black-50 { - color: rgba(0, 0, 0, 0.5) !important; -} - -.text-white-50 { - color: rgba(255, 255, 255, 0.5) !important; -} - -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} - -.text-decoration-none { - text-decoration: none !important; -} - -.text-break { - word-break: break-word !important; - overflow-wrap: break-word !important; -} - -.text-reset { - color: inherit !important; -} - -.visible { - visibility: visible !important; -} - -.invisible { - visibility: hidden !important; -} - -@media print { - *, - *::before, - *::after { - text-shadow: none !important; - -webkit-box-shadow: none !important; - box-shadow: none !important; - } - a:not(.btn) { - text-decoration: underline; - } - abbr[title]::after { - content: " (" attr(title) ")"; - } - pre { - white-space: pre-wrap !important; - } - pre, - blockquote { - border: 1px solid #888; - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } - @page { - size: a3; - } - body { - min-width: 992px !important; - } - .container { - min-width: 992px !important; - } - .navbar { - display: none; - } - .badge { - border: 1px solid #000; - } - .table { - border-collapse: collapse !important; - } - .table td, - .table th { - background-color: #fff !important; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #dee2e6 !important; - } - .table-dark { - color: inherit; - } - .table-dark th, - .table-dark td, - .table-dark thead th, - .table-dark tbody + tbody { - border-color: #282828; - } - .table .thead-dark th { - color: inherit; - border-color: #282828; - } -} - -.navbar.bg-primary { - border: 1px solid #282828; -} - -.navbar.bg-dark { - background-color: #060606 !important; - border: 1px solid #282828; -} - -.navbar.bg-light { - background-color: #888 !important; -} - -.navbar.fixed-top { - border-width: 0 0 1px 0; -} - -.navbar.fixed-bottom { - border-width: 1px 0 0 0; -} - -.btn-primary { - background-color: #2A9FD6; -} - -.btn-secondary { - background-color: #555; -} - -.btn-success { - background-color: #77B300; -} - -.btn-info { - background-color: #9933CC; -} - -.btn-warning { - background-color: #FF8800; -} - -.btn-danger { - background-color: #CC0000; -} - -.btn-light { - background-color: #222; -} - -.btn-dark { - background-color: #ADAFAE; -} - -table { - color: #fff; -} - -.table-primary, -.table-primary > th, -.table-primary > td { - background-color: #2A9FD6; -} - -.table-secondary, -.table-secondary > th, -.table-secondary > td { - background-color: #555; -} - -.table-light, -.table-light > th, -.table-light > td { - background-color: #222; -} - -.table-dark, -.table-dark > th, -.table-dark > td { - background-color: #ADAFAE; -} - -.table-success, -.table-success > th, -.table-success > td { - background-color: #77B300; -} - -.table-info, -.table-info > th, -.table-info > td { - background-color: #9933CC; -} - -.table-danger, -.table-danger > th, -.table-danger > td { - background-color: #CC0000; -} - -.table-warning, -.table-warning > th, -.table-warning > td { - background-color: #FF8800; -} - -.table-active, -.table-active > th, -.table-active > td { - background-color: rgba(255, 255, 255, 0.075); -} - -.table-hover .table-primary:hover, .table-hover .table-primary:hover > th, .table-hover .table-primary:hover > td { - background-color: #258fc1; -} - -.table-hover .table-secondary:hover, .table-hover .table-secondary:hover > th, .table-hover .table-secondary:hover > td { - background-color: #484848; -} - -.table-hover .table-light:hover, .table-hover .table-light:hover > th, .table-hover .table-light:hover > td { - background-color: #151515; -} - -.table-hover .table-dark:hover, .table-hover .table-dark:hover > th, .table-hover .table-dark:hover > td { - background-color: #a0a2a1; -} - -.table-hover .table-success:hover, .table-hover .table-success:hover > th, .table-hover .table-success:hover > td { - background-color: #669a00; -} - -.table-hover .table-info:hover, .table-hover .table-info:hover > th, .table-hover .table-info:hover > td { - background-color: #8a2eb8; -} - -.table-hover .table-danger:hover, .table-hover .table-danger:hover > th, .table-hover .table-danger:hover > td { - background-color: #b30000; -} - -.table-hover .table-warning:hover, .table-hover .table-warning:hover > th, .table-hover .table-warning:hover > td { - background-color: #e67a00; -} - -.table-hover .table-active:hover, .table-hover .table-active:hover > th, .table-hover .table-active:hover > td { - background-color: rgba(255, 255, 255, 0.075); -} - -legend { - color: #fff; -} - -.nav-tabs .nav-link, -.nav-pills .nav-link { - color: #fff; -} - -.nav-tabs .nav-link:hover, -.nav-pills .nav-link:hover { - background-color: #282828; -} - -.nav-tabs .nav-link.disabled, .nav-tabs .nav-link.disabled:hover, -.nav-pills .nav-link.disabled, -.nav-pills .nav-link.disabled:hover { - background-color: transparent; - color: #555; -} - -.nav-tabs .nav-link.active, -.nav-pills .nav-link.active { - background-color: #2A9FD6; -} - -.breadcrumb a { - color: #fff; -} - -.pagination a:hover { - text-decoration: none; -} - -.alert { - border: none; - color: #fff; -} - -.alert a, -.alert .alert-link { - color: #fff; - text-decoration: underline; -} - -.alert-primary { - background-color: #2A9FD6; -} - -.alert-secondary { - background-color: #555; -} - -.alert-success { - background-color: #77B300; -} - -.alert-info { - background-color: #9933CC; -} - -.alert-warning { - background-color: #FF8800; -} - -.alert-danger { - background-color: #CC0000; -} - -.alert-light { - background-color: #222; -} - -.alert-dark { - background-color: #ADAFAE; -} - -.alert-light, -.alert-light a, -.alert-light .alert-link { - color: #060606; -} - -.badge-warning { - color: #fff; -} - -.close { - opacity: 0.6; -} - -.close:hover { - opacity: 1; -} - -.list-group-item:hover { - background-color: #282828; - color: #fff; -} - -.list-group-item-action { - color: #888; -} - -.list-group-item-action .list-group-item-heading { - color: #888; -} - -.list-group-item:hover .list-group-item-heading { - color: #fff; -} - -.card h1, .card h2, .card h3, .card h4, .card h5, .card h6, -.list-group-item h1, -.list-group-item h2, -.list-group-item h3, -.list-group-item h4, -.list-group-item h5, -.list-group-item h6 { - color: inherit; -} - -.popover-title { - border-bottom: none; -} - -/* Oqtane Styles */ +/* Oqtane Styles */ body { padding-top: 7rem; diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index aa0529a0..50c8aa9b 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -70,7 +70,7 @@ window.interop = { link.integrity = integrity; } if (crossorigin !== "") { - link.crossorigin = crossorigin; + link.crossOrigin = crossorigin; } document.head.appendChild(link); } @@ -87,7 +87,7 @@ window.interop = { if (integrity !== "" && link.integrity !== integrity) { link.setAttribute('integrity', integrity); } - if (crossorigin !== "" && link.crossorigin !== crossorigin) { + if (crossorigin !== "" && link.crossOrigin !== crossorigin) { link.setAttribute('crossorigin', crossorigin); } } From d7ad175cd7946f3d65b6c038370a0af573f55f2c Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 7 May 2020 14:38:24 -0400 Subject: [PATCH 198/265] fixes for framework upgrade, fixes for control panel CSS styles, added AllPages attrubute for Modules, bumped version to 0.9.1 to test upgrades --- .../Modules/Admin/Modules/Settings.razor | 23 +++++++++++++++---- .../Modules/Admin/Upgrade/Index.razor | 10 ++++---- Oqtane.Client/Oqtane.Client.csproj | 2 +- .../Themes/Controls/ControlPanel.razor | 9 ++++---- .../Themes/OqtaneTheme/Default.razor | 2 +- Oqtane.Package/Oqtane.Framework.nuspec | 2 +- Oqtane.Server/Controllers/ModuleController.cs | 20 ++++++++++++++-- Oqtane.Server/Controllers/PageController.cs | 11 +++++++++ .../Infrastructure/InstallationManager.cs | 6 ++--- Oqtane.Server/Oqtane.Server.csproj | 7 +++++- Oqtane.Server/Repository/SiteRepository.cs | 1 + Oqtane.Server/Scripts/Tenant.0.9.1.sql | 14 +++++++++++ Oqtane.Server/wwwroot/css/app.css | 10 -------- Oqtane.Shared/Models/Module.cs | 1 + Oqtane.Shared/Oqtane.Shared.csproj | 2 +- Oqtane.Shared/Shared/Constants.cs | 4 ++-- Oqtane.Upgrade/Oqtane.Upgrade.csproj | 2 +- Oqtane.Upgrade/Program.cs | 16 ++++++------- 18 files changed, 95 insertions(+), 47 deletions(-) create mode 100644 Oqtane.Server/Scripts/Tenant.0.9.1.sql diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index c8a90d23..eec6c6e2 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -32,6 +32,17 @@
    + + + +
    @@ -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,7 @@ _title = ModuleState.Title; _containers = ThemeService.GetContainerTypes(await ThemeService.GetThemesAsync()); _containerType = ModuleState.ContainerType; + _allPages = ModuleState.AllPages.ToString(); _permissions = ModuleState.Permissions; _permissionNames = ModuleState.ModuleDefinition.PermissionNames; _pageId = ModuleState.PageId.ToString(); @@ -120,18 +133,18 @@ 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; - 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/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index 2aa2ce86..f2e2e709 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/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index f19dac81..6bb2f064 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 + 0.9.1 Oqtane Shaun Walker .NET Foundation diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index fe9e5486..f3b3354f 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -16,7 +16,7 @@
    - Control Panel + Control Panel @@ -245,17 +245,17 @@ { if (string.IsNullOrEmpty(ButtonClass)) { - ButtonClass = "btn-outline-primary"; + ButtonClass = "btn-outline-secondary"; } if (string.IsNullOrEmpty(CardClass)) { - CardClass = "card bg-secondary mb-3"; + CardClass = "card border-secondary mb-3"; } if (string.IsNullOrEmpty(HeaderClass)) { - HeaderClass = "card-header text-white"; + HeaderClass = "card-header"; } if (string.IsNullOrEmpty(BodyClass)) @@ -361,6 +361,7 @@ module.SiteId = PageState.Site.SiteId; module.PageId = PageState.Page.PageId; module.ModuleDefinitionName = _moduleDefinitionName; + module.AllPages = false; module.Permissions = PageState.Page.Permissions; module = await ModuleService.AddModuleAsync(module); _moduleId = module.ModuleId.ToString(); diff --git a/Oqtane.Client/Themes/OqtaneTheme/Default.razor b/Oqtane.Client/Themes/OqtaneTheme/Default.razor index 89623e29..88542f0f 100644 --- a/Oqtane.Client/Themes/OqtaneTheme/Default.razor +++ b/Oqtane.Client/Themes/OqtaneTheme/Default.razor @@ -3,7 +3,7 @@
    diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec index b537a230..000bf21e 100644 --- a/Oqtane.Package/Oqtane.Framework.nuspec +++ b/Oqtane.Package/Oqtane.Framework.nuspec @@ -2,7 +2,7 @@ Oqtane.Framework - 0.9.0 + 0.9.1 Shaun Walker .NET Foundation Oqtane Framework diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index 1afc13d6..55ea9e1e 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -16,14 +16,16 @@ namespace Oqtane.Controllers { private readonly IModuleRepository _modules; private readonly IPageModuleRepository _pageModules; + private readonly IPageRepository _pages; private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly IUserPermissions _userPermissions; private readonly ILogManager _logger; - public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, IUserPermissions userPermissions, ILogManager logger) + public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IPageRepository pages, IModuleDefinitionRepository moduleDefinitions, IUserPermissions userPermissions, ILogManager logger) { _modules = modules; _pageModules = pageModules; + _pages = pages; _moduleDefinitions = moduleDefinitions; _userPermissions = userPermissions; _logger = logger; @@ -42,6 +44,7 @@ namespace Oqtane.Controllers Module module = new Module(); module.SiteId = pagemodule.Module.SiteId; module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName; + module.AllPages = pagemodule.Module.AllPages; module.Permissions = pagemodule.Module.Permissions; module.CreatedBy = pagemodule.Module.CreatedBy; module.CreatedOn = pagemodule.Module.CreatedOn; @@ -111,7 +114,20 @@ namespace Oqtane.Controllers if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit)) { module = _modules.UpdateModule(module); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module); + if (module.AllPages) + { + var pageModule = _pageModules.GetPageModules(module.SiteId).FirstOrDefault(item => item.ModuleId == module.ModuleId); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module); + + var pages = _pages.GetPages(module.SiteId).ToList(); + foreach (Page page in pages) + { + if (page.PageId != pageModule.PageId && !page.EditMode) + { + _pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType }); + } + } + } } else { diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 16afb06e..915fccdc 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -124,6 +124,16 @@ namespace Oqtane.Controllers page = _pages.AddPage(page); _syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page); + + if (!page.EditMode) + { + var modules = _modules.GetModules(page.SiteId).Where(item => item.AllPages).ToList(); + foreach (Module module in modules) + { + var pageModule = _pageModules.GetPageModules(page.SiteId).FirstOrDefault(item => item.ModuleId == module.ModuleId); + _pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType }); + } + } } else { @@ -174,6 +184,7 @@ namespace Oqtane.Controllers module.SiteId = page.SiteId; module.PageId = page.PageId; module.ModuleDefinitionName = pm.Module.ModuleDefinitionName; + module.AllPages = false; module.Permissions = new List { new Permission(PermissionNames.View, userid, true), new Permission(PermissionNames.Edit, userid, true) diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index 4cfbaa6b..b1f73543 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -180,17 +180,17 @@ namespace Oqtane.Infrastructure private void FinishUpgrade() { - string folder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); - // check if upgrade application exists + string folder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); if (folder == null || !File.Exists(Path.Combine(folder, "Oqtane.Upgrade.exe"))) return; + // run upgrade application var process = new Process { StartInfo = { FileName = Path.Combine(folder, "Oqtane.Upgrade.exe"), - Arguments = "", + Arguments = "\"" + _environment.ContentRootPath + "\" \"" + _environment.WebRootPath + "\"", ErrorDialog = false, UseShellExecute = false, CreateNoWindow = true, diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index e115b73a..b298fc75 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -4,7 +4,7 @@ netcoreapp3.1 7.3 Debug;Release - 0.9.0 + 0.9.1 Oqtane Shaun Walker .NET Foundation @@ -17,10 +17,15 @@ Oqtane + + + + + diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 3dea955c..d7c8a1e5 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -775,6 +775,7 @@ namespace Oqtane.Repository { SiteId = site.SiteId, ModuleDefinitionName = pagetemplatemodule.ModuleDefinitionName, + AllPages = false, Permissions = pagetemplatemodule.ModulePermissions, }; module = _moduleRepository.AddModule(module); diff --git a/Oqtane.Server/Scripts/Tenant.0.9.1.sql b/Oqtane.Server/Scripts/Tenant.0.9.1.sql new file mode 100644 index 00000000..94728128 --- /dev/null +++ b/Oqtane.Server/Scripts/Tenant.0.9.1.sql @@ -0,0 +1,14 @@ +/* + +migration script + +*/ + +ALTER TABLE [dbo].[Module] ADD + [AllPages] [bit] NULL +GO + +UPDATE [dbo].[Module] +SET [AllPages] = 0 +GO + diff --git a/Oqtane.Server/wwwroot/css/app.css b/Oqtane.Server/wwwroot/css/app.css index f1c79deb..403f803e 100644 --- a/Oqtane.Server/wwwroot/css/app.css +++ b/Oqtane.Server/wwwroot/css/app.css @@ -28,16 +28,6 @@ app { width: 100%; /* 100% width */ } - /* Pad the navigation links */ - .app-controlpanel .nav-item { - font-size: 0.9rem; - padding-bottom: 0.5rem; - } - - .app-controlpanel .card-body .control-label { - color: white; - } - /* Admin Modal */ .app-admin-modal .modal { position: fixed; /* Stay in place */ diff --git a/Oqtane.Shared/Models/Module.cs b/Oqtane.Shared/Models/Module.cs index 927377a0..16b51679 100644 --- a/Oqtane.Shared/Models/Module.cs +++ b/Oqtane.Shared/Models/Module.cs @@ -9,6 +9,7 @@ namespace Oqtane.Models public int ModuleId { get; set; } public int SiteId { get; set; } public string ModuleDefinitionName { get; set; } + public bool AllPages { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index 131d6055..c50111cd 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -4,7 +4,7 @@ netstandard2.1 7.3 Debug;Release - 0.9.0 + 0.9.1 Oqtane Shaun Walker .NET Foundation diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 847e7a9e..c50347e2 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -3,8 +3,8 @@ public class Constants { public const string PackageId = "Oqtane.Framework"; - public const string Version = "0.9.0"; - public const string ReleaseVersions = "0.9.0"; + public const string Version = "0.9.1"; + public const string ReleaseVersions = "0.9.0,0.9.1"; public const string PageComponent = "Oqtane.UI.ThemeBuilder, Oqtane.Client"; public const string ContainerComponent = "Oqtane.UI.ContainerBuilder, Oqtane.Client"; diff --git a/Oqtane.Upgrade/Oqtane.Upgrade.csproj b/Oqtane.Upgrade/Oqtane.Upgrade.csproj index f4437db3..d9648c07 100644 --- a/Oqtane.Upgrade/Oqtane.Upgrade.csproj +++ b/Oqtane.Upgrade/Oqtane.Upgrade.csproj @@ -4,7 +4,7 @@ netcoreapp3.1 7.3 Exe - 0.9.0 + 0.9.1 Oqtane Shaun Walker .NET Foundation diff --git a/Oqtane.Upgrade/Program.cs b/Oqtane.Upgrade/Program.cs index f1f0f9b4..6c8fb1df 100644 --- a/Oqtane.Upgrade/Program.cs +++ b/Oqtane.Upgrade/Program.cs @@ -10,14 +10,12 @@ namespace Oqtane.Upgrade { static void Main(string[] args) { - string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - - // assumes that the application executable must be deployed to the /bin of the Oqtane.Server project - if (binfolder.Contains(Path.Combine("Oqtane.Server", "bin"))) + // requires 2 arguments - the contentrootpath and the webrootpath of the site + if (args.Length == 2) { - // ie. binfolder = Oqtane.Server\bin\Debug\netcoreapp3.0\ - string rootfolder = Directory.GetParent(binfolder).Parent.Parent.FullName; - string deployfolder = Path.Combine(rootfolder, Path.Combine("wwwroot","Framework")); + string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + string rootfolder = args[0]; + string deployfolder = Path.Combine(args[1], "Framework"); if (Directory.Exists(deployfolder)) { @@ -87,7 +85,7 @@ namespace Oqtane.Upgrade success = false; } - if (success) + if (success) { // clean up backup foreach (string file in files) @@ -99,7 +97,7 @@ namespace Oqtane.Upgrade } } } - else + else { // restore on failure foreach (string file in files) From 47f17a589faec1a30b03dc88c027c34a5f5552ff Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Thu, 7 May 2020 18:51:55 +0200 Subject: [PATCH 199/265] implementation of [OqtaneIgnore] class attribute in controls --- .../Modules/Controls/ActionDialog.razor | 2 +- .../Modules/Controls/ActionLink.razor | 3 +- .../Modules/Controls/AuditInfo.razor | 3 +- .../Modules/Controls/FileManager.razor | 3 +- Oqtane.Client/Modules/Controls/Label.razor | 5 +- .../Modules/Controls/ModuleMessage.razor | 3 +- Oqtane.Client/Modules/Controls/Pager.razor | 6 +- .../Modules/Controls/PermissionGrid.razor | 3 +- .../Modules/Controls/RichTextEditor.razor | 3 +- Oqtane.Client/Modules/Controls/Section.razor | 3 +- Oqtane.Client/Modules/Controls/TabPanel.razor | 3 +- Oqtane.Client/Modules/Controls/TabStrip.razor | 3 +- Oqtane.Client/Services/ServiceBase.cs | 3 +- .../Themes/Controls/Breadcrumbs.razor | 3 +- .../Themes/Controls/ControlPanel.razor | 3 +- Oqtane.Client/Themes/Controls/Login.razor | 3 +- Oqtane.Client/Themes/Controls/Logo.razor | 3 +- .../Themes/Controls/MenuHorizontal.Razor | 1 + .../Themes/Controls/MenuVertical.razor | 3 +- .../Themes/Controls/ModuleActions.razor | 3 +- .../Themes/Controls/ModuleTitle.razor | 3 +- .../Themes/Controls/UserProfile.razor | 3 +- .../Extensions/AssemblyExtensions.cs | 17 ++ .../Extensions/OqtaneMvcBuilderExtensions.cs | 6 +- .../OqtaneServiceCollectionExtensions.cs | 114 +++++-------- Oqtane.Server/Extensions/StringExtensions.cs | 17 ++ .../Repository/ModuleDefinitionRepository.cs | 158 ++++++++++-------- .../Repository/SiteTemplateRepository.cs | 4 +- Oqtane.Server/Repository/ThemeRepository.cs | 3 +- Oqtane.Server/Startup.cs | 9 +- Oqtane.Shared/Shared/OqtaneIgnoreAttribute.cs | 9 + 31 files changed, 227 insertions(+), 178 deletions(-) create mode 100644 Oqtane.Server/Extensions/StringExtensions.cs create mode 100644 Oqtane.Shared/Shared/OqtaneIgnoreAttribute.cs diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor index 08b2d77d..0ee455d7 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) {
    diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index cf588579..97fd0a0e 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) 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..8dc70ffb 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@inherits ModuleBase +@attribute [OqtaneIgnore] @inject IFolderService FolderService @inject IFileService FileService @inject IJSRuntime JsRuntime 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..175717fd 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@inherits ModuleBase +@attribute [OqtaneIgnore] @inject IJSRuntime JsRuntime @if (_filemanagervisible) 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/Services/ServiceBase.cs b/Oqtane.Client/Services/ServiceBase.cs index 742bd3fa..57bad2b7 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}"); } diff --git a/Oqtane.Client/Themes/Controls/Breadcrumbs.razor b/Oqtane.Client/Themes/Controls/Breadcrumbs.razor index 7175f85d..06d9eceb 100644 --- a/Oqtane.Client/Themes/Controls/Breadcrumbs.razor +++ b/Oqtane.Client/Themes/Controls/Breadcrumbs.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Themes.Controls -@inherits ThemeControlBase +@inherits ThemeControlBase +@attribute [OqtaneIgnore] @if (BreadCrumbPages.Any()) { diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index fe9e5486..042e2ef4 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -1,6 +1,7 @@ @namespace Oqtane.Themes.Controls @using Oqtane.Enums -@inherits ThemeControlBase +@inherits ThemeControlBase +@attribute [OqtaneIgnore] @inject NavigationManager NavigationManager @inject IUserService UserService @inject IModuleDefinitionService ModuleDefinitionService diff --git a/Oqtane.Client/Themes/Controls/Login.razor b/Oqtane.Client/Themes/Controls/Login.razor index 55d975aa..dbdb9444 100644 --- a/Oqtane.Client/Themes/Controls/Login.razor +++ b/Oqtane.Client/Themes/Controls/Login.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Themes.Controls -@inherits LoginBase +@inherits LoginBase +@attribute [OqtaneIgnore] diff --git a/Oqtane.Client/Themes/Controls/Logo.razor b/Oqtane.Client/Themes/Controls/Logo.razor index fe9929f4..c98d58b7 100644 --- a/Oqtane.Client/Themes/Controls/Logo.razor +++ b/Oqtane.Client/Themes/Controls/Logo.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Themes.Controls -@inherits ThemeControlBase +@inherits ThemeControlBase +@attribute [OqtaneIgnore] @inject NavigationManager NavigationManager @if (PageState.Site.LogoFileId != null) diff --git a/Oqtane.Client/Themes/Controls/MenuHorizontal.Razor b/Oqtane.Client/Themes/Controls/MenuHorizontal.Razor index 708a3059..075a955a 100644 --- a/Oqtane.Client/Themes/Controls/MenuHorizontal.Razor +++ b/Oqtane.Client/Themes/Controls/MenuHorizontal.Razor @@ -1,5 +1,6 @@ @namespace Oqtane.Themes.Controls @inherits MenuBase +@attribute [OqtaneIgnore] @if (MenuPages.Any()) {
    diff --git a/Oqtane.Client/Themes/Controls/MenuVertical.razor b/Oqtane.Client/Themes/Controls/MenuVertical.razor index 8991b277..3d6500bf 100644 --- a/Oqtane.Client/Themes/Controls/MenuVertical.razor +++ b/Oqtane.Client/Themes/Controls/MenuVertical.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Themes.Controls -@inherits MenuBase +@inherits MenuBase +@attribute [OqtaneIgnore] @if (MenuPages.Any()) {
    diff --git a/Oqtane.Client/Themes/Controls/ModuleActions.razor b/Oqtane.Client/Themes/Controls/ModuleActions.razor index 6ca55f1a..0a9cc6ea 100644 --- a/Oqtane.Client/Themes/Controls/ModuleActions.razor +++ b/Oqtane.Client/Themes/Controls/ModuleActions.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Themes.Controls -@inherits ModuleActionsBase +@inherits ModuleActionsBase +@attribute [OqtaneIgnore] @if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions)) { diff --git a/Oqtane.Client/Themes/Controls/ModuleTitle.razor b/Oqtane.Client/Themes/Controls/ModuleTitle.razor index bb8d6f62..b8da32bf 100644 --- a/Oqtane.Client/Themes/Controls/ModuleTitle.razor +++ b/Oqtane.Client/Themes/Controls/ModuleTitle.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Themes.Controls -@inherits ContainerBase +@inherits ContainerBase +@attribute [OqtaneIgnore] @((MarkupString)title) diff --git a/Oqtane.Client/Themes/Controls/UserProfile.razor b/Oqtane.Client/Themes/Controls/UserProfile.razor index 1f101caf..4f78741b 100644 --- a/Oqtane.Client/Themes/Controls/UserProfile.razor +++ b/Oqtane.Client/Themes/Controls/UserProfile.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Themes.Controls -@inherits ThemeControlBase +@inherits ThemeControlBase +@attribute [OqtaneIgnore] @inject NavigationManager NavigationManager diff --git a/Oqtane.Server/Extensions/AssemblyExtensions.cs b/Oqtane.Server/Extensions/AssemblyExtensions.cs index 8cb7c39d..9785d229 100644 --- a/Oqtane.Server/Extensions/AssemblyExtensions.cs +++ b/Oqtane.Server/Extensions/AssemblyExtensions.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +using System.IO; using System.Linq; +using Oqtane.Shared; // ReSharper disable once CheckNamespace namespace System.Reflection @@ -31,5 +33,20 @@ namespace System.Reflection return assembly.GetTypes() .Where(t => t.GetInterfaces().Contains(interfaceType)); } + + public static bool IsOqtaneAssembly(this Assembly assembly) + { + return assembly.FullName != null && (assembly.FullName.Contains("oqtane.", StringComparison.OrdinalIgnoreCase)); + } + + public static bool IsOqtaneAssembly(this FileInfo fileInfo) + { + return (fileInfo.Name.Contains("oqtane.", StringComparison.OrdinalIgnoreCase)); + } + + public static IEnumerable GetOqtaneAssemblies(this AppDomain appDomain) + { + return appDomain.GetAssemblies().Where(a => a.IsOqtaneAssembly()); + } } } diff --git a/Oqtane.Server/Extensions/OqtaneMvcBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneMvcBuilderExtensions.cs index 9464e767..fdb801df 100644 --- a/Oqtane.Server/Extensions/OqtaneMvcBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneMvcBuilderExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Reflection; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationParts; @@ -16,10 +17,11 @@ namespace Microsoft.Extensions.DependencyInjection } // load MVC application parts from module assemblies - foreach (var assembly in OqtaneServiceCollectionExtensions.GetOqtaneModuleAssemblies()) + var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); + foreach (var assembly in assemblies) { // check if assembly contains MVC Controllers - if (assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Controller))).ToArray().Length > 0) + if (assembly.GetTypes().Any(t => t.IsSubclassOf(typeof(Controller)))) { var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly); foreach (var part in partFactory.GetApplicationParts(assembly)) diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index 685327a9..5c093f48 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -5,67 +5,35 @@ using System.Linq; using System.Reflection; using System.Runtime.Loader; using Microsoft.Extensions.Hosting; +using Oqtane.Extensions; using Oqtane.Infrastructure; using Oqtane.Modules; +using Oqtane.Shared; // ReSharper disable once CheckNamespace namespace Microsoft.Extensions.DependencyInjection { public static class OqtaneServiceCollectionExtensions { - private static readonly IList OqtaneModuleAssemblies = new List(); - - private static Assembly[] Assemblies => AppDomain.CurrentDomain.GetAssemblies(); - - internal static IEnumerable GetOqtaneModuleAssemblies() => OqtaneModuleAssemblies; - - public static IServiceCollection AddOqtaneModules(this IServiceCollection services) + public static IServiceCollection AddOqtaneParts(this IServiceCollection services) { - if (services is null) - { - throw new ArgumentNullException(nameof(services)); - } - - LoadAssemblies("Module"); - + LoadAssemblies(); + services.AddOqtaneServices(); return services; } - public static IServiceCollection AddOqtaneThemes(this IServiceCollection services) + private static IServiceCollection AddOqtaneServices(this IServiceCollection services) { if (services is null) { throw new ArgumentNullException(nameof(services)); } - LoadAssemblies("Theme"); - - return services; - } - - public static IServiceCollection AddOqtaneSiteTemplates(this IServiceCollection services) - { - if (services is null) - { - throw new ArgumentNullException(nameof(services)); - } - - LoadAssemblies("SiteTemplate"); - - return services; - } - - public static IServiceCollection AddOqtaneServices(this IServiceCollection services) - { - if (services is null) - { - throw new ArgumentNullException(nameof(services)); - } - - // dynamically register module services, contexts, and repository classes - var assemblies = Assemblies.Where(item => item.FullName != null && (item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module."))).ToArray(); + var hostedServiceType = typeof(IHostedService); + var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); foreach (var assembly in assemblies) { + // dynamically register module services, contexts, and repository classes var implementationTypes = assembly.GetInterfaces(); foreach (var implementationType in implementationTypes) { @@ -75,22 +43,8 @@ namespace Microsoft.Extensions.DependencyInjection services.AddScoped(serviceType ?? implementationType, implementationType); } } - } - return services; - } - - public static IServiceCollection AddOqtaneHostedServices(this IServiceCollection services) - { - if (services is null) - { - throw new ArgumentNullException(nameof(services)); - } - - // dynamically register hosted services - var hostedServiceType = typeof(IHostedService); - foreach (var assembly in Assemblies) - { + // dynamically register hosted services var serviceTypes = assembly.GetTypes(hostedServiceType); foreach (var serviceType in serviceTypes) { @@ -104,34 +58,50 @@ namespace Microsoft.Extensions.DependencyInjection return services; } - private static void LoadAssemblies(string pattern) + private static void LoadAssemblies() { var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); if (assemblyPath == null) return; var assembliesFolder = new DirectoryInfo(assemblyPath); + // iterate through Oqtane assemblies in /bin ( filter is narrow to optimize loading process ) - foreach (var dll in assembliesFolder.EnumerateFiles($"*.{pattern}.*.dll")) + foreach (var dll in assembliesFolder.EnumerateFiles($"*.dll", SearchOption.TopDirectoryOnly).Where(f => f.IsOqtaneAssembly())) { - // check if assembly is already loaded - var assembly = Assemblies.FirstOrDefault(a =>!a.IsDynamic && a.Location == dll.FullName); - if (assembly == null) + AssemblyName assemblyName; + try { - // load assembly ( and symbols ) from stream to prevent locking files ( as long as dependencies are in /bin they will load as well ) - string pdb = dll.FullName.Replace(".dll", ".pdb"); - if (File.Exists(pdb)) + assemblyName = AssemblyName.GetAssemblyName(dll.FullName); + } + catch + { + Console.WriteLine($"Not Assembly : {dll.Name}"); + continue; + } + + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + if (!assemblies.Any(a => AssemblyName.ReferenceMatchesDefinition(assemblyName, a.GetName()))) + { + try { - assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(dll.FullName)), new MemoryStream(File.ReadAllBytes(pdb))); + var pdb = Path.ChangeExtension(dll.FullName, ".pdb"); + Assembly assembly = null; + + // load assembly ( and symbols ) from stream to prevent locking files ( as long as dependencies are in /bin they will load as well ) + if (File.Exists(pdb)) + { + assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(dll.FullName)), new MemoryStream(File.ReadAllBytes(pdb))); + } + else + { + assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(dll.FullName))); + } + Console.WriteLine($"Loaded : {assemblyName}"); } - else + catch (Exception e) { - assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(dll.FullName))); - } - if (pattern == "Module") - { - // build a list of module assemblies - OqtaneModuleAssemblies.Add(assembly); + Console.WriteLine($"Failed : {assemblyName}\n{e}"); } } } diff --git a/Oqtane.Server/Extensions/StringExtensions.cs b/Oqtane.Server/Extensions/StringExtensions.cs new file mode 100644 index 00000000..49b3b2cd --- /dev/null +++ b/Oqtane.Server/Extensions/StringExtensions.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Oqtane.Extensions +{ + public static class StringExtensions + { + public static bool StartWithAnyOf(this string s, IEnumerable list) + { + if (s == null) + { + return false; + } + return list.Any(f => s.StartsWith(f)); + } + } +} diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index 0e968076..9e717e37 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -75,6 +75,7 @@ namespace Oqtane.Repository // get module assemblies _moduleDefinitions = LoadModuleDefinitionsFromAssemblies(); } + List moduleDefinitions = _moduleDefinitions; List permissions = new List(); @@ -94,7 +95,7 @@ namespace Oqtane.Repository if (moduledef == null) { // new module definition - moduledef = new ModuleDefinition { ModuleDefinitionName = moduledefinition.ModuleDefinitionName }; + moduledef = new ModuleDefinition {ModuleDefinitionName = moduledefinition.ModuleDefinitionName}; _db.ModuleDefinition.Add(moduledef); _db.SaveChanges(); if (siteId != -1) @@ -109,18 +110,22 @@ namespace Oqtane.Repository { moduledefinition.Name = moduledef.Name; } + if (!string.IsNullOrEmpty(moduledef.Description)) { moduledefinition.Description = moduledef.Description; } + if (!string.IsNullOrEmpty(moduledef.Categories)) { moduledefinition.Categories = moduledef.Categories; } + if (!string.IsNullOrEmpty(moduledef.Version)) { moduledefinition.Version = moduledef.Version; } + if (siteId != -1) { if (permissions.Count == 0) @@ -139,9 +144,11 @@ namespace Oqtane.Repository } } } + // remove module definition from list as it is already synced moduledefs.Remove(moduledef); } + moduledefinition.ModuleDefinitionId = moduledef.ModuleDefinitionId; moduledefinition.SiteId = siteId; moduledefinition.CreatedBy = moduledef.CreatedBy; @@ -157,6 +164,7 @@ namespace Oqtane.Repository { _permissions.DeletePermissions(siteId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId); } + _db.ModuleDefinition.Remove(moduledefinition); // delete _db.SaveChanges(); } @@ -168,12 +176,12 @@ namespace Oqtane.Repository { List moduleDefinitions = new List(); // iterate through Oqtane module assemblies - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() - .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray(); + var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); foreach (Assembly assembly in assemblies) { moduleDefinitions = LoadModuleDefinitionsFromAssembly(moduleDefinitions, assembly); } + return moduleDefinitions; } @@ -183,84 +191,92 @@ namespace Oqtane.Repository Type[] modulecontroltypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModuleControl))).ToArray(); foreach (Type modulecontroltype in modulecontroltypes) { - if (modulecontroltype.Name != "ModuleBase" && !modulecontroltype.Namespace.EndsWith(".Controls")) - { - string[] typename = modulecontroltype.AssemblyQualifiedName?.Split(',').Select(item => item.Trim()).ToArray(); - string[] segments = typename[0].Split('.'); - Array.Resize(ref segments, segments.Length - 1); - string moduleType = string.Join(".", segments); - string qualifiedModuleType = moduleType + ", " + typename[1]; + // Check if type should be ignored + if (modulecontroltype.Name == "ModuleBase" + || modulecontroltype.IsGenericType + || Attribute.IsDefined(modulecontroltype, typeof(OqtaneIgnoreAttribute)) + ) continue; - int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType); - if (index == -1) + string[] typename = modulecontroltype.AssemblyQualifiedName?.Split(',').Select(item => item.Trim()).ToArray(); + string[] segments = typename[0].Split('.'); + Array.Resize(ref segments, segments.Length - 1); + string moduleType = string.Join(".", segments); + string qualifiedModuleType = moduleType + ", " + typename[1]; + + int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType); + if (index == -1) + { + // determine if this module implements IModule + Type moduletype = assembly + .GetTypes() + .Where(item => item.Namespace != null) + .Where(item => item.Namespace.StartsWith(moduleType)) + .FirstOrDefault(item => item.GetInterfaces().Contains(typeof(IModule))); + if (moduletype != null) { - // determine if this module implements IModule - Type moduletype = assembly - .GetTypes() - .Where(item => item.Namespace != null) - .Where(item => item.Namespace.StartsWith(moduleType)) - .FirstOrDefault(item => item.GetInterfaces().Contains(typeof(IModule))); - if (moduletype != null) - { - // get property values from IModule - var moduleobject = Activator.CreateInstance(moduletype); - moduledefinition = (ModuleDefinition)moduletype.GetProperty("ModuleDefinition").GetValue(moduleobject); - } - else - { - // set default property values - moduledefinition = new ModuleDefinition - { - Name = moduleType.Substring(moduleType.LastIndexOf(".") + 1), - Description = "Manage " + moduleType.Substring(moduleType.LastIndexOf(".") + 1), - Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : "") - }; - } - // set internal properties - moduledefinition.ModuleDefinitionName = qualifiedModuleType; - moduledefinition.Version = ""; // will be populated from database - moduledefinition.ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1]; - moduledefinition.AssemblyName = assembly.GetName().Name; - - if (string.IsNullOrEmpty(moduledefinition.Categories)) - { - moduledefinition.Categories = "Common"; - } - if (moduledefinition.Categories == "Admin") - { - moduledefinition.Permissions = new List - { - new Permission(PermissionNames.Utilize, Constants.AdminRole, true) - }.EncodePermissions(); - } - else - { - moduledefinition.Permissions = new List - { - new Permission(PermissionNames.Utilize, Constants.AdminRole, true), - new Permission(PermissionNames.Utilize, Constants.RegisteredRole, true) - }.EncodePermissions(); - } - moduledefinitions.Add(moduledefinition); - index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType); + // get property values from IModule + var moduleobject = Activator.CreateInstance(moduletype); + moduledefinition = (ModuleDefinition) moduletype.GetProperty("ModuleDefinition").GetValue(moduleobject); } - moduledefinition = moduledefinitions[index]; - // actions - var modulecontrolobject = Activator.CreateInstance(modulecontroltype); - string actions = (string)modulecontroltype.GetProperty("Actions")?.GetValue(modulecontrolobject); - if (!string.IsNullOrEmpty(actions)) + else { - foreach (string action in actions.Split(',')) + // set default property values + moduledefinition = new ModuleDefinition { - moduledefinition.ControlTypeRoutes += (action + "=" + modulecontroltype.FullName + ", " + typename[1] + ";"); - } + Name = moduleType.Substring(moduleType.LastIndexOf(".") + 1), + Description = "Manage " + moduleType.Substring(moduleType.LastIndexOf(".") + 1), + Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : "") + }; } - moduledefinitions[index] = moduledefinition; + + // set internal properties + moduledefinition.ModuleDefinitionName = qualifiedModuleType; + moduledefinition.Version = ""; // will be populated from database + moduledefinition.ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1]; + moduledefinition.AssemblyName = assembly.GetName().Name; + + if (string.IsNullOrEmpty(moduledefinition.Categories)) + { + moduledefinition.Categories = "Common"; + } + + if (moduledefinition.Categories == "Admin") + { + moduledefinition.Permissions = new List + { + new Permission(PermissionNames.Utilize, Constants.AdminRole, true) + }.EncodePermissions(); + } + else + { + moduledefinition.Permissions = new List + { + new Permission(PermissionNames.Utilize, Constants.AdminRole, true), + new Permission(PermissionNames.Utilize, Constants.RegisteredRole, true) + }.EncodePermissions(); + } + + Console.WriteLine($"Registering module: {moduledefinition.ModuleDefinitionName}"); + moduledefinitions.Add(moduledefinition); + index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType); } + + moduledefinition = moduledefinitions[index]; + // actions + var modulecontrolobject = Activator.CreateInstance(modulecontroltype); + string actions = (string) modulecontroltype.GetProperty("Actions")?.GetValue(modulecontrolobject); + if (!string.IsNullOrEmpty(actions)) + { + foreach (string action in actions.Split(',')) + { + moduledefinition.ControlTypeRoutes += (action + "=" + modulecontroltype.FullName + ", " + typename[1] + ";"); + } + } + + moduledefinitions[index] = moduledefinition; } return moduledefinitions; } - } } diff --git a/Oqtane.Server/Repository/SiteTemplateRepository.cs b/Oqtane.Server/Repository/SiteTemplateRepository.cs index c471f5c0..6c559cdd 100644 --- a/Oqtane.Server/Repository/SiteTemplateRepository.cs +++ b/Oqtane.Server/Repository/SiteTemplateRepository.cs @@ -22,8 +22,8 @@ namespace Oqtane.Repository List siteTemplates = new List(); // iterate through Oqtane site template assemblies - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() - .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".SiteTemplate.")).ToArray(); + var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); + foreach (Assembly assembly in assemblies) { siteTemplates = LoadSiteTemplatesFromAssembly(siteTemplates, assembly); diff --git a/Oqtane.Server/Repository/ThemeRepository.cs b/Oqtane.Server/Repository/ThemeRepository.cs index 943f42a8..9885fe83 100644 --- a/Oqtane.Server/Repository/ThemeRepository.cs +++ b/Oqtane.Server/Repository/ThemeRepository.cs @@ -31,8 +31,7 @@ namespace Oqtane.Repository List themes = new List(); // iterate through Oqtane theme assemblies - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() - .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Theme.")).ToArray(); + var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); foreach (Assembly assembly in assemblies) { themes = LoadThemesFromAssembly(themes, assembly); diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 00b5f188..de4c89a7 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -41,7 +41,7 @@ namespace Oqtane // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { - services.AddMvc().AddNewtonsoftJson(); + services.AddServerSideBlazor(); // setup HttpClient for server side in a client side compatible fashion ( with auth cookie ) @@ -188,16 +188,13 @@ namespace Oqtane services.AddTransient(); // load the external assemblies into the app domain - services.AddOqtaneModules(); - services.AddOqtaneThemes(); - services.AddOqtaneSiteTemplates(); + services.AddOqtaneParts(); services.AddMvc() .AddOqtaneApplicationParts() // register any Controllers from custom modules .AddNewtonsoftJson(); - services.AddOqtaneServices(); - services.AddOqtaneHostedServices(); + services.AddSwaggerGen(c => { diff --git a/Oqtane.Shared/Shared/OqtaneIgnoreAttribute.cs b/Oqtane.Shared/Shared/OqtaneIgnoreAttribute.cs new file mode 100644 index 00000000..0cffe8d1 --- /dev/null +++ b/Oqtane.Shared/Shared/OqtaneIgnoreAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Oqtane.Shared +{ + [AttributeUsage(AttributeTargets.Class)] + public class OqtaneIgnoreAttribute : Attribute + { + } +} From f07146fd5064dd791fbc28c31e249e54fe047f79 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 7 May 2020 16:24:36 -0400 Subject: [PATCH 200/265] updated external module template to support new assembly loading criteria, fixed minor issue in assembky loading logic --- .../Templates/External/Client/Index.razor | 10 +++++----- .../Templates/External/Client/ModuleInfo.cs | 2 +- ...ient.csproj => [Owner].[Module]s.Client.csproj} | 5 +++-- ...age.csproj => [Owner].[Module]s.Package.csproj} | 6 +++--- ...le]s.Module.nuspec => [Owner].[Module]s.nuspec} | 14 +++++++------- .../Templates/External/Package/debug.cmd | 12 ++++++------ .../Templates/External/Package/release.cmd | 2 +- ...rver.csproj => [Owner].[Module]s.Server.csproj} | 5 +++-- ...ared.csproj => [Owner].[Module]s.Shared.csproj} | 3 ++- ....[Module]s.Module.sln => [Owner].[Module]s.sln} | 8 ++++---- .../Controllers/ModuleDefinitionController.cs | 6 +++--- Oqtane.Server/Extensions/AssemblyExtensions.cs | 4 ++-- 12 files changed, 40 insertions(+), 37 deletions(-) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/{[Owner].[Module]s.Module.Client.csproj => [Owner].[Module]s.Client.csproj} (87%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/{[Owner].[Module]s.Module.Package.csproj => [Owner].[Module]s.Package.csproj} (63%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/{[Owner].[Module]s.Module.nuspec => [Owner].[Module]s.nuspec} (81%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/{[Owner].[Module]s.Module.Server.csproj => [Owner].[Module]s.Server.csproj} (88%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/{[Owner].[Module]s.Module.Shared.csproj => [Owner].[Module]s.Shared.csproj} (86%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/{[Owner].[Module]s.Module.sln => [Owner].[Module]s.sln} (86%) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor index 78e7c079..1c92fc1a 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor @@ -42,7 +42,7 @@ else
    [Module] Module Created Successfully. Use Edit Mode To Add A [Module]. You Can Access The Files At The Following Locations:

    [RootPath]Client\
    -- [Owner].[Module]s.Module.Client.csproj - client project
    +- [Owner].[Module]s.Client.csproj - client project
    - _Imports.razor - global imports for module components
    - Edit.razor - component for adding or editing content
    - Index.razor - main component for your module **the content you are reading is in this file**
    @@ -51,12 +51,12 @@ else - Services\I[Module]Service.cs - interface for defining service API methods
    - Services\[Module]Service.cs - implements service API interface methods

    [RootPath]Package\
    -- [Owner].[Module]s.Module.nuspec - nuget manifest for packaging module
    -- [Owner].[Module]s.Module.Package.csproj - packaging project
    +- [Owner].[Module]s.nuspec - nuget manifest for packaging module
    +- [Owner].[Module]s.Package.csproj - packaging project
    - debug.cmd - copies assemblies to Oqtane bin folder when in Debug mode
    - release.cmd - creates nuget package and deploys to Oqtane wwwroot/modules folder when in Release mode

    [RootPath]Server\
    -- [Owner].[Module]s.Module.Server.csproj - server project
    +- [Owner].[Module]s.Server.csproj - server project
    - Controllers\[Module]Controller.cs - API methods implemented using a REST pattern
    - Manager\[Module]Manager.cs - implements optional module interfaces for features such as import/export of content
    - Repository\I[Module]Repository.cs - interface for defining repository methods
    @@ -65,7 +65,7 @@ else - Scripts\[Owner].[Module].1.0.0.sql - database schema definition script

    - Scripts\[Owner].[Module].Uninstall.sql - database uninstall script

    [RootPath]Shared\
    -- [Owner].[Module]s.Module.Shared.csproj - shared project
    +- [Owner].[Module]s.csproj - shared project
    - Models\[Module].cs - model definition

    diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs index a95a461e..35a74a27 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs @@ -10,7 +10,7 @@ namespace [Owner].[Module]s.Modules Name = "[Module]", Description = "[Module]", Version = "1.0.0", - Dependencies = "[Owner].[Module]s.Module.Shared", + Dependencies = "[Owner].[Module]s.Shared.Oqtane", ServerManagerType = "[ServerManagerType]", ReleaseVersions = "1.0.0" }; diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Client.csproj similarity index 87% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Client.csproj index cc286603..e6ea8287 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Module.Client.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/[Owner].[Module]s.Client.csproj @@ -7,8 +7,9 @@ [Owner] [Owner] [Description] - [Owner].[Module]s.Module + [Owner].[Module]s [Owner] + [Owner].[Module]s.Client.Oqtane @@ -18,7 +19,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.Package.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Package.csproj similarity index 63% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.Package.csproj rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Package.csproj index 0689d66f..31d02d8f 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.Package.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Package.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.nuspec b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.nuspec similarity index 81% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.nuspec rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.nuspec index 607f2ff3..57071160 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.Module.nuspec +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.nuspec @@ -1,7 +1,7 @@  - [Owner].[Module]s.Module + [Owner].[Module]s 1.0.0 [Owner] [Owner] @@ -20,12 +20,12 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/debug.cmd b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/debug.cmd index 37f0dd87..c70bdba8 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/debug.cmd +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/debug.cmd @@ -1,6 +1,6 @@ -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 +XCOPY "..\Client\bin\Debug\netstandard2.1\[Owner].[Module]s.Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y +XCOPY "..\Client\bin\Debug\netstandard2.1\[Owner].[Module]s.Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y +XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Server.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y +XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Server.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y +XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Shared.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y +XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Shared.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/release.cmd b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/release.cmd index 71f2c513..b294ccdf 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/release.cmd +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/release.cmd @@ -1,2 +1,2 @@ -"..\..\[RootFolder]\oqtane.package\nuget.exe" pack [Owner].[Module]s.Module.nuspec +"..\..\[RootFolder]\oqtane.package\nuget.exe" pack [Owner].[Module]s.nuspec XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\wwwroot\Modules\" /Y diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Server.csproj similarity index 88% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Server.csproj index 96d4154f..0c93403b 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Module.Server.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Server.csproj @@ -5,11 +5,12 @@ 7.3 true 1.0.0 - [Owner].[Module]s.Module + [Owner].[Module]s [Owner] [Owner] [Description] [Owner] + [Owner].[Module]s.Server.Oqtane @@ -26,7 +27,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/[Owner].[Module]s.Module.Shared.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/[Owner].[Module]s.Shared.csproj similarity index 86% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/[Owner].[Module]s.Module.Shared.csproj rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/[Owner].[Module]s.Shared.csproj index 2482b4fa..fb4d620d 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/[Owner].[Module]s.Module.Shared.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Shared/[Owner].[Module]s.Shared.csproj @@ -4,11 +4,12 @@ netstandard2.1 7.3 1.0.0 - [Owner].[Module].Module + [Owner].[Module]s [Owner] [Owner] [Description] [Owner] + [Owner].[Module]s.Shared.Oqtane diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/[Owner].[Module]s.Module.sln b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/[Owner].[Module]s.sln similarity index 86% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/[Owner].[Module]s.Module.sln rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/[Owner].[Module]s.sln index 9879eac6..eb313e64 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/[Owner].[Module]s.Module.sln +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/[Owner].[Module]s.sln @@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28621.142 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Module.Client", "Client\[Owner].[Module]s.Module.Client.csproj", "{AA8E58A1-CD09-4208-BF66-A8BB341FD669}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Client", "Client\[Owner].[Module]s.Client.csproj", "{AA8E58A1-CD09-4208-BF66-A8BB341FD669}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Module.Server", "Server\[Owner].[Module]s.Module.Server.csproj", "{04B05448-788F-433D-92C0-FED35122D45A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Server", "Server\[Owner].[Module]s.Server.csproj", "{04B05448-788F-433D-92C0-FED35122D45A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Module.Shared", "Shared\[Owner].[Module]s.Module.Shared.csproj", "{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Module]s.Shared", "Shared\[Owner].[Module]s.Shared.csproj", "{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "[Owner].[Module]s.Module.Package", "Package\[Owner].[Module]s.Module.Package.csproj", "{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "[Owner].[Module]s.Package", "Package\[Owner].[Module]s.Package.csproj", "{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 7ef9f7e1..e1b9534c 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -190,9 +190,9 @@ namespace Oqtane.Controllers } else { - rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module","\\"); - moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Client"; - moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Module.Server"; + rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s","\\"); + moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Client.Oqtane"; + moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Server.Oqtane"; } ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, moduleDefinition); diff --git a/Oqtane.Server/Extensions/AssemblyExtensions.cs b/Oqtane.Server/Extensions/AssemblyExtensions.cs index 9785d229..443b8cc6 100644 --- a/Oqtane.Server/Extensions/AssemblyExtensions.cs +++ b/Oqtane.Server/Extensions/AssemblyExtensions.cs @@ -36,12 +36,12 @@ namespace System.Reflection public static bool IsOqtaneAssembly(this Assembly assembly) { - return assembly.FullName != null && (assembly.FullName.Contains("oqtane.", StringComparison.OrdinalIgnoreCase)); + return assembly.FullName != null && (assembly.FullName.Contains("oqtane", StringComparison.OrdinalIgnoreCase)); } public static bool IsOqtaneAssembly(this FileInfo fileInfo) { - return (fileInfo.Name.Contains("oqtane.", StringComparison.OrdinalIgnoreCase)); + return (fileInfo.Name.Contains("oqtane", StringComparison.OrdinalIgnoreCase)); } public static IEnumerable GetOqtaneAssemblies(this AppDomain appDomain) From 97354a952527ed61b2c17e44e4ab6bc56e893cd5 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 8 May 2020 08:34:31 -0400 Subject: [PATCH 201/265] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15b1222f..5baaef33 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Please note that this project is governed by the **[.NET Foundation Contributor # Roadmap This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist. -Note: We are planning to release V1 at the same time that Blazor WebAssembly ships on May 21, 2020 +Note: We are planning to release V1 at the same time that Blazor WebAssembly ships on May 19, 2020 V1 (MVP) - [x] Multi-Tenant ( Shared Database & Isolated Database ) From 051534b80c947b0d1d3f63a45e6ed1053ad0138c Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 8 May 2020 12:17:06 -0400 Subject: [PATCH 202/265] resolved #270 - migrated raw html entry into richtexteditor control --- .../Modules/Controls/RichTextEditor.razor | 158 ++++++++++++---- .../Controls}/RichTextEditorInterop.cs | 2 +- Oqtane.Client/Modules/HtmlText/Edit.razor | 168 ++++-------------- Oqtane.Client/Modules/HtmlText/Index.razor | 2 +- 4 files changed, 158 insertions(+), 172 deletions(-) rename Oqtane.Client/{UI => Modules/Controls}/RichTextEditorInterop.cs (98%) diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index 175717fd..1586eac7 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -1,29 +1,82 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@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 + { + + } +
    +
    @@ -32,10 +85,12 @@ private ElementReference _toolBar; private bool _filemanagervisible = false; private FileManager _fileManager; + private string _original = string.Empty; + private string _content = 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; @@ -43,12 +98,27 @@ [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() + { + _original = Content; + + _content = _original; + if (string.IsNullOrEmpty(_content)) + { + _content = Placeholder; + } + } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) @@ -61,6 +131,10 @@ Placeholder, Theme, DebugLevel); + + await RichTextEditorInterop.LoadEditorContent( + JsRuntime, + _editorElement, _original); } } @@ -73,9 +147,18 @@ public async Task GetHtml() { - return await RichTextEditorInterop.GetHtml( - JsRuntime, - _editorElement); + if (_original != _content) + { + // raw html content changed + return _content; + } + else + { + // return rich text content + return await RichTextEditorInterop.GetHtml( + JsRuntime, + _editorElement); + } } public async Task GetContent() @@ -85,13 +168,6 @@ _editorElement); } - public async Task LoadContent(string content) - { - await RichTextEditorInterop.LoadEditorContent( - JsRuntime, - _editorElement, content); - } - public async Task EnableEditor(bool mode) { await RichTextEditorInterop.EnableEditor( @@ -122,7 +198,6 @@ _filemanagervisible = true; _message = string.Empty; } - StateHasChanged(); } @@ -130,8 +205,21 @@ { _filemanagervisible = false; _message = string.Empty; - StateHasChanged(); } + public async Task RefreshRichText() + { + await RichTextEditorInterop.LoadEditorContent( + JsRuntime, + _editorElement, _content); + } + + public async Task RefreshRawHtml() + { + _content = await RichTextEditorInterop.GetHtml( + JsRuntime, + _editorElement); + StateHasChanged(); + } } diff --git a/Oqtane.Client/UI/RichTextEditorInterop.cs b/Oqtane.Client/Modules/Controls/RichTextEditorInterop.cs similarity index 98% rename from Oqtane.Client/UI/RichTextEditorInterop.cs rename to Oqtane.Client/Modules/Controls/RichTextEditorInterop.cs index 54536005..1f590f8a 100644 --- a/Oqtane.Client/UI/RichTextEditorInterop.cs +++ b/Oqtane.Client/Modules/Controls/RichTextEditorInterop.cs @@ -2,7 +2,7 @@ using Microsoft.JSInterop; using System.Threading.Tasks; -namespace Oqtane.UI +namespace Oqtane.Modules.Controls { public static class RichTextEditorInterop { diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index ab0f1feb..9df38687 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -7,111 +7,48 @@ @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; + private string _content = null; + 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 - { - get => _richTextEditorMode; - set - { - _richTextEditorMode = value; - - if (_richTextEditorMode) - { - _visibleText = "d-none"; - _visibleRich = string.Empty; - } - else - { - _visibleText = string.Empty; - _visibleRich = "d-none"; - } - } - } - - protected override async Task OnAfterRenderAsync(bool firstRender) + protected override async Task OnInitializedAsync() { try { - if (firstRender) + var htmltextservice = new HtmlTextService(http, sitestate); + 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,48 +58,10 @@ } } - 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 { @@ -180,7 +79,7 @@ htmltext.Content = content; await htmltextservice.AddHtmlTextAsync(htmltext); } - + await logger.LogInformation("Html/Text Content Saved {HtmlText}", htmltext); NavigationManager.NavigateTo(NavigateUrl()); } @@ -190,5 +89,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..c76afa02 100644 --- a/Oqtane.Client/Modules/HtmlText/Index.razor +++ b/Oqtane.Client/Modules/HtmlText/Index.razor @@ -30,7 +30,7 @@ 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) From aaf2c96374f016b3741b05d95205bfd45d282a22 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 8 May 2020 12:25:37 -0400 Subject: [PATCH 203/265] minor fix for placeholder content --- Oqtane.Client/Modules/Controls/RichTextEditor.razor | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index 1586eac7..fad5399f 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -69,11 +69,11 @@
    @if (ReadOnly) { - + } else { - + } @@ -111,12 +111,7 @@ protected override void OnInitialized() { _original = Content; - _content = _original; - if (string.IsNullOrEmpty(_content)) - { - _content = Placeholder; - } } protected override async Task OnAfterRenderAsync(bool firstRender) From 1e270e642331214cddb2f3a8f78c5fd5fce84dd1 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Fri, 8 May 2020 21:30:31 +0200 Subject: [PATCH 204/265] Ignore abstract classes at loading --- Oqtane.Server/Repository/ModuleDefinitionRepository.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index 9e717e37..3b9891b4 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -194,6 +194,7 @@ namespace Oqtane.Repository // Check if type should be ignored if (modulecontroltype.Name == "ModuleBase" || modulecontroltype.IsGenericType + || modulecontroltype.IsAbstract || Attribute.IsDefined(modulecontroltype, typeof(OqtaneIgnoreAttribute)) ) continue; From c2ed71ab0d337ba99194e618d7a9284b3a46cc5c Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 8 May 2020 17:30:35 -0400 Subject: [PATCH 205/265] added classes to all theme controls, added mobile support to Oqtane theme --- .../Modules/Controls/RichTextEditor.razor | 104 ++++++++------- .../Themes/BlazorTheme/Default.razor | 10 +- .../Themes/Controls/Breadcrumbs.razor | 32 +++-- .../Themes/Controls/ControlPanel.razor | 1 - Oqtane.Client/Themes/Controls/Login.razor | 24 ++-- Oqtane.Client/Themes/Controls/Logo.razor | 19 +-- .../Themes/Controls/MenuHorizontal.Razor | 7 +- .../Themes/Controls/MenuVertical.razor | 44 +++--- .../Themes/Controls/ModuleActions.razor | 30 +++-- .../Themes/Controls/ModuleTitle.razor | 4 +- .../Themes/Controls/UserProfile.razor | 31 ++--- .../Themes/OqtaneTheme/Default.razor | 5 +- Oqtane.Client/UI/SiteRouter.razor | 11 +- Oqtane.Client/_Imports.razor | 3 +- .../Repository/ModuleDefinitionRepository.cs | 17 +-- Oqtane.Server/Repository/ThemeRepository.cs | 126 +++++++++--------- .../Oqtane.Themes.BlazorTheme/Theme.css | 1 + .../Oqtane.Themes.OqtaneTheme/Theme.css | 39 ++++++ 18 files changed, 278 insertions(+), 230 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index fad5399f..47df0248 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -85,8 +85,8 @@ private ElementReference _toolBar; private bool _filemanagervisible = false; private FileManager _fileManager; - private string _original = string.Empty; private string _content = string.Empty; + private string _original = string.Empty; private string _message = string.Empty; [Parameter] @@ -110,8 +110,7 @@ protected override void OnInitialized() { - _original = Content; - _content = _original; + _content = Content; // raw HTML } protected override async Task OnAfterRenderAsync(bool firstRender) @@ -129,45 +128,54 @@ await RichTextEditorInterop.LoadEditorContent( JsRuntime, - _editorElement, _original); - } - } + _editorElement, Content); - public async Task GetText() - { - return await RichTextEditorInterop.GetText( - JsRuntime, - _editorElement); - } - - public async Task GetHtml() - { - if (_original != _content) - { - // raw html content changed - return _content; - } - else - { - // return rich text content - return await RichTextEditorInterop.GetHtml( + // preserve a copy of the rich text content ( Quill sanitizes content so we need to retrieve it from the editor ) + _original = await RichTextEditorInterop.GetHtml( JsRuntime, _editorElement); } } - public async Task GetContent() + public void CloseFileManager() { - return await RichTextEditorInterop.GetContent( - JsRuntime, - _editorElement); + _filemanagervisible = false; + _message = string.Empty; + StateHasChanged(); } - public async Task EnableEditor(bool mode) + public async Task RefreshRichText() { - await RichTextEditorInterop.EnableEditor( + await RichTextEditorInterop.LoadEditorContent( JsRuntime, - _editorElement, mode); + _editorElement, _content); + } + + public async Task RefreshRawHtml() + { + _content = await RichTextEditorInterop.GetHtml( + JsRuntime, + _editorElement); + StateHasChanged(); + } + + public async Task GetHtml() + { + // get rich text content + string content = await RichTextEditorInterop.GetHtml( + JsRuntime, + _editorElement); + + if (_original != content) + { + // rich text content has changed - return it + return content; + } + else + { + // return raw html content + return _content; + } } public async Task InsertImage() @@ -196,25 +204,25 @@ 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(); - } - - public async Task RefreshRichText() - { - await RichTextEditorInterop.LoadEditorContent( - JsRuntime, - _editorElement, _content); - } - - public async Task RefreshRawHtml() - { - _content = await RichTextEditorInterop.GetHtml( + return await RichTextEditorInterop.GetText( JsRuntime, _editorElement); - StateHasChanged(); + } + + public async Task GetContent() + { + return await RichTextEditorInterop.GetContent( + JsRuntime, + _editorElement); + } + + public async Task EnableEditor(bool mode) + { + await RichTextEditorInterop.EnableEditor( + JsRuntime, + _editorElement, mode); } } diff --git a/Oqtane.Client/Themes/BlazorTheme/Default.razor b/Oqtane.Client/Themes/BlazorTheme/Default.razor index 7d9301e2..ecd3596e 100644 --- a/Oqtane.Client/Themes/BlazorTheme/Default.razor +++ b/Oqtane.Client/Themes/BlazorTheme/Default.razor @@ -7,15 +7,7 @@ diff --git a/Oqtane.Client/Themes/Controls/Breadcrumbs.razor b/Oqtane.Client/Themes/Controls/Breadcrumbs.razor index 06d9eceb..310132f5 100644 --- a/Oqtane.Client/Themes/Controls/Breadcrumbs.razor +++ b/Oqtane.Client/Themes/Controls/Breadcrumbs.razor @@ -4,24 +4,30 @@ @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 e59feb1f..ec463657 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -1,5 +1,4 @@ @namespace Oqtane.Themes.Controls -@using Oqtane.Enums @inherits ThemeControlBase @attribute [OqtaneIgnore] @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Themes/Controls/Login.razor b/Oqtane.Client/Themes/Controls/Login.razor index dbdb9444..ffa46d15 100644 --- a/Oqtane.Client/Themes/Controls/Login.razor +++ b/Oqtane.Client/Themes/Controls/Login.razor @@ -2,14 +2,16 @@ @inherits LoginBase @attribute [OqtaneIgnore] - - - ... - - - - - - - - + \ No newline at end of file diff --git a/Oqtane.Client/Themes/Controls/Logo.razor b/Oqtane.Client/Themes/Controls/Logo.razor index c98d58b7..e189b753 100644 --- a/Oqtane.Client/Themes/Controls/Logo.razor +++ b/Oqtane.Client/Themes/Controls/Logo.razor @@ -1,22 +1,13 @@ @namespace Oqtane.Themes.Controls @inherits ThemeControlBase @attribute [OqtaneIgnore] -@inject NavigationManager NavigationManager @if (PageState.Site.LogoFileId != null) { - - @PageState.Site.Name - + } -@code { - string Href - { - get - { - var uri = new Uri(NavigationManager.Uri); - return $"{uri.Scheme}://{uri.Authority}"; - } - } -} diff --git a/Oqtane.Client/Themes/Controls/MenuHorizontal.Razor b/Oqtane.Client/Themes/Controls/MenuHorizontal.Razor index 075a955a..22728021 100644 --- a/Oqtane.Client/Themes/Controls/MenuHorizontal.Razor +++ b/Oqtane.Client/Themes/Controls/MenuHorizontal.Razor @@ -3,11 +3,10 @@ @attribute [OqtaneIgnore] @if (MenuPages.Any()) { +
    - - } diff --git a/Oqtane.Client/Themes/Controls/ModuleActions.razor b/Oqtane.Client/Themes/Controls/ModuleActions.razor index 0a9cc6ea..90742004 100644 --- a/Oqtane.Client/Themes/Controls/ModuleActions.razor +++ b/Oqtane.Client/Themes/Controls/ModuleActions.razor @@ -4,18 +4,20 @@ @if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions)) { - -
    - + @@ -112,57 +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.SiteId = PageState.Site.SiteId; - - if (_parentId == -1) - { - folder.ParentId = null; - } - else - { - folder.ParentId = _parentId; - } - - 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 (folder != null) - { - await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); - await logger.LogInformation("Folder Saved {Folder}", folder); - NavigationManager.NavigateTo(NavigateUrl()); - } - else - { - AddModuleMessage("An Error Was Encountered Saving The Folder", MessageType.Error); - } + folder = await FolderService.GetFolderAsync(_folderId); } else { - AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning); + folder = new Folder(); + } + + folder.SiteId = PageState.Site.SiteId; + + if (_parentId == -1) + { + folder.ParentId = null; + } + else + { + folder.ParentId = _parentId; + } + + 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 (folder != null) + { + await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); + await logger.LogInformation("Folder Saved {Folder}", folder); + NavigationManager.NavigateTo(NavigateUrl()); + } + else + { + AddModuleMessage("An Error Was Encountered Saving The Folder", MessageType.Error); } } catch (Exception ex) diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index fb824641..ebaaa590 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -105,7 +105,7 @@ namespace Oqtane.Controllers } if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, permissions)) { - if (FolderPathValid(folder)) + if (folder.IsPathValid()) { if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { @@ -140,7 +140,7 @@ namespace Oqtane.Controllers { if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit)) { - if (FolderPathValid(folder)) + if (folder.IsPathValid()) { if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) { @@ -210,13 +210,5 @@ namespace Oqtane.Controllers HttpContext.Response.StatusCode = 401; } } - - private bool FolderPathValid(Folder folder) - { - // prevent folder path traversal and reserved devices - return (folder.Name.IndexOfAny(Constants.InvalidFileNameChars) == -1 && - !Constants.InvalidFileNameEndingChars.Any(x => folder.Name.EndsWith(x)) && - !Constants.ReservedDevices.Split(',').Contains(folder.Name.ToUpper().Split('.')[0])); - } } } diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 11bef4dd..de81561b 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -2,8 +2,10 @@ using System; using System.Globalization; using System.IO; +using System.Linq; using System.Text; using System.Text.RegularExpressions; +using File = Oqtane.Models.File; namespace Oqtane.Shared { @@ -254,5 +256,22 @@ namespace Oqtane.Shared return Path.Combine(segments).TrimEnd(); } + + public static bool IsPathValid(this Folder folder) + { + return IsPathOrFileValid(folder.Name); + } + + public static bool IsFileValid(this File file) + { + return IsPathOrFileValid(file.Name); + } + + public static bool IsPathOrFileValid(this string name) + { + return (name.IndexOfAny(Constants.InvalidFileNameChars) == -1 && + !Constants.InvalidFileNameEndingChars.Any(name.EndsWith) && + !Constants.ReservedDevices.Split(',').Contains(name.ToUpper().Split('.')[0])); + } } } From 5e04cb18a456199c3050934be44d7d1c356ed48e Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Fri, 15 May 2020 08:18:07 +0200 Subject: [PATCH 225/265] File Manager Tune-up --- Oqtane.Client/Modules/Admin/Files/Add.razor | 2 +- .../Modules/Admin/ModuleDefinitions/Add.razor | 2 +- Oqtane.Client/Modules/Admin/Site/Index.razor | 8 +- Oqtane.Client/Modules/Admin/Themes/Add.razor | 2 +- .../Modules/Admin/UserProfile/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Users/Edit.razor | 2 +- .../Modules/Controls/FileManager.razor | 163 +++++++++--------- 7 files changed, 87 insertions(+), 94 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Files/Add.razor b/Oqtane.Client/Modules/Admin/Files/Add.razor index a01b9b1c..653f5cf3 100644 --- a/Oqtane.Client/Modules/Admin/Files/Add.razor +++ b/Oqtane.Client/Modules/Admin/Files/Add.razor @@ -12,7 +12,7 @@ - +
    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/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/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/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/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index 8dc70ffb..35f04706 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -1,5 +1,6 @@ @namespace Oqtane.Modules.Controls -@inherits ModuleBase +@inherits ModuleBase + @attribute [OqtaneIgnore] @inject IFolderService FolderService @inject IFileService FileService @@ -10,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)
    }
    @@ -84,19 +88,19 @@ @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; private string _guid; + private int _folderId = -1; + private bool _uploadMultiple; + private int _fileId; [Parameter] public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility @@ -105,19 +109,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() { @@ -129,56 +139,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() @@ -191,11 +184,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 { @@ -222,9 +215,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(); } @@ -238,7 +231,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(); @@ -247,21 +240,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 = "\"""; } } } @@ -281,7 +274,7 @@ } else { - result = await FileService.UploadFilesAsync(_folderid, upload, _guid); + result = await FileService.UploadFilesAsync(FolderId, upload, _guid); } if (result == string.Empty) @@ -295,7 +288,7 @@ var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault(); if (file != null) { - _fileid = file.FileId; + FileId = file.FileId; await SetImage(); } } @@ -325,21 +318,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; } From 9850e249fc913a65d2124feaa88918681cb44875 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Fri, 15 May 2020 08:20:00 +0200 Subject: [PATCH 226/265] File Controller bug --- Oqtane.Server/Controllers/FileController.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 0c30bbdf..0bfe1de2 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -413,8 +413,11 @@ namespace Oqtane.Controllers { _logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath); HttpContext.Response.StatusCode = 404; - byte[] filebytes = System.IO.File.ReadAllBytes(errorpath); - return File(filebytes, "application/octet-stream", file.Name); + if (System.IO.File.Exists(errorpath)) + { + byte[] filebytes = System.IO.File.ReadAllBytes(errorpath); + return File(filebytes, "application/octet-stream", file.Name); + } } } else @@ -432,6 +435,7 @@ namespace Oqtane.Controllers byte[] filebytes = System.IO.File.ReadAllBytes(errorpath); return File(filebytes, "application/octet-stream", "error.png"); } + return null; } private string GetFolderPath(Folder folder) From ac03afb146c881f1a00673cef7aedde2269857d9 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 15 May 2020 09:50:48 -0400 Subject: [PATCH 227/265] added ability to set default container at the page level, expanded size of role description in upgrade script for 0.9.2 --- .../Modules/Admin/Modules/Settings.razor | 20 ++++++++- Oqtane.Client/Modules/Admin/Pages/Add.razor | 36 ++++++++++++---- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 41 +++++++++++++++---- Oqtane.Client/UI/SiteRouter.razor | 2 +- Oqtane.Server/Controllers/PageController.cs | 1 + Oqtane.Server/Oqtane.Server.csproj | 2 + Oqtane.Server/Repository/SiteRepository.cs | 1 + Oqtane.Server/Scripts/Tenant.0.9.1.sql | 2 +- Oqtane.Server/Scripts/Tenant.0.9.2.sql | 18 ++++++++ Oqtane.Shared/Models/Page.cs | 1 + 10 files changed, 104 insertions(+), 20 deletions(-) create mode 100644 Oqtane.Server/Scripts/Tenant.0.9.2.sql diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index eec6c6e2..3d981593 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -24,7 +24,7 @@ - + @foreach (KeyValuePair item in _themes) { if (item.Key == _themetype) @@ -122,7 +122,7 @@ + + + + + + + + @@ -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/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index ac7b692e..488c43bf 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -260,7 +260,7 @@ if (PageState == null || reload >= Reload.Page) { modules = await ModuleService.GetModulesAsync(site.SiteId); - modules = ProcessModules(modules, page.PageId, _pagestate.ModuleId, _pagestate.Action, page.Panes, site.DefaultContainerType); + modules = ProcessModules(modules, page.PageId, _pagestate.ModuleId, _pagestate.Action, page.Panes, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType); } else { diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 915fccdc..6797796a 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -166,6 +166,7 @@ namespace Oqtane.Controllers page.EditMode = false; page.ThemeType = parent.ThemeType; page.LayoutType = parent.LayoutType; + page.DefaultContainerType = parent.DefaultContainerType; page.Icon = parent.Icon; page.Permissions = new List { new Permission(PermissionNames.View, userid, true), diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index b298fc75..88064ed9 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -19,12 +19,14 @@ + + diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index d7c8a1e5..ae179abe 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -757,6 +757,7 @@ namespace Oqtane.Repository EditMode = pagetemplate.EditMode, ThemeType = "", LayoutType = "", + DefaultContainerType = "", Icon = pagetemplate.Icon, Permissions = pagetemplate.PagePermissions, IsPersonalizable = pagetemplate.IsPersonalizable, diff --git a/Oqtane.Server/Scripts/Tenant.0.9.1.sql b/Oqtane.Server/Scripts/Tenant.0.9.1.sql index 94728128..55825ada 100644 --- a/Oqtane.Server/Scripts/Tenant.0.9.1.sql +++ b/Oqtane.Server/Scripts/Tenant.0.9.1.sql @@ -1,6 +1,6 @@ /* -migration script +Version 0.9.1 migration script */ diff --git a/Oqtane.Server/Scripts/Tenant.0.9.2.sql b/Oqtane.Server/Scripts/Tenant.0.9.2.sql new file mode 100644 index 00000000..11330770 --- /dev/null +++ b/Oqtane.Server/Scripts/Tenant.0.9.2.sql @@ -0,0 +1,18 @@ +/* + +Version 0.9.2 migration script + +*/ + +ALTER TABLE [dbo].[Role] +ALTER COLUMN [Description] VARCHAR (256) NOT NULL +GO + +ALTER TABLE [dbo].[Page] ADD + [DefaultContainerType] [nvarchar](200) NULL +GO + +UPDATE [dbo].[Page] +SET [DefaultContainerType] = '' +GO + diff --git a/Oqtane.Shared/Models/Page.cs b/Oqtane.Shared/Models/Page.cs index f1268875..2d3f3624 100644 --- a/Oqtane.Shared/Models/Page.cs +++ b/Oqtane.Shared/Models/Page.cs @@ -15,6 +15,7 @@ namespace Oqtane.Models public string Url { get; set; } public string ThemeType { get; set; } public string LayoutType { get; set; } + public string DefaultContainerType { get; set; } public string Icon { get; set; } public bool IsNavigation { get; set; } public bool EditMode { get; set; } From 3cbb6e3e6ed917001156f55576d070928440bb38 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 15 May 2020 12:36:52 -0400 Subject: [PATCH 228/265] enable module creator to add embeddedresources to csproj for internal modules --- .../Templates/External/Client/Index.razor | 4 +-- .../Modules/[Module]/Index.razor | 4 +-- .../Controllers/ModuleDefinitionController.cs | 26 ++++++++++++++++--- Oqtane.Server/Oqtane.Server.csproj | 23 +++++----------- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor index df16458b..111e69ab 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor @@ -61,8 +61,8 @@ else - Repository\I[Module]Repository.cs - interface for defining repository methods
    - Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core
    - Repository\[Module]Context.cs - provides a DB Context for data access
    -- Scripts\[Owner].[Module].1.0.0.sql - database schema definition script

    -- Scripts\[Owner].[Module].Uninstall.sql - database uninstall script

    +- Scripts\[Owner].[Module]s.1.0.0.sql - database schema definition script
    +- Scripts\[Owner].[Module]s.Uninstall.sql - database uninstall script

    [RootPath]Shared\
    - [Owner].[Module]s.csproj - shared project
    - Models\[Module].cs - model definition

    diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor index 0656cb80..b683abf4 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor @@ -53,8 +53,8 @@ else - Repository\I[Module]Repository.cs - interface for defining repository methods
    - Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core
    - Repository\[Module]Context.cs - provides a DB Context for data access
    -- Scripts\[Owner].[Module].1.0.0.sql - database schema definition script

    -- Scripts\[Owner].[Module].Uninstall.sql - database uninstall script

    +- Scripts\[Owner].[Module]s.1.0.0.sql - database schema definition script
    +- Scripts\[Owner].[Module]s.Uninstall.sql - database uninstall script

    [RootPath]Oqtane.Shared\Modules\[Module]\
    - Models\[Module].cs - model definition

    diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index d9131d69..039d6782 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -14,6 +14,8 @@ using Oqtane.Security; using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Configuration; +using System.Xml.Linq; +using Microsoft.AspNetCore.Mvc.Formatters; // ReSharper disable StringIndexOfIsCultureSpecific.1 namespace Oqtane.Controllers @@ -208,13 +210,13 @@ namespace Oqtane.Controllers if (moduleDefinition.Template == "internal") { rootPath = Utilities.PathCombine(rootFolder.FullName,"\\"); - moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, Oqtane.Client"; + moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, Oqtane.Client"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server"; } else { rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s","\\"); - moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Client.Oqtane"; + moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Client.Oqtane"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Server.Oqtane"; } @@ -227,7 +229,11 @@ namespace Oqtane.Controllers if (moduleDefinition.Template == "internal") { - // need logic to add embedded scripts to Oqtane.Server.csproj - also you need to remove them on uninstall + // add embedded resources to project + List resources = new List(); + resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.1.0.0.sql")); + resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Uninstall.sql")); + EmbedResourceFiles(Utilities.PathCombine(rootPath, "Oqtane.Server", "Oqtane.Server.csproj"), resources); } _installationManager.RestartApplication(); @@ -276,5 +282,19 @@ namespace Oqtane.Controllers } } } + + private void EmbedResourceFiles(string projectfile, List resources) + { + XDocument project = XDocument.Load(projectfile); + var itemGroup = project.Descendants("ItemGroup").Descendants("EmbeddedResource").FirstOrDefault().Parent; + if (itemGroup != null) + { + foreach (var resource in resources) + { + itemGroup.Add(new XElement("EmbeddedResource", new XAttribute("Include", resource))); + } + } + project.Save(projectfile); + } } } diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 88064ed9..a2f3e77c 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -1,5 +1,5 @@ - - + + netcoreapp3.1 7.3 @@ -16,21 +16,14 @@ Not for production use. Oqtane - - - - - - + + + + - - - - - @@ -41,10 +34,8 @@ - - - + \ No newline at end of file From 8a1e83ff7f60f3342101137c55e917bf286918e9 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 15 May 2020 17:43:45 -0400 Subject: [PATCH 229/265] Modified the package installer to use target folders ( based on the Nuget specification ) rather than file extensions --- .../External/Package/[Owner].[Module]s.nuspec | 1 + .../Templates/External/content/resources.txt | 1 + .../Templates/External/wwwroot/resources.txt | 2 +- .../Infrastructure/InstallationManager.cs | 38 ++++++++++--------- Oqtane.Server/Startup.cs | 2 +- Oqtane.Server/wwwroot/resources.txt | 1 + 6 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/content/resources.txt create mode 100644 Oqtane.Server/wwwroot/resources.txt diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.nuspec b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.nuspec index 57071160..64c81a8d 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.nuspec +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.nuspec @@ -27,5 +27,6 @@ +
    \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/content/resources.txt b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/content/resources.txt new file mode 100644 index 00000000..ac84ee50 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/content/resources.txt @@ -0,0 +1 @@ +This is the location where static resources for third party libraries should be located ( the third party library assemblies will be included in the /lib folder ). They should be placed in subfolders which match the naming convention of the third party library. When the module package is deployed the static resource subfolders will be extracted under the web root. \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/wwwroot/resources.txt b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/wwwroot/resources.txt index 2542de03..91780f80 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/wwwroot/resources.txt +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/wwwroot/resources.txt @@ -1 +1 @@ -This is the location where static resources such as images or style sheets should be located \ No newline at end of file +This is the location where static resources such as images or style sheets for this module will be located. Static assets can be organized in subfolders. When the module package is deployed the assets will be extracted under the web root in a folder that matches the module name. \ No newline at end of file diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index b1f73543..adce62e9 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -28,7 +28,7 @@ namespace Oqtane.Infrastructure { var webRootPath = _environment.WebRootPath; - var install = UnpackPackages(folders, webRootPath); + var install = InstallPackages(folders, webRootPath); if (install && restart) { @@ -36,7 +36,7 @@ namespace Oqtane.Infrastructure } } - public static bool UnpackPackages(string folders, string webRootPath) + public static bool InstallPackages(string folders, string webRootPath) { bool install = false; string binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); @@ -44,14 +44,12 @@ namespace Oqtane.Infrastructure foreach (string folder in folders.Split(',')) { string sourceFolder = Path.Combine(webRootPath, folder); - - // create folder if it does not exist if (!Directory.Exists(sourceFolder)) { Directory.CreateDirectory(sourceFolder); } - // iterate through packages + // iterate through Nuget packages in source folder foreach (string packagename in Directory.GetFiles(sourceFolder, "*.nupkg")) { string name = Path.GetFileNameWithoutExtension(packagename); @@ -89,29 +87,33 @@ namespace Oqtane.Infrastructure // deploy to appropriate locations foreach (ZipArchiveEntry entry in archive.Entries) { + string foldername = Path.GetDirectoryName(entry.FullName).Split('\\')[0]; string filename = Path.GetFileName(entry.FullName); - switch (Path.GetExtension(filename).ToLower()) + + switch (foldername) { - case ".pdb": - case ".dll": + case "lib": if (binFolder != null) entry.ExtractToFile(Path.Combine(binFolder, filename), true); break; - case ".png": - case ".jpg": - case ".jpeg": - case ".gif": - case ".svg": - case ".js": - case ".css": - string entryPath = Utilities.PathCombine(entry.FullName.Replace("wwwroot", name).Split('/')); - filename = Path.Combine(sourceFolder, entryPath); + case "wwwroot": + filename = Path.Combine(sourceFolder, Utilities.PathCombine(entry.FullName.Replace("wwwroot", name).Split('/'))); if (!Directory.Exists(Path.GetDirectoryName(filename))) { Directory.CreateDirectory(Path.GetDirectoryName(filename)); } - entry.ExtractToFile(filename, true); break; + case "content": + if (Path.GetDirectoryName(entry.FullName) != "content") // assets must be in subfolders + { + filename = Path.Combine(webRootPath, Utilities.PathCombine(entry.FullName.Replace("content", "").Split('/'))); + if (!Directory.Exists(Path.GetDirectoryName(filename))) + { + Directory.CreateDirectory(Path.GetDirectoryName(filename)); + } + entry.ExtractToFile(filename, true); + } + break; } } } diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 34f73ffe..b47cdd4f 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -163,7 +163,7 @@ namespace Oqtane services.AddSingleton(); // install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain ) - InstallationManager.UnpackPackages("Modules,Themes", _webRoot); + InstallationManager.InstallPackages("Modules,Themes", _webRoot); // register transient scoped core services services.AddTransient(); diff --git a/Oqtane.Server/wwwroot/resources.txt b/Oqtane.Server/wwwroot/resources.txt new file mode 100644 index 00000000..2542de03 --- /dev/null +++ b/Oqtane.Server/wwwroot/resources.txt @@ -0,0 +1 @@ +This is the location where static resources such as images or style sheets should be located \ No newline at end of file From 13adebb36c81930e3c965181ec119c4878e00497 Mon Sep 17 00:00:00 2001 From: Jim Spillane Date: Fri, 15 May 2020 23:12:24 -0400 Subject: [PATCH 230/265] Add File Name validation Apply file name validation rules to the File Controller and client. --- Oqtane.Client/Modules/Admin/Files/Add.razor | 35 +++-- Oqtane.Server/Controllers/FileController.cs | 147 +++++++++++--------- 2 files changed, 110 insertions(+), 72 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Files/Add.razor b/Oqtane.Client/Modules/Admin/Files/Add.razor index 653f5cf3..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 @@ -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.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 0bfe1de2..0c4bfad6 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -189,41 +189,54 @@ namespace Oqtane.Controllers { Models.File file = null; Folder folder = _folders.GetFolder(int.Parse(folderid)); - if (folder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions)) - { - string folderPath = GetFolderPath(folder); - CreateDirectory(folderPath); - string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); - // check for allowable file extensions - if (Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(filename).ToLower().Replace(".", ""))) - { - try - { - var client = new WebClient(); - string targetPath = Path.Combine(folderPath, filename); - // remove file if it already exists - if (System.IO.File.Exists(targetPath)) - { - System.IO.File.Delete(targetPath); - } - client.DownloadFile(url, targetPath); - _files.AddFile(CreateFile(filename, folder.FolderId, targetPath)); - } - catch - { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url); - } - } - else - { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url); - } - } - else + if (folder == null || !_userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions)) { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Download File {Url} {FolderId}", url, folderid); + _logger.Log(LogLevel.Error, this, LogFunction.Create, + "User Not Authorized To Download File {Url} {FolderId}", url, folderid); HttpContext.Response.StatusCode = 401; + return file; + } + + string folderPath = GetFolderPath(folder); + CreateDirectory(folderPath); + + string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); + // check for allowable file extensions + if (!Constants.UploadableFiles.Split(',') + .Contains(Path.GetExtension(filename).ToLower().Replace(".", ""))) + { + _logger.Log(LogLevel.Error, this, LogFunction.Create, + "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; + return file; + } + + if (!filename.IsPathOrFileValid()) + { + _logger.Log(LogLevel.Error, this, LogFunction.Create, + $"File Could Not Be Downloaded From Url Due To Its File Name Not Allowed {url}"); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; + return file; + } + + try + { + var client = new WebClient(); + string targetPath = Path.Combine(folderPath, filename); + // remove file if it already exists + if (System.IO.File.Exists(targetPath)) + { + System.IO.File.Delete(targetPath); + } + + client.DownloadFile(url, targetPath); + file = _files.AddFile(CreateFile(filename, folder.FolderId, targetPath)); + } + catch + { + _logger.Log(LogLevel.Error, this, LogFunction.Create, + "File Could Not Be Downloaded From Url {Url}", url); } return file; @@ -233,46 +246,56 @@ namespace Oqtane.Controllers [HttpPost("upload")] public async Task UploadFile(string folder, IFormFile file) { - if (file.Length > 0) + if (file.Length <= 0) { - string folderPath = ""; + return; + } - if (int.TryParse(folder, out int folderId)) + if (!file.FileName.IsPathOrFileValid()) + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; + return; + } + + string folderPath = ""; + + if (int.TryParse(folder, out int folderId)) + { + Folder virtualFolder = _folders.GetFolder(folderId); + if (virtualFolder != null && + _userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions)) { - Folder virtualFolder = _folders.GetFolder(folderId); - if (virtualFolder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions)) - { - folderPath = GetFolderPath(virtualFolder); - } + folderPath = GetFolderPath(virtualFolder); } - else + } + else + { + if (User.IsInRole(Constants.HostRole)) { - if (User.IsInRole(Constants.HostRole)) - { - folderPath = GetFolderPath(folder); - } + folderPath = GetFolderPath(folder); + } + } + + if (folderPath != "") + { + CreateDirectory(folderPath); + using (var stream = new FileStream(Path.Combine(folderPath, file.FileName), FileMode.Create)) + { + await file.CopyToAsync(stream); } - if (folderPath != "") + string upload = await MergeFile(folderPath, file.FileName); + if (upload != "" && folderId != -1) { - CreateDirectory(folderPath); - using (var stream = new FileStream(Path.Combine(folderPath, file.FileName), FileMode.Create)) - { - await file.CopyToAsync(stream); - } - - string upload = await MergeFile(folderPath, file.FileName); - if (upload != "" && folderId != -1) - { - _files.AddFile(CreateFile(upload, folderId, Path.Combine(folderPath, upload))); - } - } - else - { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Upload File {Folder} {File}", folder, file); - HttpContext.Response.StatusCode = 401; + _files.AddFile(CreateFile(upload, folderId, Path.Combine(folderPath, upload))); } } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Create, + "User Not Authorized To Upload File {Folder} {File}", folder, file); + HttpContext.Response.StatusCode = 401; + } } private async Task MergeFile(string folder, string filename) From 96f5668a3bf6f9278e4f0c174db010a990f06804 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sat, 16 May 2020 08:40:30 +0200 Subject: [PATCH 231/265] Setting service bug. --- Oqtane.Client/Services/SettingService.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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(); From 3a19ced2d1728aecf0c238531a81b3db68b0c66a Mon Sep 17 00:00:00 2001 From: Jim Spillane Date: Sat, 16 May 2020 10:36:16 -0400 Subject: [PATCH 232/265] Fix module pluralization Added 's' to the module creator sql script in the csproj to fix compilation error. --- .../Templates/External/Server/[Owner].[Module]s.Server.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Server.csproj b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Server.csproj index 0c93403b..f8cc9a95 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Server.csproj +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/[Owner].[Module]s.Server.csproj @@ -14,8 +14,8 @@ - - + + From 54d4447d23f59597ffe63067c6f1332be730af5d Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sat, 16 May 2020 12:00:15 -0400 Subject: [PATCH 233/265] Central management of resources ( ie. stylesheets and scripts ) --- .../Themes/BlazorTheme/Default.razor | 12 +++++++- .../Themes/OqtaneTheme/Default.razor | 19 +++++++++--- Oqtane.Client/Themes/ThemeBase.cs | 3 ++ Oqtane.Client/UI/Interop.cs | 16 ++++++++++ Oqtane.Client/UI/SiteRouter.razor | 29 +++++++++++++------ Oqtane.Client/UI/ThemeBuilder.razor | 22 ++++++++++++++ Oqtane.Server/wwwroot/js/interop.js | 9 ++++++ Oqtane.Shared/Enums/ResourceType.cs | 8 +++++ Oqtane.Shared/Interfaces/IThemeControl.cs | 6 +++- Oqtane.Shared/Models/Page.cs | 3 ++ Oqtane.Shared/Models/Resource.cs | 12 ++++++++ Oqtane.Shared/Models/Theme.cs | 4 ++- 12 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 Oqtane.Shared/Enums/ResourceType.cs create mode 100644 Oqtane.Shared/Models/Resource.cs diff --git a/Oqtane.Client/Themes/BlazorTheme/Default.razor b/Oqtane.Client/Themes/BlazorTheme/Default.razor index ecd3596e..4fa2a01d 100644 --- a/Oqtane.Client/Themes/BlazorTheme/Default.razor +++ b/Oqtane.Client/Themes/BlazorTheme/Default.razor @@ -28,9 +28,19 @@ @code { public override string Panes => "Content"; + public override List Resources + { + get + { + List resources = new List(); + resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = "Themes/" + GetType().Namespace + "/Theme.css", Integrity = "", CrossOrigin = "" }); + return resources; + } + } + protected override async Task OnParametersSetAsync() { - await IncludeCSS("Theme.css"); + //await IncludeCSS("Theme.css"); } } \ No newline at end of file diff --git a/Oqtane.Client/Themes/OqtaneTheme/Default.razor b/Oqtane.Client/Themes/OqtaneTheme/Default.razor index f92823e7..f6238a17 100644 --- a/Oqtane.Client/Themes/OqtaneTheme/Default.razor +++ b/Oqtane.Client/Themes/OqtaneTheme/Default.razor @@ -19,11 +19,22 @@ @code { public override string Panes => string.Empty; + public override List Resources + { + get + { + List resources = new List(); + resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css", Integrity = "sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM", CrossOrigin = "anonymous" }); + resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = "Themes/" + GetType().Namespace + "/Theme.css" }); + return resources; + } + } + protected override async Task OnParametersSetAsync() { - // go to https://www.bootstrapcdn.com/bootswatch/ and take your favorite theme - // - await LoadBootstrapTheme("https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css","sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM"); - await IncludeCSS("Theme.css"); + // go to https://www.bootstrapcdn.com/bootswatch/ and take your favorite theme + // + //await LoadBootstrapTheme("https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css","sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM"); + //await IncludeCSS("Theme.css"); } } diff --git a/Oqtane.Client/Themes/ThemeBase.cs b/Oqtane.Client/Themes/ThemeBase.cs index 83694766..3e3136f5 100644 --- a/Oqtane.Client/Themes/ThemeBase.cs +++ b/Oqtane.Client/Themes/ThemeBase.cs @@ -1,7 +1,9 @@ using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using Oqtane.Models; using Oqtane.Shared; using Oqtane.UI; +using System.Collections.Generic; using System.Threading.Tasks; namespace Oqtane.Themes @@ -14,6 +16,7 @@ namespace Oqtane.Themes [CascadingParameter] protected PageState PageState { get; set; } public virtual string Panes { get; set; } + public virtual List Resources { get; set; } public string ThemePath() { diff --git a/Oqtane.Client/UI/Interop.cs b/Oqtane.Client/UI/Interop.cs index 73aaf029..0ae86ac5 100644 --- a/Oqtane.Client/UI/Interop.cs +++ b/Oqtane.Client/UI/Interop.cs @@ -118,6 +118,22 @@ namespace Oqtane.UI } } + public Task RemoveElementsById(string prefix, string first, string last) + { + try + { + _jsRuntime.InvokeAsync( + "interop.removeElementsById", + prefix, first, last); + return Task.CompletedTask; + } + catch + { + return Task.CompletedTask; + } + } + + public ValueTask GetElementByName(string name) { try diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 488c43bf..37676dff 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -355,19 +355,30 @@ page.ThemeType = site.DefaultThemeType; page.LayoutType = site.DefaultLayoutType; } - Type type; + + page.Resources = new List(); + + Type themetype = Type.GetType(page.ThemeType); + var themeobject = Activator.CreateInstance(themetype); + if (themeobject != null) + { + page.Panes = (string)themetype.GetProperty("Panes").GetValue(themeobject, null); + var resources = (List)themetype.GetProperty("Resources").GetValue(themeobject, null); + if (resources != null) + { + page.Resources.AddRange(resources); + } + } if (!string.IsNullOrEmpty(page.LayoutType)) { - type = Type.GetType(page.LayoutType); + themetype = Type.GetType(page.LayoutType); + themeobject = Activator.CreateInstance(themetype); + if (themeobject != null) + { + page.Panes = (string)themetype.GetProperty("Panes").GetValue(themeobject, null); + } } - else - { - type = Type.GetType(page.ThemeType); - } - - var property = type.GetProperty("Panes"); - page.Panes = (string)property.GetValue(Activator.CreateInstance(type), null); } catch { diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index e2cf9e4a..9289fbfd 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -12,6 +12,8 @@ protected override async Task OnParametersSetAsync() { var interop = new Interop(JsRuntime); + + // set page title if (!string.IsNullOrEmpty(PageState.Page.Title)) { await interop.UpdateTitle(PageState.Page.Title); @@ -20,10 +22,30 @@ { await interop.UpdateTitle(PageState.Site.Name + " - " + PageState.Page.Name); } + + // manage page resources- they cannot be removed first and then added because the browser will "flash" and result in a poor user experience - they need to be updated + int index = 0; + foreach (Resource resource in PageState.Page.Resources) + { + index += 1; + switch (resource.ResourceType) + { + case ResourceType.Stylesheet: + await interop.IncludeLink("app-resource" + index.ToString("00"), "stylesheet", resource.Url, "text/css", resource.Integrity, resource.CrossOrigin); + break; + case ResourceType.Script: + break; + } + } + // remove any page resources references which are no longer required for this page + await interop.RemoveElementsById("app-resource", "app-resource" + (index + 1).ToString("00"), ""); + + // add favicon if (PageState.Site.FaviconFileId != null) { await interop.IncludeLink("fav-icon", "shortcut icon", Utilities.ContentUrl(PageState.Alias, PageState.Site.FaviconFileId.Value), "image/x-icon", "", ""); } + // add PWA support if (PageState.Site.PwaIsEnabled) { await InitializePwa(interop); diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index 5f41709c..36b6c4f3 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -160,6 +160,15 @@ window.interop = { } } }, + removeElementsById: function (prefix, first, last) { + var elements = document.querySelectorAll('[id^=' + prefix + ']'); + for (var i = elements.length - 1; i >= 0; i--) { + var element = elements[i]; + if (element.id.startsWith(prefix) && (first === '' || element.id >= first) && (last === '' || element.id <= last)) { + element.parentNode.removeChild(element); + } + } + }, getElementByName: function (name) { var elements = document.getElementsByName(name); if (elements.length) { diff --git a/Oqtane.Shared/Enums/ResourceType.cs b/Oqtane.Shared/Enums/ResourceType.cs new file mode 100644 index 00000000..b2196652 --- /dev/null +++ b/Oqtane.Shared/Enums/ResourceType.cs @@ -0,0 +1,8 @@ +namespace Oqtane.Shared +{ + public enum ResourceType + { + Stylesheet, + Script + } +} diff --git a/Oqtane.Shared/Interfaces/IThemeControl.cs b/Oqtane.Shared/Interfaces/IThemeControl.cs index e9e19e7c..0f6ad00a 100644 --- a/Oqtane.Shared/Interfaces/IThemeControl.cs +++ b/Oqtane.Shared/Interfaces/IThemeControl.cs @@ -1,7 +1,11 @@ -namespace Oqtane.Themes +using Oqtane.Models; +using System.Collections.Generic; + +namespace Oqtane.Themes { public interface IThemeControl { string Panes { get; } // identifies all panes in a theme ( delimited by ";" ) - assumed to be a layout if no panes specified + List Resources { get; } // identifies all resources in a theme } } diff --git a/Oqtane.Shared/Models/Page.cs b/Oqtane.Shared/Models/Page.cs index 2d3f3624..6512580f 100644 --- a/Oqtane.Shared/Models/Page.cs +++ b/Oqtane.Shared/Models/Page.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; namespace Oqtane.Models @@ -33,6 +34,8 @@ namespace Oqtane.Models [NotMapped] public string Panes { get; set; } [NotMapped] + public List Resources { get; set; } + [NotMapped] public string Permissions { get; set; } [NotMapped] public int Level { get; set; } diff --git a/Oqtane.Shared/Models/Resource.cs b/Oqtane.Shared/Models/Resource.cs new file mode 100644 index 00000000..81dc50fd --- /dev/null +++ b/Oqtane.Shared/Models/Resource.cs @@ -0,0 +1,12 @@ +using Oqtane.Shared; + +namespace Oqtane.Models +{ + public class Resource + { + public ResourceType ResourceType { get; set; } + public string Url { get; set; } + public string Integrity { get; set; } + public string CrossOrigin { get; set; } + } +} diff --git a/Oqtane.Shared/Models/Theme.cs b/Oqtane.Shared/Models/Theme.cs index a2dd83df..d5a29552 100644 --- a/Oqtane.Shared/Models/Theme.cs +++ b/Oqtane.Shared/Models/Theme.cs @@ -1,4 +1,6 @@ -namespace Oqtane.Models +using System.Collections.Generic; + +namespace Oqtane.Models { public class Theme { From c426302242f39a21f7f102422ea01fdbcb21e839 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sat, 16 May 2020 13:40:59 -0400 Subject: [PATCH 234/265] enhanced module creator to display location where module will be created --- .../Modules/Admin/ModuleCreator/Index.razor | 116 ++++++++++++------ .../{[Module] => [Owner].[Module]}/Edit.razor | 0 .../Index.razor | 0 .../ModuleInfo.cs | 0 .../Services/I[Module]Service.cs | 0 .../Services/[Module]Service.cs | 0 .../Settings.razor | 0 .../Controllers/[Module]Controller.cs | 0 .../Manager/[Module]Manager.cs | 0 .../Repository/I[Module]Repository.cs | 0 .../Repository/[Module]Context.cs | 0 .../Repository/[Module]Repository.cs | 0 .../Scripts/[Owner].[Module]s.1.0.0.sql | 0 .../Scripts/[Owner].[Module]s.Uninstall.sql | 0 .../Models/[Module].cs | 0 .../Controllers/ModuleDefinitionController.cs | 4 +- 16 files changed, 82 insertions(+), 38 deletions(-) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/{[Module] => [Owner].[Module]}/Edit.razor (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/{[Module] => [Owner].[Module]}/Index.razor (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/{[Module] => [Owner].[Module]}/ModuleInfo.cs (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/{[Module] => [Owner].[Module]}/Services/I[Module]Service.cs (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/{[Module] => [Owner].[Module]}/Services/[Module]Service.cs (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/{[Module] => [Owner].[Module]}/Settings.razor (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/{[Module] => [Owner].[Module]}/Controllers/[Module]Controller.cs (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/{[Module] => [Owner].[Module]}/Manager/[Module]Manager.cs (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/{[Module] => [Owner].[Module]}/Repository/I[Module]Repository.cs (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/{[Module] => [Owner].[Module]}/Repository/[Module]Context.cs (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/{[Module] => [Owner].[Module]}/Repository/[Module]Repository.cs (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/{[Module] => [Owner].[Module]}/Scripts/[Owner].[Module]s.1.0.0.sql (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/{[Module] => [Owner].[Module]}/Scripts/[Owner].[Module]s.Uninstall.sql (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Shared/Modules/{[Module] => [Owner].[Module]}/Models/[Module].cs (100%) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor index 8b4cb758..198ecb9b 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; + } + else + { + _location = string.Join("\\", path, 0, path.Length - 2) + "\\" + _owner + "." + _module; + } + } + } + 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/Internal/Oqtane.Client/Modules/[Module]/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Edit.razor similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Edit.razor diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Index.razor similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Index.razor diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/ModuleInfo.cs similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/ModuleInfo.cs diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Services/I[Module]Service.cs similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/I[Module]Service.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Services/I[Module]Service.cs diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Services/[Module]Service.cs similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Services/[Module]Service.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Services/[Module]Service.cs diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Settings.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Settings.razor similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Settings.razor rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Settings.razor diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Controllers/[Module]Controller.cs similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Controllers/[Module]Controller.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Controllers/[Module]Controller.cs diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Manager/[Module]Manager.cs similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Manager/[Module]Manager.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Manager/[Module]Manager.cs diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/I[Module]Repository.cs similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/I[Module]Repository.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/I[Module]Repository.cs diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/[Module]Context.cs similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/[Module]Context.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/[Module]Context.cs diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/[Module]Repository.cs similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Repository/[Module]Repository.cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/[Module]Repository.cs diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module]s.1.0.0.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Scripts/[Owner].[Module]s.1.0.0.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module]s.1.0.0.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Scripts/[Owner].[Module]s.1.0.0.sql diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module]s.Uninstall.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Scripts/[Owner].[Module]s.Uninstall.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module]s.Uninstall.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Scripts/[Owner].[Module]s.Uninstall.sql diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Shared/Modules/[Module]/Models/[Module].cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Shared/Modules/[Owner].[Module]/Models/[Module].cs similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Shared/Modules/[Module]/Models/[Module].cs rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Shared/Modules/[Owner].[Module]/Models/[Module].cs diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index 039d6782..29dd91d2 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -231,8 +231,8 @@ namespace Oqtane.Controllers { // add embedded resources to project List resources = new List(); - resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.1.0.0.sql")); - resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Uninstall.sql")); + resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.1.0.0.sql")); + resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Uninstall.sql")); EmbedResourceFiles(Utilities.PathCombine(rootPath, "Oqtane.Server", "Oqtane.Server.csproj"), resources); } From f8ab8867504c5f7eef077c2ee3f8f17dd6c56204 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sat, 16 May 2020 22:11:58 -0400 Subject: [PATCH 235/265] Fixed issue with loading resources --- Oqtane.Client/Themes/BlazorTheme/Default.razor | 6 ------ Oqtane.Client/Themes/OqtaneTheme/Default.razor | 10 +--------- Oqtane.Server/wwwroot/js/interop.js | 12 ++++++++---- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/Oqtane.Client/Themes/BlazorTheme/Default.razor b/Oqtane.Client/Themes/BlazorTheme/Default.razor index 4fa2a01d..2ed0d1fb 100644 --- a/Oqtane.Client/Themes/BlazorTheme/Default.razor +++ b/Oqtane.Client/Themes/BlazorTheme/Default.razor @@ -37,10 +37,4 @@ return resources; } } - - protected override async Task OnParametersSetAsync() - { - //await IncludeCSS("Theme.css"); - } - } \ No newline at end of file diff --git a/Oqtane.Client/Themes/OqtaneTheme/Default.razor b/Oqtane.Client/Themes/OqtaneTheme/Default.razor index f6238a17..67d53081 100644 --- a/Oqtane.Client/Themes/OqtaneTheme/Default.razor +++ b/Oqtane.Client/Themes/OqtaneTheme/Default.razor @@ -25,16 +25,8 @@ { List resources = new List(); resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css", Integrity = "sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM", CrossOrigin = "anonymous" }); - resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = "Themes/" + GetType().Namespace + "/Theme.css" }); + resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = "Themes/" + GetType().Namespace + "/Theme.css", Integrity = "", CrossOrigin = "" }); return resources; } } - - protected override async Task OnParametersSetAsync() - { - // go to https://www.bootstrapcdn.com/bootswatch/ and take your favorite theme - // - //await LoadBootstrapTheme("https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css","sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM"); - //await IncludeCSS("Theme.css"); - } } diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index 36b6c4f3..3f42cd11 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -62,10 +62,10 @@ window.interop = { link.id = id; } link.rel = rel; - link.href = url; if (type !== "") { link.type = type; } + link.href = url; if (integrity !== "") { link.integrity = integrity; } @@ -78,9 +78,6 @@ window.interop = { if (link.rel !== rel) { link.setAttribute('rel', rel); } - if (link.href !== url) { - link.setAttribute('href', url); - } if (type !== "") { if (link.type !== type) { link.setAttribute('type', type); @@ -88,6 +85,11 @@ window.interop = { } else { link.removeAttribute('type'); } + if (link.href !== url) { + link.removeAttribute('integrity'); + link.removeAttribute('crossorigin'); + link.setAttribute('href', url); + } if (integrity !== "") { if (link.integrity !== integrity) { link.setAttribute('integrity', integrity); @@ -136,6 +138,8 @@ window.interop = { else { if (src !== "") { if (script.src !== src) { + script.removeAttribute('integrity'); + script.removeAttribute('crossorigin'); script.src = src; } if (integrity !== "") { From 9b74262c763a2b274ae6eb99954102575c0b264a Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 18 May 2020 09:47:37 -0400 Subject: [PATCH 236/265] Added support for module resource management --- Oqtane.Client/Modules/HtmlText/Edit.razor | 24 ++- Oqtane.Client/Modules/HtmlText/Index.razor | 10 ++ Oqtane.Client/Modules/ModuleBase.cs | 4 + .../Themes/BlazorTheme/Default.razor | 2 +- .../Themes/Controls/ControlPanel.razor | 4 +- .../Themes/Controls/ModuleActionsBase.cs | 2 +- .../Themes/OqtaneTheme/Default.razor | 2 +- Oqtane.Client/UI/SiteRouter.razor | 137 ++++++++++-------- Oqtane.Client/UI/ThemeBuilder.razor | 14 +- Oqtane.Server/Pages/_Host.cshtml | 5 - .../Oqtane.Modules.HtmlText/Module.css | 1 + Oqtane.Server/wwwroot/js/interop.js | 48 ------ Oqtane.Server/wwwroot/js/quill-interop.js | 50 +++++++ Oqtane.Server/wwwroot/js/site.js | 1 - Oqtane.Shared/Interfaces/IModuleControl.cs | 5 +- Oqtane.Shared/Models/Page.cs | 2 +- 16 files changed, 183 insertions(+), 128 deletions(-) create mode 100644 Oqtane.Server/wwwroot/Modules/Oqtane.Modules.HtmlText/Module.css create mode 100644 Oqtane.Server/wwwroot/js/quill-interop.js delete mode 100644 Oqtane.Server/wwwroot/js/site.js diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index c9c94bc7..442aee36 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -20,6 +20,26 @@ } @code { + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; + + public override string Title => "Edit Html/Text"; + + public override List Resources + { + get + { + List resources = new List(); + resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css", Integrity = "", CrossOrigin = "" }); + // the following resources should be declared in the RichTextEditor component however the framework currently only supports resource management for modules and themes + resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.bubble.css", Integrity = "", CrossOrigin = "" }); + resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.snow.css", Integrity = "", CrossOrigin = "" }); + resources.Add(new Resource { ResourceType = ResourceType.Script, Url = "js/quill1.3.6.min.js", Integrity = "", CrossOrigin = "" }); + resources.Add(new Resource { ResourceType = ResourceType.Script, Url = "js/quill-blot-formatter.min.js", Integrity = "", CrossOrigin = "" }); + resources.Add(new Resource { ResourceType = ResourceType.Script, Url = "js/quill-interop.js", Integrity = "", CrossOrigin = "" }); + return resources; + } + } + private RichTextEditor RichTextEditorHtml; private string _content = null; private string _createdby; @@ -27,10 +47,6 @@ private string _modifiedby; private DateTime _modifiedon; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; - - public override string Title => "Edit Html/Text"; - protected override async Task OnInitializedAsync() { try diff --git a/Oqtane.Client/Modules/HtmlText/Index.razor b/Oqtane.Client/Modules/HtmlText/Index.razor index 6821c425..5d18e498 100644 --- a/Oqtane.Client/Modules/HtmlText/Index.razor +++ b/Oqtane.Client/Modules/HtmlText/Index.razor @@ -11,6 +11,16 @@ } @code { + public override List Resources + { + get + { + List resources = new List(); + resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css", Integrity = "", CrossOrigin = "" }); + return resources; + } + } + private string content = ""; protected override async Task OnParametersSetAsync() diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 188da769..9da4a090 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -6,6 +6,7 @@ using Oqtane.Services; using System; using Oqtane.Enums; using Oqtane.UI; +using System.Collections.Generic; namespace Oqtane.Modules { @@ -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/Themes/BlazorTheme/Default.razor b/Oqtane.Client/Themes/BlazorTheme/Default.razor index 2ed0d1fb..a51a130c 100644 --- a/Oqtane.Client/Themes/BlazorTheme/Default.razor +++ b/Oqtane.Client/Themes/BlazorTheme/Default.razor @@ -33,7 +33,7 @@ get { List resources = new List(); - resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = "Themes/" + GetType().Namespace + "/Theme.css", Integrity = "", CrossOrigin = "" }); + resources.Add(new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css", Integrity = "", CrossOrigin = "" }); return resources; } } diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index ec463657..e80ca085 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -149,7 +149,7 @@ ','',"
    "].join(""),e.BubbleTooltip=_,e.default=m},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),s=function t(e,n,r){null===e&&(e=Function.prototype);var o=Object.getOwnPropertyDescriptor(e,n);if(void 0===o){var i=Object.getPrototypeOf(e);return null===i?void 0:t(i,n,r)}if("value"in o)return o.value;var l=o.get;if(void 0!==l)return l.call(r)},u=function(){function t(t,e){for(var n=0;n','','',''].join(""),e.default=w}]).default}); -//# sourceMappingURL=quill.min.js.map \ No newline at end of file +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Quill=e():t.Quill=e()}("undefined"!=typeof self?self:this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=45)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(17),o=n(18),i=n(19),l=n(48),a=n(49),s=n(50),u=n(51),c=n(52),f=n(11),h=n(29),p=n(30),d=n(28),y=n(1),v={Scope:y.Scope,create:y.create,find:y.find,query:y.query,register:y.register,Container:r.default,Format:o.default,Leaf:i.default,Embed:u.default,Scroll:l.default,Block:s.default,Inline:a.default,Text:c.default,Attributor:{Attribute:f.default,Class:h.default,Style:p.default,Store:d.default}};e.default=v},function(t,e,n){"use strict";function r(t,e){var n=i(t);if(null==n)throw new s("Unable to create "+t+" blot");var r=n;return new r(t instanceof Node||t.nodeType===Node.TEXT_NODE?t:r.create(e),e)}function o(t,n){return void 0===n&&(n=!1),null==t?null:null!=t[e.DATA_KEY]?t[e.DATA_KEY].blot:n?o(t.parentNode,n):null}function i(t,e){void 0===e&&(e=p.ANY);var n;if("string"==typeof t)n=h[t]||u[t];else if(t instanceof Text||t.nodeType===Node.TEXT_NODE)n=h.text;else if("number"==typeof t)t&p.LEVEL&p.BLOCK?n=h.block:t&p.LEVEL&p.INLINE&&(n=h.inline);else if(t instanceof HTMLElement){var r=(t.getAttribute("class")||"").split(/\s+/);for(var o in r)if(n=c[r[o]])break;n=n||f[t.tagName]}return null==n?null:e&p.LEVEL&n.scope&&e&p.TYPE&n.scope?n:null}function l(){for(var t=[],e=0;e1)return t.map(function(t){return l(t)});var n=t[0];if("string"!=typeof n.blotName&&"string"!=typeof n.attrName)throw new s("Invalid definition");if("abstract"===n.blotName)throw new s("Cannot register abstract class");if(h[n.blotName||n.attrName]=n,"string"==typeof n.keyName)u[n.keyName]=n;else if(null!=n.className&&(c[n.className]=n),null!=n.tagName){Array.isArray(n.tagName)?n.tagName=n.tagName.map(function(t){return t.toUpperCase()}):n.tagName=n.tagName.toUpperCase();var r=Array.isArray(n.tagName)?n.tagName:[n.tagName];r.forEach(function(t){null!=f[t]&&null!=n.className||(f[t]=n)})}return n}var a=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var s=function(t){function e(e){var n=this;return e="[Parchment] "+e,n=t.call(this,e)||this,n.message=e,n.name=n.constructor.name,n}return a(e,t),e}(Error);e.ParchmentError=s;var u={},c={},f={},h={};e.DATA_KEY="__blot";var p;!function(t){t[t.TYPE=3]="TYPE",t[t.LEVEL=12]="LEVEL",t[t.ATTRIBUTE=13]="ATTRIBUTE",t[t.BLOT=14]="BLOT",t[t.INLINE=7]="INLINE",t[t.BLOCK=11]="BLOCK",t[t.BLOCK_BLOT=10]="BLOCK_BLOT",t[t.INLINE_BLOT=6]="INLINE_BLOT",t[t.BLOCK_ATTRIBUTE=9]="BLOCK_ATTRIBUTE",t[t.INLINE_ATTRIBUTE=5]="INLINE_ATTRIBUTE",t[t.ANY=15]="ANY"}(p=e.Scope||(e.Scope={})),e.create=r,e.find=o,e.query=i,e.register=l},function(t,e){"use strict";var n=Object.prototype.hasOwnProperty,r=Object.prototype.toString,o=function(t){return"function"==typeof Array.isArray?Array.isArray(t):"[object Array]"===r.call(t)},i=function(t){if(!t||"[object Object]"!==r.call(t))return!1;var e=n.call(t,"constructor"),o=t.constructor&&t.constructor.prototype&&n.call(t.constructor.prototype,"isPrototypeOf");if(t.constructor&&!e&&!o)return!1;var i;for(i in t);return void 0===i||n.call(t,i)};t.exports=function t(){var e,n,r,l,a,s,u=arguments[0],c=1,f=arguments.length,h=!1;for("boolean"==typeof u&&(h=u,u=arguments[1]||{},c=2),(null==u||"object"!=typeof u&&"function"!=typeof u)&&(u={});c1&&void 0!==arguments[1]?arguments[1]:{};return null==t?e:("function"==typeof t.formats&&(e=(0,f.default)(e,t.formats())),null==t.parent||"scroll"==t.parent.blotName||t.parent.statics.scope!==t.statics.scope?e:a(t.parent,e))}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.BlockEmbed=e.bubbleFormats=void 0;var s=function(){function t(t,e){for(var n=0;n0&&(t1&&void 0!==arguments[1]&&arguments[1];if(n&&(0===t||t>=this.length()-1)){var r=this.clone();return 0===t?(this.parent.insertBefore(r,this),this):(this.parent.insertBefore(r,this.next),r)}var o=u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"split",this).call(this,t,n);return this.cache={},o}}]),e}(y.default.Block);x.blotName="block",x.tagName="P",x.defaultChild="break",x.allowedChildren=[m.default,y.default.Embed,O.default],e.bubbleFormats=a,e.BlockEmbed=w,e.default=x},function(t,e,n){var r=n(54),o=n(12),i=n(2),l=n(20),a=String.fromCharCode(0),s=function(t){Array.isArray(t)?this.ops=t:null!=t&&Array.isArray(t.ops)?this.ops=t.ops:this.ops=[]};s.prototype.insert=function(t,e){var n={};return 0===t.length?this:(n.insert=t,null!=e&&"object"==typeof e&&Object.keys(e).length>0&&(n.attributes=e),this.push(n))},s.prototype.delete=function(t){return t<=0?this:this.push({delete:t})},s.prototype.retain=function(t,e){if(t<=0)return this;var n={retain:t};return null!=e&&"object"==typeof e&&Object.keys(e).length>0&&(n.attributes=e),this.push(n)},s.prototype.push=function(t){var e=this.ops.length,n=this.ops[e-1];if(t=i(!0,{},t),"object"==typeof n){if("number"==typeof t.delete&&"number"==typeof n.delete)return this.ops[e-1]={delete:n.delete+t.delete},this;if("number"==typeof n.delete&&null!=t.insert&&(e-=1,"object"!=typeof(n=this.ops[e-1])))return this.ops.unshift(t),this;if(o(t.attributes,n.attributes)){if("string"==typeof t.insert&&"string"==typeof n.insert)return this.ops[e-1]={insert:n.insert+t.insert},"object"==typeof t.attributes&&(this.ops[e-1].attributes=t.attributes),this;if("number"==typeof t.retain&&"number"==typeof n.retain)return this.ops[e-1]={retain:n.retain+t.retain},"object"==typeof t.attributes&&(this.ops[e-1].attributes=t.attributes),this}}return e===this.ops.length?this.ops.push(t):this.ops.splice(e,0,t),this},s.prototype.chop=function(){var t=this.ops[this.ops.length-1];return t&&t.retain&&!t.attributes&&this.ops.pop(),this},s.prototype.filter=function(t){return this.ops.filter(t)},s.prototype.forEach=function(t){this.ops.forEach(t)},s.prototype.map=function(t){return this.ops.map(t)},s.prototype.partition=function(t){var e=[],n=[];return this.forEach(function(r){(t(r)?e:n).push(r)}),[e,n]},s.prototype.reduce=function(t,e){return this.ops.reduce(t,e)},s.prototype.changeLength=function(){return this.reduce(function(t,e){return e.insert?t+l.length(e):e.delete?t-e.delete:t},0)},s.prototype.length=function(){return this.reduce(function(t,e){return t+l.length(e)},0)},s.prototype.slice=function(t,e){t=t||0,"number"!=typeof e&&(e=1/0);for(var n=[],r=l.iterator(this.ops),o=0;o0&&(e.push(t.ops[0]),e.ops=e.ops.concat(t.ops.slice(1))),e},s.prototype.diff=function(t,e){if(this.ops===t.ops)return new s;var n=[this,t].map(function(e){return e.map(function(n){if(null!=n.insert)return"string"==typeof n.insert?n.insert:a;var r=e===t?"on":"with";throw new Error("diff() called "+r+" non-document")}).join("")}),i=new s,u=r(n[0],n[1],e),c=l.iterator(this.ops),f=l.iterator(t.ops);return u.forEach(function(t){for(var e=t[1].length;e>0;){var n=0;switch(t[0]){case r.INSERT:n=Math.min(f.peekLength(),e),i.push(f.next(n));break;case r.DELETE:n=Math.min(e,c.peekLength()),c.next(n),i.delete(n);break;case r.EQUAL:n=Math.min(c.peekLength(),f.peekLength(),e);var a=c.next(n),s=f.next(n);o(a.insert,s.insert)?i.retain(n,l.attributes.diff(a.attributes,s.attributes)):i.push(s).delete(n)}e-=n}}),i.chop()},s.prototype.eachLine=function(t,e){e=e||"\n";for(var n=l.iterator(this.ops),r=new s,o=0;n.hasNext();){if("insert"!==n.peekType())return;var i=n.peek(),a=l.length(i)-n.peekLength(),u="string"==typeof i.insert?i.insert.indexOf(e,a)-a:-1;if(u<0)r.push(n.next());else if(u>0)r.push(n.next(u));else{if(!1===t(r,n.next(1).attributes||{},o))return;o+=1,r=new s}}r.length()>0&&t(r,{},o)},s.prototype.transform=function(t,e){if(e=!!e,"number"==typeof t)return this.transformPosition(t,e);for(var n=l.iterator(this.ops),r=l.iterator(t.ops),o=new s;n.hasNext()||r.hasNext();)if("insert"!==n.peekType()||!e&&"insert"===r.peekType())if("insert"===r.peekType())o.push(r.next());else{var i=Math.min(n.peekLength(),r.peekLength()),a=n.next(i),u=r.next(i);if(a.delete)continue;u.delete?o.push(u):o.retain(i,l.attributes.transform(a.attributes,u.attributes,e))}else o.retain(l.length(n.next()));return o.chop()},s.prototype.transformPosition=function(t,e){e=!!e;for(var n=l.iterator(this.ops),r=0;n.hasNext()&&r<=t;){var o=n.peekLength(),i=n.peekType();n.next(),"delete"!==i?("insert"===i&&(r0){var n=this.parent.isolate(this.offset(),this.length());this.moveChildren(n),n.wrap(this)}}}],[{key:"compare",value:function(t,n){var r=e.order.indexOf(t),o=e.order.indexOf(n);return r>=0||o>=0?r-o:t===n?0:t0){var a,s=[g.default.events.TEXT_CHANGE,l,i,e];if((a=this.emitter).emit.apply(a,[g.default.events.EDITOR_CHANGE].concat(s)),e!==g.default.sources.SILENT){var c;(c=this.emitter).emit.apply(c,s)}}return l}function s(t,e,n,r,o){var i={};return"number"==typeof t.index&&"number"==typeof t.length?"number"!=typeof e?(o=r,r=n,n=e,e=t.length,t=t.index):(e=t.length,t=t.index):"number"!=typeof e&&(o=r,r=n,n=e,e=0),"object"===(void 0===n?"undefined":c(n))?(i=n,o=r):"string"==typeof n&&(null!=r?i[n]=r:o=n),o=o||g.default.sources.API,[t,e,i,o]}function u(t,e,n,r){if(null==t)return null;var o=void 0,i=void 0;if(e instanceof d.default){var l=[t.index,t.index+t.length].map(function(t){return e.transformPosition(t,r!==g.default.sources.USER)}),a=f(l,2);o=a[0],i=a[1]}else{var s=[t.index,t.index+t.length].map(function(t){return t=0?t+n:Math.max(e,t+n)}),u=f(s,2);o=u[0],i=u[1]}return new x.Range(o,i-o)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.overload=e.expandConfig=void 0;var c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},f=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),h=function(){function t(t,e){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{};if(i(this,t),this.options=l(e,r),this.container=this.options.container,null==this.container)return P.error("Invalid Quill container",e);this.options.debug&&t.debug(this.options.debug);var o=this.container.innerHTML.trim();this.container.classList.add("ql-container"),this.container.innerHTML="",this.container.__quill=this,this.root=this.addContainer("ql-editor"),this.root.classList.add("ql-blank"),this.root.setAttribute("data-gramm",!1),this.scrollingContainer=this.options.scrollingContainer||this.root,this.emitter=new g.default,this.scroll=w.default.create(this.root,{emitter:this.emitter,whitelist:this.options.formats}),this.editor=new v.default(this.scroll),this.selection=new k.default(this.scroll,this.emitter),this.theme=new this.options.theme(this,this.options),this.keyboard=this.theme.addModule("keyboard"),this.clipboard=this.theme.addModule("clipboard"),this.history=this.theme.addModule("history"),this.theme.init(),this.emitter.on(g.default.events.EDITOR_CHANGE,function(t){t===g.default.events.TEXT_CHANGE&&n.root.classList.toggle("ql-blank",n.editor.isBlank())}),this.emitter.on(g.default.events.SCROLL_UPDATE,function(t,e){var r=n.selection.lastRange,o=r&&0===r.length?r.index:void 0;a.call(n,function(){return n.editor.update(null,e,o)},t)});var s=this.clipboard.convert("
    "+o+"


    ");this.setContents(s),this.history.clear(),this.options.placeholder&&this.root.setAttribute("data-placeholder",this.options.placeholder),this.options.readOnly&&this.disable()}return h(t,null,[{key:"debug",value:function(t){!0===t&&(t="log"),A.default.level(t)}},{key:"find",value:function(t){return t.__quill||w.default.find(t)}},{key:"import",value:function(t){return null==this.imports[t]&&P.error("Cannot import "+t+". Are you sure it was registered?"),this.imports[t]}},{key:"register",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if("string"!=typeof t){var o=t.attrName||t.blotName;"string"==typeof o?this.register("formats/"+o,t,e):Object.keys(t).forEach(function(r){n.register(r,t[r],e)})}else null==this.imports[t]||r||P.warn("Overwriting "+t+" with",e),this.imports[t]=e,(t.startsWith("blots/")||t.startsWith("formats/"))&&"abstract"!==e.blotName?w.default.register(e):t.startsWith("modules")&&"function"==typeof e.register&&e.register()}}]),h(t,[{key:"addContainer",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if("string"==typeof t){var n=t;t=document.createElement("div"),t.classList.add(n)}return this.container.insertBefore(t,e),t}},{key:"blur",value:function(){this.selection.setRange(null)}},{key:"deleteText",value:function(t,e,n){var r=this,o=s(t,e,n),i=f(o,4);return t=i[0],e=i[1],n=i[3],a.call(this,function(){return r.editor.deleteText(t,e)},n,t,-1*e)}},{key:"disable",value:function(){this.enable(!1)}},{key:"enable",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.scroll.enable(t),this.container.classList.toggle("ql-disabled",!t)}},{key:"focus",value:function(){var t=this.scrollingContainer.scrollTop;this.selection.focus(),this.scrollingContainer.scrollTop=t,this.scrollIntoView()}},{key:"format",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:g.default.sources.API;return a.call(this,function(){var r=n.getSelection(!0),i=new d.default;if(null==r)return i;if(w.default.query(t,w.default.Scope.BLOCK))i=n.editor.formatLine(r.index,r.length,o({},t,e));else{if(0===r.length)return n.selection.format(t,e),i;i=n.editor.formatText(r.index,r.length,o({},t,e))}return n.setSelection(r,g.default.sources.SILENT),i},r)}},{key:"formatLine",value:function(t,e,n,r,o){var i=this,l=void 0,u=s(t,e,n,r,o),c=f(u,4);return t=c[0],e=c[1],l=c[2],o=c[3],a.call(this,function(){return i.editor.formatLine(t,e,l)},o,t,0)}},{key:"formatText",value:function(t,e,n,r,o){var i=this,l=void 0,u=s(t,e,n,r,o),c=f(u,4);return t=c[0],e=c[1],l=c[2],o=c[3],a.call(this,function(){return i.editor.formatText(t,e,l)},o,t,0)}},{key:"getBounds",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=void 0;n="number"==typeof t?this.selection.getBounds(t,e):this.selection.getBounds(t.index,t.length);var r=this.container.getBoundingClientRect();return{bottom:n.bottom-r.top,height:n.height,left:n.left-r.left,right:n.right-r.left,top:n.top-r.top,width:n.width}}},{key:"getContents",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.getLength()-t,n=s(t,e),r=f(n,2);return t=r[0],e=r[1],this.editor.getContents(t,e)}},{key:"getFormat",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.getSelection(!0),e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return"number"==typeof t?this.editor.getFormat(t,e):this.editor.getFormat(t.index,t.length)}},{key:"getIndex",value:function(t){return t.offset(this.scroll)}},{key:"getLength",value:function(){return this.scroll.length()}},{key:"getLeaf",value:function(t){return this.scroll.leaf(t)}},{key:"getLine",value:function(t){return this.scroll.line(t)}},{key:"getLines",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Number.MAX_VALUE;return"number"!=typeof t?this.scroll.lines(t.index,t.length):this.scroll.lines(t,e)}},{key:"getModule",value:function(t){return this.theme.modules[t]}},{key:"getSelection",value:function(){return arguments.length>0&&void 0!==arguments[0]&&arguments[0]&&this.focus(),this.update(),this.selection.getRange()[0]}},{key:"getText",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.getLength()-t,n=s(t,e),r=f(n,2);return t=r[0],e=r[1],this.editor.getText(t,e)}},{key:"hasFocus",value:function(){return this.selection.hasFocus()}},{key:"insertEmbed",value:function(e,n,r){var o=this,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:t.sources.API;return a.call(this,function(){return o.editor.insertEmbed(e,n,r)},i,e)}},{key:"insertText",value:function(t,e,n,r,o){var i=this,l=void 0,u=s(t,0,n,r,o),c=f(u,4);return t=c[0],l=c[2],o=c[3],a.call(this,function(){return i.editor.insertText(t,e,l)},o,t,e.length)}},{key:"isEnabled",value:function(){return!this.container.classList.contains("ql-disabled")}},{key:"off",value:function(){return this.emitter.off.apply(this.emitter,arguments)}},{key:"on",value:function(){return this.emitter.on.apply(this.emitter,arguments)}},{key:"once",value:function(){return this.emitter.once.apply(this.emitter,arguments)}},{key:"pasteHTML",value:function(t,e,n){this.clipboard.dangerouslyPasteHTML(t,e,n)}},{key:"removeFormat",value:function(t,e,n){var r=this,o=s(t,e,n),i=f(o,4);return t=i[0],e=i[1],n=i[3],a.call(this,function(){return r.editor.removeFormat(t,e)},n,t)}},{key:"scrollIntoView",value:function(){this.selection.scrollIntoView(this.scrollingContainer)}},{key:"setContents",value:function(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:g.default.sources.API;return a.call(this,function(){t=new d.default(t);var n=e.getLength(),r=e.editor.deleteText(0,n),o=e.editor.applyDelta(t),i=o.ops[o.ops.length-1];return null!=i&&"string"==typeof i.insert&&"\n"===i.insert[i.insert.length-1]&&(e.editor.deleteText(e.getLength()-1,1),o.delete(1)),r.compose(o)},n)}},{key:"setSelection",value:function(e,n,r){if(null==e)this.selection.setRange(null,n||t.sources.API);else{var o=s(e,n,r),i=f(o,4);e=i[0],n=i[1],r=i[3],this.selection.setRange(new x.Range(e,n),r),r!==g.default.sources.SILENT&&this.selection.scrollIntoView(this.scrollingContainer)}}},{key:"setText",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:g.default.sources.API,n=(new d.default).insert(t);return this.setContents(n,e)}},{key:"update",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:g.default.sources.USER,e=this.scroll.update(t);return this.selection.update(t),e}},{key:"updateContents",value:function(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:g.default.sources.API;return a.call(this,function(){return t=new d.default(t),e.editor.applyDelta(t,n)},n,!0)}}]),t}();S.DEFAULTS={bounds:null,formats:null,modules:{},placeholder:"",readOnly:!1,scrollingContainer:null,strict:!0,theme:"default"},S.events=g.default.events,S.sources=g.default.sources,S.version="1.3.6",S.imports={delta:d.default,parchment:w.default,"core/module":_.default,"core/theme":T.default},e.expandConfig=l,e.overload=s,e.default=S},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var o=function t(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};r(this,t),this.quill=e,this.options=n};o.DEFAULTS={},e.default=o},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=n(0),a=function(t){return t&&t.__esModule?t:{default:t}}(l),s=function(t){function e(){return r(this,e),o(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return i(e,t),e}(a.default.Text);e.default=s},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var n=0;n1?e-1:0),r=1;r1?n-1:0),o=1;o-1:this.whitelist.indexOf(e)>-1))},t.prototype.remove=function(t){t.removeAttribute(this.keyName)},t.prototype.value=function(t){var e=t.getAttribute(this.keyName);return this.canAdd(t,e)&&e?e:""},t}();e.default=o},function(t,e,n){function r(t){return null===t||void 0===t}function o(t){return!(!t||"object"!=typeof t||"number"!=typeof t.length)&&("function"==typeof t.copy&&"function"==typeof t.slice&&!(t.length>0&&"number"!=typeof t[0]))}function i(t,e,n){var i,c;if(r(t)||r(e))return!1;if(t.prototype!==e.prototype)return!1;if(s(t))return!!s(e)&&(t=l.call(t),e=l.call(e),u(t,e,n));if(o(t)){if(!o(e))return!1;if(t.length!==e.length)return!1;for(i=0;i=0;i--)if(f[i]!=h[i])return!1;for(i=f.length-1;i>=0;i--)if(c=f[i],!u(t[c],e[c],n))return!1;return typeof t==typeof e}var l=Array.prototype.slice,a=n(55),s=n(56),u=t.exports=function(t,e,n){return n||(n={}),t===e||(t instanceof Date&&e instanceof Date?t.getTime()===e.getTime():!t||!e||"object"!=typeof t&&"object"!=typeof e?n.strict?t===e:t==e:i(t,e,n))}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.Code=void 0;var a=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),s=function(){function t(t,e){for(var n=0;n=t+n)){var l=this.newlineIndex(t,!0)+1,a=i-l+1,s=this.isolate(l,a),u=s.next;s.format(r,o),u instanceof e&&u.formatAt(0,t-l+n-a,r,o)}}}},{key:"insertAt",value:function(t,e,n){if(null==n){var r=this.descendant(m.default,t),o=a(r,2),i=o[0],l=o[1];i.insertAt(l,e)}}},{key:"length",value:function(){var t=this.domNode.textContent.length;return this.domNode.textContent.endsWith("\n")?t:t+1}},{key:"newlineIndex",value:function(t){if(arguments.length>1&&void 0!==arguments[1]&&arguments[1])return this.domNode.textContent.slice(0,t).lastIndexOf("\n");var e=this.domNode.textContent.slice(t).indexOf("\n");return e>-1?t+e:-1}},{key:"optimize",value:function(t){this.domNode.textContent.endsWith("\n")||this.appendChild(p.default.create("text","\n")),u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"optimize",this).call(this,t);var n=this.next;null!=n&&n.prev===this&&n.statics.blotName===this.statics.blotName&&this.statics.formats(this.domNode)===n.statics.formats(n.domNode)&&(n.optimize(t),n.moveChildren(this),n.remove())}},{key:"replace",value:function(t){u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"replace",this).call(this,t),[].slice.call(this.domNode.querySelectorAll("*")).forEach(function(t){var e=p.default.find(t);null==e?t.parentNode.removeChild(t):e instanceof p.default.Embed?e.remove():e.unwrap()})}}],[{key:"create",value:function(t){var n=u(e.__proto__||Object.getPrototypeOf(e),"create",this).call(this,t);return n.setAttribute("spellcheck",!1),n}},{key:"formats",value:function(){return!0}}]),e}(y.default);O.blotName="code-block",O.tagName="PRE",O.TAB=" ",e.Code=_,e.default=O},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;n-1}Object.defineProperty(e,"__esModule",{value:!0}),e.sanitize=e.default=void 0;var a=function(){function t(t,e){for(var n=0;n1&&void 0!==arguments[1]&&arguments[1],n=this.container.querySelector(".ql-selected");if(t!==n&&(null!=n&&n.classList.remove("ql-selected"),null!=t&&(t.classList.add("ql-selected"),this.select.selectedIndex=[].indexOf.call(t.parentNode.children,t),t.hasAttribute("data-value")?this.label.setAttribute("data-value",t.getAttribute("data-value")):this.label.removeAttribute("data-value"),t.hasAttribute("data-label")?this.label.setAttribute("data-label",t.getAttribute("data-label")):this.label.removeAttribute("data-label"),e))){if("function"==typeof Event)this.select.dispatchEvent(new Event("change"));else if("object"===("undefined"==typeof Event?"undefined":l(Event))){var r=document.createEvent("Event");r.initEvent("change",!0,!0),this.select.dispatchEvent(r)}this.close()}}},{key:"update",value:function(){var t=void 0;if(this.select.selectedIndex>-1){var e=this.container.querySelector(".ql-picker-options").children[this.select.selectedIndex];t=this.select.options[this.select.selectedIndex],this.selectItem(e)}else this.selectItem(null);var n=null!=t&&t!==this.select.querySelector("option[selected]");this.label.classList.toggle("ql-active",n)}}]),t}();e.default=p},function(t,e,n){"use strict";function r(t){var e=a.find(t);if(null==e)try{e=a.create(t)}catch(n){e=a.create(a.Scope.INLINE),[].slice.call(t.childNodes).forEach(function(t){e.domNode.appendChild(t)}),t.parentNode&&t.parentNode.replaceChild(e.domNode,t),e.attach()}return e}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(47),l=n(27),a=n(1),s=function(t){function e(e){var n=t.call(this,e)||this;return n.build(),n}return o(e,t),e.prototype.appendChild=function(t){this.insertBefore(t)},e.prototype.attach=function(){t.prototype.attach.call(this),this.children.forEach(function(t){t.attach()})},e.prototype.build=function(){var t=this;this.children=new i.default,[].slice.call(this.domNode.childNodes).reverse().forEach(function(e){try{var n=r(e);t.insertBefore(n,t.children.head||void 0)}catch(t){if(t instanceof a.ParchmentError)return;throw t}})},e.prototype.deleteAt=function(t,e){if(0===t&&e===this.length())return this.remove();this.children.forEachAt(t,e,function(t,e,n){t.deleteAt(e,n)})},e.prototype.descendant=function(t,n){var r=this.children.find(n),o=r[0],i=r[1];return null==t.blotName&&t(o)||null!=t.blotName&&o instanceof t?[o,i]:o instanceof e?o.descendant(t,i):[null,-1]},e.prototype.descendants=function(t,n,r){void 0===n&&(n=0),void 0===r&&(r=Number.MAX_VALUE);var o=[],i=r;return this.children.forEachAt(n,r,function(n,r,l){(null==t.blotName&&t(n)||null!=t.blotName&&n instanceof t)&&o.push(n),n instanceof e&&(o=o.concat(n.descendants(t,r,i))),i-=l}),o},e.prototype.detach=function(){this.children.forEach(function(t){t.detach()}),t.prototype.detach.call(this)},e.prototype.formatAt=function(t,e,n,r){this.children.forEachAt(t,e,function(t,e,o){t.formatAt(e,o,n,r)})},e.prototype.insertAt=function(t,e,n){var r=this.children.find(t),o=r[0],i=r[1];if(o)o.insertAt(i,e,n);else{var l=null==n?a.create("text",e):a.create(e,n);this.appendChild(l)}},e.prototype.insertBefore=function(t,e){if(null!=this.statics.allowedChildren&&!this.statics.allowedChildren.some(function(e){return t instanceof e}))throw new a.ParchmentError("Cannot insert "+t.statics.blotName+" into "+this.statics.blotName);t.insertInto(this,e)},e.prototype.length=function(){return this.children.reduce(function(t,e){return t+e.length()},0)},e.prototype.moveChildren=function(t,e){this.children.forEach(function(n){t.insertBefore(n,e)})},e.prototype.optimize=function(e){if(t.prototype.optimize.call(this,e),0===this.children.length)if(null!=this.statics.defaultChild){var n=a.create(this.statics.defaultChild);this.appendChild(n),n.optimize(e)}else this.remove()},e.prototype.path=function(t,n){void 0===n&&(n=!1);var r=this.children.find(t,n),o=r[0],i=r[1],l=[[this,t]];return o instanceof e?l.concat(o.path(i,n)):(null!=o&&l.push([o,i]),l)},e.prototype.removeChild=function(t){this.children.remove(t)},e.prototype.replace=function(n){n instanceof e&&n.moveChildren(this),t.prototype.replace.call(this,n)},e.prototype.split=function(t,e){if(void 0===e&&(e=!1),!e){if(0===t)return this;if(t===this.length())return this.next}var n=this.clone();return this.parent.insertBefore(n,this.next),this.children.forEachAt(t,this.length(),function(t,r,o){t=t.split(r,e),n.appendChild(t)}),n},e.prototype.unwrap=function(){this.moveChildren(this.parent,this.next),this.remove()},e.prototype.update=function(t,e){var n=this,o=[],i=[];t.forEach(function(t){t.target===n.domNode&&"childList"===t.type&&(o.push.apply(o,t.addedNodes),i.push.apply(i,t.removedNodes))}),i.forEach(function(t){if(!(null!=t.parentNode&&"IFRAME"!==t.tagName&&document.body.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_CONTAINED_BY)){var e=a.find(t);null!=e&&(null!=e.domNode.parentNode&&e.domNode.parentNode!==n.domNode||e.detach())}}),o.filter(function(t){return t.parentNode==n.domNode}).sort(function(t,e){return t===e?0:t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING?1:-1}).forEach(function(t){var e=null;null!=t.nextSibling&&(e=a.find(t.nextSibling));var o=r(t);o.next==e&&null!=o.next||(null!=o.parent&&o.parent.removeChild(n),n.insertBefore(o,e||void 0))})},e}(l.default);e.default=s},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(11),i=n(28),l=n(17),a=n(1),s=function(t){function e(e){var n=t.call(this,e)||this;return n.attributes=new i.default(n.domNode),n}return r(e,t),e.formats=function(t){return"string"==typeof this.tagName||(Array.isArray(this.tagName)?t.tagName.toLowerCase():void 0)},e.prototype.format=function(t,e){var n=a.query(t);n instanceof o.default?this.attributes.attribute(n,e):e&&(null==n||t===this.statics.blotName&&this.formats()[t]===e||this.replaceWith(t,e))},e.prototype.formats=function(){var t=this.attributes.values(),e=this.statics.formats(this.domNode);return null!=e&&(t[this.statics.blotName]=e),t},e.prototype.replaceWith=function(e,n){var r=t.prototype.replaceWith.call(this,e,n);return this.attributes.copy(r),r},e.prototype.update=function(e,n){var r=this;t.prototype.update.call(this,e,n),e.some(function(t){return t.target===r.domNode&&"attributes"===t.type})&&this.attributes.build()},e.prototype.wrap=function(n,r){var o=t.prototype.wrap.call(this,n,r);return o instanceof e&&o.statics.scope===this.statics.scope&&this.attributes.move(o),o},e}(l.default);e.default=s},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(27),i=n(1),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r(e,t),e.value=function(t){return!0},e.prototype.index=function(t,e){return this.domNode===t||this.domNode.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_CONTAINED_BY?Math.min(e,1):-1},e.prototype.position=function(t,e){var n=[].indexOf.call(this.parent.domNode.childNodes,this.domNode);return t>0&&(n+=1),[this.parent.domNode,n]},e.prototype.value=function(){return t={},t[this.statics.blotName]=this.statics.value(this.domNode)||!0,t;var t},e.scope=i.Scope.INLINE_BLOT,e}(o.default);e.default=l},function(t,e,n){function r(t){this.ops=t,this.index=0,this.offset=0}var o=n(12),i=n(2),l={attributes:{compose:function(t,e,n){"object"!=typeof t&&(t={}),"object"!=typeof e&&(e={});var r=i(!0,{},e);n||(r=Object.keys(r).reduce(function(t,e){return null!=r[e]&&(t[e]=r[e]),t},{}));for(var o in t)void 0!==t[o]&&void 0===e[o]&&(r[o]=t[o]);return Object.keys(r).length>0?r:void 0},diff:function(t,e){"object"!=typeof t&&(t={}),"object"!=typeof e&&(e={});var n=Object.keys(t).concat(Object.keys(e)).reduce(function(n,r){return o(t[r],e[r])||(n[r]=void 0===e[r]?null:e[r]),n},{});return Object.keys(n).length>0?n:void 0},transform:function(t,e,n){if("object"!=typeof t)return e;if("object"==typeof e){if(!n)return e;var r=Object.keys(e).reduce(function(n,r){return void 0===t[r]&&(n[r]=e[r]),n},{});return Object.keys(r).length>0?r:void 0}}},iterator:function(t){return new r(t)},length:function(t){return"number"==typeof t.delete?t.delete:"number"==typeof t.retain?t.retain:"string"==typeof t.insert?t.insert.length:1}};r.prototype.hasNext=function(){return this.peekLength()<1/0},r.prototype.next=function(t){t||(t=1/0);var e=this.ops[this.index];if(e){var n=this.offset,r=l.length(e);if(t>=r-n?(t=r-n,this.index+=1,this.offset=0):this.offset+=t,"number"==typeof e.delete)return{delete:t};var o={};return e.attributes&&(o.attributes=e.attributes),"number"==typeof e.retain?o.retain=t:"string"==typeof e.insert?o.insert=e.insert.substr(n,t):o.insert=e.insert,o}return{retain:1/0}},r.prototype.peek=function(){return this.ops[this.index]},r.prototype.peekLength=function(){return this.ops[this.index]?l.length(this.ops[this.index])-this.offset:1/0},r.prototype.peekType=function(){return this.ops[this.index]?"number"==typeof this.ops[this.index].delete?"delete":"number"==typeof this.ops[this.index].retain?"retain":"insert":"retain"},t.exports=l},function(t,e){var n=function(){"use strict";function t(t,e){return null!=e&&t instanceof e}function e(n,r,o,i,c){function f(n,o){if(null===n)return null;if(0===o)return n;var y,v;if("object"!=typeof n)return n;if(t(n,a))y=new a;else if(t(n,s))y=new s;else if(t(n,u))y=new u(function(t,e){n.then(function(e){t(f(e,o-1))},function(t){e(f(t,o-1))})});else if(e.__isArray(n))y=[];else if(e.__isRegExp(n))y=new RegExp(n.source,l(n)),n.lastIndex&&(y.lastIndex=n.lastIndex);else if(e.__isDate(n))y=new Date(n.getTime());else{if(d&&Buffer.isBuffer(n))return y=new Buffer(n.length),n.copy(y),y;t(n,Error)?y=Object.create(n):void 0===i?(v=Object.getPrototypeOf(n),y=Object.create(v)):(y=Object.create(i),v=i)}if(r){var b=h.indexOf(n);if(-1!=b)return p[b];h.push(n),p.push(y)}t(n,a)&&n.forEach(function(t,e){var n=f(e,o-1),r=f(t,o-1);y.set(n,r)}),t(n,s)&&n.forEach(function(t){var e=f(t,o-1);y.add(e)});for(var g in n){var m;v&&(m=Object.getOwnPropertyDescriptor(v,g)),m&&null==m.set||(y[g]=f(n[g],o-1))}if(Object.getOwnPropertySymbols)for(var _=Object.getOwnPropertySymbols(n),g=0;g<_.length;g++){var O=_[g],w=Object.getOwnPropertyDescriptor(n,O);(!w||w.enumerable||c)&&(y[O]=f(n[O],o-1),w.enumerable||Object.defineProperty(y,O,{enumerable:!1}))}if(c)for(var x=Object.getOwnPropertyNames(n),g=0;g1&&void 0!==arguments[1]?arguments[1]:0;i(this,t),this.index=e,this.length=n},O=function(){function t(e,n){var r=this;i(this,t),this.emitter=n,this.scroll=e,this.composing=!1,this.mouseDown=!1,this.root=this.scroll.domNode,this.cursor=c.default.create("cursor",this),this.lastRange=this.savedRange=new _(0,0),this.handleComposition(),this.handleDragging(),this.emitter.listenDOM("selectionchange",document,function(){r.mouseDown||setTimeout(r.update.bind(r,v.default.sources.USER),1)}),this.emitter.on(v.default.events.EDITOR_CHANGE,function(t,e){t===v.default.events.TEXT_CHANGE&&e.length()>0&&r.update(v.default.sources.SILENT)}),this.emitter.on(v.default.events.SCROLL_BEFORE_UPDATE,function(){if(r.hasFocus()){var t=r.getNativeRange();null!=t&&t.start.node!==r.cursor.textNode&&r.emitter.once(v.default.events.SCROLL_UPDATE,function(){try{r.setNativeRange(t.start.node,t.start.offset,t.end.node,t.end.offset)}catch(t){}})}}),this.emitter.on(v.default.events.SCROLL_OPTIMIZE,function(t,e){if(e.range){var n=e.range,o=n.startNode,i=n.startOffset,l=n.endNode,a=n.endOffset;r.setNativeRange(o,i,l,a)}}),this.update(v.default.sources.SILENT)}return s(t,[{key:"handleComposition",value:function(){var t=this;this.root.addEventListener("compositionstart",function(){t.composing=!0}),this.root.addEventListener("compositionend",function(){if(t.composing=!1,t.cursor.parent){var e=t.cursor.restore();if(!e)return;setTimeout(function(){t.setNativeRange(e.startNode,e.startOffset,e.endNode,e.endOffset)},1)}})}},{key:"handleDragging",value:function(){var t=this;this.emitter.listenDOM("mousedown",document.body,function(){t.mouseDown=!0}),this.emitter.listenDOM("mouseup",document.body,function(){t.mouseDown=!1,t.update(v.default.sources.USER)})}},{key:"focus",value:function(){this.hasFocus()||(this.root.focus(),this.setRange(this.savedRange))}},{key:"format",value:function(t,e){if(null==this.scroll.whitelist||this.scroll.whitelist[t]){this.scroll.update();var n=this.getNativeRange();if(null!=n&&n.native.collapsed&&!c.default.query(t,c.default.Scope.BLOCK)){if(n.start.node!==this.cursor.textNode){var r=c.default.find(n.start.node,!1);if(null==r)return;if(r instanceof c.default.Leaf){var o=r.split(n.start.offset);r.parent.insertBefore(this.cursor,o)}else r.insertBefore(this.cursor,n.start.node);this.cursor.attach()}this.cursor.format(t,e),this.scroll.optimize(),this.setNativeRange(this.cursor.textNode,this.cursor.textNode.data.length),this.update()}}}},{key:"getBounds",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=this.scroll.length();t=Math.min(t,n-1),e=Math.min(t+e,n-1)-t;var r=void 0,o=this.scroll.leaf(t),i=a(o,2),l=i[0],s=i[1];if(null==l)return null;var u=l.position(s,!0),c=a(u,2);r=c[0],s=c[1];var f=document.createRange();if(e>0){f.setStart(r,s);var h=this.scroll.leaf(t+e),p=a(h,2);if(l=p[0],s=p[1],null==l)return null;var d=l.position(s,!0),y=a(d,2);return r=y[0],s=y[1],f.setEnd(r,s),f.getBoundingClientRect()}var v="left",b=void 0;return r instanceof Text?(s0&&(v="right")),{bottom:b.top+b.height,height:b.height,left:b[v],right:b[v],top:b.top,width:0}}},{key:"getNativeRange",value:function(){var t=document.getSelection();if(null==t||t.rangeCount<=0)return null;var e=t.getRangeAt(0);if(null==e)return null;var n=this.normalizeNative(e);return m.info("getNativeRange",n),n}},{key:"getRange",value:function(){var t=this.getNativeRange();return null==t?[null,null]:[this.normalizedToRange(t),t]}},{key:"hasFocus",value:function(){return document.activeElement===this.root}},{key:"normalizedToRange",value:function(t){var e=this,n=[[t.start.node,t.start.offset]];t.native.collapsed||n.push([t.end.node,t.end.offset]);var r=n.map(function(t){var n=a(t,2),r=n[0],o=n[1],i=c.default.find(r,!0),l=i.offset(e.scroll);return 0===o?l:i instanceof c.default.Container?l+i.length():l+i.index(r,o)}),i=Math.min(Math.max.apply(Math,o(r)),this.scroll.length()-1),l=Math.min.apply(Math,[i].concat(o(r)));return new _(l,i-l)}},{key:"normalizeNative",value:function(t){if(!l(this.root,t.startContainer)||!t.collapsed&&!l(this.root,t.endContainer))return null;var e={start:{node:t.startContainer,offset:t.startOffset},end:{node:t.endContainer,offset:t.endOffset},native:t};return[e.start,e.end].forEach(function(t){for(var e=t.node,n=t.offset;!(e instanceof Text)&&e.childNodes.length>0;)if(e.childNodes.length>n)e=e.childNodes[n],n=0;else{if(e.childNodes.length!==n)break;e=e.lastChild,n=e instanceof Text?e.data.length:e.childNodes.length+1}t.node=e,t.offset=n}),e}},{key:"rangeToNative",value:function(t){var e=this,n=t.collapsed?[t.index]:[t.index,t.index+t.length],r=[],o=this.scroll.length();return n.forEach(function(t,n){t=Math.min(o-1,t);var i=void 0,l=e.scroll.leaf(t),s=a(l,2),u=s[0],c=s[1],f=u.position(c,0!==n),h=a(f,2);i=h[0],c=h[1],r.push(i,c)}),r.length<2&&(r=r.concat(r)),r}},{key:"scrollIntoView",value:function(t){var e=this.lastRange;if(null!=e){var n=this.getBounds(e.index,e.length);if(null!=n){var r=this.scroll.length()-1,o=this.scroll.line(Math.min(e.index,r)),i=a(o,1),l=i[0],s=l;if(e.length>0){var u=this.scroll.line(Math.min(e.index+e.length,r));s=a(u,1)[0]}if(null!=l&&null!=s){var c=t.getBoundingClientRect();n.topc.bottom&&(t.scrollTop+=n.bottom-c.bottom)}}}}},{key:"setNativeRange",value:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:t,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:e,o=arguments.length>4&&void 0!==arguments[4]&&arguments[4];if(m.info("setNativeRange",t,e,n,r),null==t||null!=this.root.parentNode&&null!=t.parentNode&&null!=n.parentNode){var i=document.getSelection();if(null!=i)if(null!=t){this.hasFocus()||this.root.focus();var l=(this.getNativeRange()||{}).native;if(null==l||o||t!==l.startContainer||e!==l.startOffset||n!==l.endContainer||r!==l.endOffset){"BR"==t.tagName&&(e=[].indexOf.call(t.parentNode.childNodes,t),t=t.parentNode),"BR"==n.tagName&&(r=[].indexOf.call(n.parentNode.childNodes,n),n=n.parentNode);var a=document.createRange();a.setStart(t,e),a.setEnd(n,r),i.removeAllRanges(),i.addRange(a)}}else i.removeAllRanges(),this.root.blur(),document.body.focus()}}},{key:"setRange",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:v.default.sources.API;if("string"==typeof e&&(n=e,e=!1),m.info("setRange",t),null!=t){var r=this.rangeToNative(t);this.setNativeRange.apply(this,o(r).concat([e]))}else this.setNativeRange(null);this.update(n)}},{key:"update",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:v.default.sources.USER,e=this.lastRange,n=this.getRange(),r=a(n,2),o=r[0],i=r[1];if(this.lastRange=o,null!=this.lastRange&&(this.savedRange=this.lastRange),!(0,d.default)(e,this.lastRange)){var l;!this.composing&&null!=i&&i.native.collapsed&&i.start.node!==this.cursor.textNode&&this.cursor.restore();var s=[v.default.events.SELECTION_CHANGE,(0,h.default)(this.lastRange),(0,h.default)(e),t];if((l=this.emitter).emit.apply(l,[v.default.events.EDITOR_CHANGE].concat(s)),t!==v.default.sources.SILENT){var u;(u=this.emitter).emit.apply(u,s)}}}}]),t}();e.Range=_,e.default=O},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=n(0),s=r(a),u=n(3),c=r(u),f=function(t){function e(){return o(this,e),i(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return l(e,t),e}(s.default.Container);f.allowedChildren=[c.default,u.BlockEmbed,f],e.default=f},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.ColorStyle=e.ColorClass=e.ColorAttributor=void 0;var l=function(){function t(t,e){for(var n=0;n1){var u=o.formats(),c=this.quill.getFormat(t.index-1,1);i=A.default.attributes.diff(u,c)||{}}}var f=/[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test(e.prefix)?2:1;this.quill.deleteText(t.index-f,f,S.default.sources.USER),Object.keys(i).length>0&&this.quill.formatLine(t.index-f,f,i,S.default.sources.USER),this.quill.focus()}}function c(t,e){var n=/^[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(e.suffix)?2:1;if(!(t.index>=this.quill.getLength()-n)){var r={},o=0,i=this.quill.getLine(t.index),l=b(i,1),a=l[0];if(e.offset>=a.length()-1){var s=this.quill.getLine(t.index+1),u=b(s,1),c=u[0];if(c){var f=a.formats(),h=this.quill.getFormat(t.index,1);r=A.default.attributes.diff(f,h)||{},o=c.length()}}this.quill.deleteText(t.index,n,S.default.sources.USER),Object.keys(r).length>0&&this.quill.formatLine(t.index+o-1,n,r,S.default.sources.USER)}}function f(t){var e=this.quill.getLines(t),n={};if(e.length>1){var r=e[0].formats(),o=e[e.length-1].formats();n=A.default.attributes.diff(o,r)||{}}this.quill.deleteText(t,S.default.sources.USER),Object.keys(n).length>0&&this.quill.formatLine(t.index,1,n,S.default.sources.USER),this.quill.setSelection(t.index,S.default.sources.SILENT),this.quill.focus()}function h(t,e){var n=this;t.length>0&&this.quill.scroll.deleteAt(t.index,t.length);var r=Object.keys(e.format).reduce(function(t,n){return T.default.query(n,T.default.Scope.BLOCK)&&!Array.isArray(e.format[n])&&(t[n]=e.format[n]),t},{});this.quill.insertText(t.index,"\n",r,S.default.sources.USER),this.quill.setSelection(t.index+1,S.default.sources.SILENT),this.quill.focus(),Object.keys(e.format).forEach(function(t){null==r[t]&&(Array.isArray(e.format[t])||"link"!==t&&n.quill.format(t,e.format[t],S.default.sources.USER))})}function p(t){return{key:D.keys.TAB,shiftKey:!t,format:{"code-block":!0},handler:function(e){var n=T.default.query("code-block"),r=e.index,o=e.length,i=this.quill.scroll.descendant(n,r),l=b(i,2),a=l[0],s=l[1];if(null!=a){var u=this.quill.getIndex(a),c=a.newlineIndex(s,!0)+1,f=a.newlineIndex(u+s+o),h=a.domNode.textContent.slice(c,f).split("\n");s=0,h.forEach(function(e,i){t?(a.insertAt(c+s,n.TAB),s+=n.TAB.length,0===i?r+=n.TAB.length:o+=n.TAB.length):e.startsWith(n.TAB)&&(a.deleteAt(c+s,n.TAB.length),s-=n.TAB.length,0===i?r-=n.TAB.length:o-=n.TAB.length),s+=e.length+1}),this.quill.update(S.default.sources.USER),this.quill.setSelection(r,o,S.default.sources.SILENT)}}}}function d(t){return{key:t[0].toUpperCase(),shortKey:!0,handler:function(e,n){this.quill.format(t,!n.format[t],S.default.sources.USER)}}}function y(t){if("string"==typeof t||"number"==typeof t)return y({key:t});if("object"===(void 0===t?"undefined":v(t))&&(t=(0,_.default)(t,!1)),"string"==typeof t.key)if(null!=D.keys[t.key.toUpperCase()])t.key=D.keys[t.key.toUpperCase()];else{if(1!==t.key.length)return null;t.key=t.key.toUpperCase().charCodeAt(0)}return t.shortKey&&(t[B]=t.shortKey,delete t.shortKey),t}Object.defineProperty(e,"__esModule",{value:!0}),e.SHORTKEY=e.default=void 0;var v="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},b=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),g=function(){function t(t,e){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=y(t);if(null==r||null==r.key)return I.warn("Attempted to add invalid keyboard binding",r);"function"==typeof e&&(e={handler:e}),"function"==typeof n&&(n={handler:n}),r=(0,k.default)(r,e,n),this.bindings[r.key]=this.bindings[r.key]||[],this.bindings[r.key].push(r)}},{key:"listen",value:function(){var t=this;this.quill.root.addEventListener("keydown",function(n){if(!n.defaultPrevented){var r=n.which||n.keyCode,o=(t.bindings[r]||[]).filter(function(t){return e.match(n,t)});if(0!==o.length){var i=t.quill.getSelection();if(null!=i&&t.quill.hasFocus()){var l=t.quill.getLine(i.index),a=b(l,2),s=a[0],u=a[1],c=t.quill.getLeaf(i.index),f=b(c,2),h=f[0],p=f[1],d=0===i.length?[h,p]:t.quill.getLeaf(i.index+i.length),y=b(d,2),g=y[0],m=y[1],_=h instanceof T.default.Text?h.value().slice(0,p):"",O=g instanceof T.default.Text?g.value().slice(m):"",x={collapsed:0===i.length,empty:0===i.length&&s.length()<=1,format:t.quill.getFormat(i),offset:u,prefix:_,suffix:O};o.some(function(e){if(null!=e.collapsed&&e.collapsed!==x.collapsed)return!1;if(null!=e.empty&&e.empty!==x.empty)return!1;if(null!=e.offset&&e.offset!==x.offset)return!1;if(Array.isArray(e.format)){if(e.format.every(function(t){return null==x.format[t]}))return!1}else if("object"===v(e.format)&&!Object.keys(e.format).every(function(t){return!0===e.format[t]?null!=x.format[t]:!1===e.format[t]?null==x.format[t]:(0,w.default)(e.format[t],x.format[t])}))return!1;return!(null!=e.prefix&&!e.prefix.test(x.prefix))&&(!(null!=e.suffix&&!e.suffix.test(x.suffix))&&!0!==e.handler.call(t,i,x))})&&n.preventDefault()}}}})}}]),e}(R.default);D.keys={BACKSPACE:8,TAB:9,ENTER:13,ESCAPE:27,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46},D.DEFAULTS={bindings:{bold:d("bold"),italic:d("italic"),underline:d("underline"),indent:{key:D.keys.TAB,format:["blockquote","indent","list"],handler:function(t,e){if(e.collapsed&&0!==e.offset)return!0;this.quill.format("indent","+1",S.default.sources.USER)}},outdent:{key:D.keys.TAB,shiftKey:!0,format:["blockquote","indent","list"],handler:function(t,e){if(e.collapsed&&0!==e.offset)return!0;this.quill.format("indent","-1",S.default.sources.USER)}},"outdent backspace":{key:D.keys.BACKSPACE,collapsed:!0,shiftKey:null,metaKey:null,ctrlKey:null,altKey:null,format:["indent","list"],offset:0,handler:function(t,e){null!=e.format.indent?this.quill.format("indent","-1",S.default.sources.USER):null!=e.format.list&&this.quill.format("list",!1,S.default.sources.USER)}},"indent code-block":p(!0),"outdent code-block":p(!1),"remove tab":{key:D.keys.TAB,shiftKey:!0,collapsed:!0,prefix:/\t$/,handler:function(t){this.quill.deleteText(t.index-1,1,S.default.sources.USER)}},tab:{key:D.keys.TAB,handler:function(t){this.quill.history.cutoff();var e=(new N.default).retain(t.index).delete(t.length).insert("\t");this.quill.updateContents(e,S.default.sources.USER),this.quill.history.cutoff(),this.quill.setSelection(t.index+1,S.default.sources.SILENT)}},"list empty enter":{key:D.keys.ENTER,collapsed:!0,format:["list"],empty:!0,handler:function(t,e){this.quill.format("list",!1,S.default.sources.USER),e.format.indent&&this.quill.format("indent",!1,S.default.sources.USER)}},"checklist enter":{key:D.keys.ENTER,collapsed:!0,format:{list:"checked"},handler:function(t){var e=this.quill.getLine(t.index),n=b(e,2),r=n[0],o=n[1],i=(0,k.default)({},r.formats(),{list:"checked"}),l=(new N.default).retain(t.index).insert("\n",i).retain(r.length()-o-1).retain(1,{list:"unchecked"});this.quill.updateContents(l,S.default.sources.USER),this.quill.setSelection(t.index+1,S.default.sources.SILENT),this.quill.scrollIntoView()}},"header enter":{key:D.keys.ENTER,collapsed:!0,format:["header"],suffix:/^$/,handler:function(t,e){var n=this.quill.getLine(t.index),r=b(n,2),o=r[0],i=r[1],l=(new N.default).retain(t.index).insert("\n",e.format).retain(o.length()-i-1).retain(1,{header:null});this.quill.updateContents(l,S.default.sources.USER),this.quill.setSelection(t.index+1,S.default.sources.SILENT),this.quill.scrollIntoView()}},"list autofill":{key:" ",collapsed:!0,format:{list:!1},prefix:/^\s*?(\d+\.|-|\*|\[ ?\]|\[x\])$/,handler:function(t,e){var n=e.prefix.length,r=this.quill.getLine(t.index),o=b(r,2),i=o[0],l=o[1];if(l>n)return!0;var a=void 0;switch(e.prefix.trim()){case"[]":case"[ ]":a="unchecked";break;case"[x]":a="checked";break;case"-":case"*":a="bullet";break;default:a="ordered"}this.quill.insertText(t.index," ",S.default.sources.USER),this.quill.history.cutoff();var s=(new N.default).retain(t.index-l).delete(n+1).retain(i.length()-2-l).retain(1,{list:a});this.quill.updateContents(s,S.default.sources.USER),this.quill.history.cutoff(),this.quill.setSelection(t.index-n,S.default.sources.SILENT)}},"code exit":{key:D.keys.ENTER,collapsed:!0,format:["code-block"],prefix:/\n\n$/,suffix:/^\s+$/,handler:function(t){var e=this.quill.getLine(t.index),n=b(e,2),r=n[0],o=n[1],i=(new N.default).retain(t.index+r.length()-o-2).retain(1,{"code-block":null}).delete(1);this.quill.updateContents(i,S.default.sources.USER)}},"embed left":s(D.keys.LEFT,!1),"embed left shift":s(D.keys.LEFT,!0),"embed right":s(D.keys.RIGHT,!1),"embed right shift":s(D.keys.RIGHT,!0)}},e.default=D,e.SHORTKEY=B},function(t,e,n){"use strict";t.exports={align:{"":n(75),center:n(76),right:n(77),justify:n(78)},background:n(79),blockquote:n(80),bold:n(81),clean:n(82),code:n(40),"code-block":n(40),color:n(83),direction:{"":n(84),rtl:n(85)},float:{center:n(86),full:n(87),left:n(88),right:n(89)},formula:n(90),header:{1:n(91),2:n(92)},italic:n(93),image:n(94),indent:{"+1":n(95),"-1":n(96)},link:n(97),list:{ordered:n(98),bullet:n(99),check:n(100)},script:{sub:n(101),super:n(102)},strike:n(103),underline:n(104),video:n(105)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1),o=function(){function t(t){this.domNode=t,this.domNode[r.DATA_KEY]={blot:this}}return Object.defineProperty(t.prototype,"statics",{get:function(){return this.constructor},enumerable:!0,configurable:!0}),t.create=function(t){if(null==this.tagName)throw new r.ParchmentError("Blot definition missing tagName");var e;return Array.isArray(this.tagName)?("string"==typeof t&&(t=t.toUpperCase(),parseInt(t).toString()===t&&(t=parseInt(t))),e="number"==typeof t?document.createElement(this.tagName[t-1]):this.tagName.indexOf(t)>-1?document.createElement(t):document.createElement(this.tagName[0])):e=document.createElement(this.tagName),this.className&&e.classList.add(this.className),e},t.prototype.attach=function(){null!=this.parent&&(this.scroll=this.parent.scroll)},t.prototype.clone=function(){var t=this.domNode.cloneNode(!1);return r.create(t)},t.prototype.detach=function(){null!=this.parent&&this.parent.removeChild(this),delete this.domNode[r.DATA_KEY]},t.prototype.deleteAt=function(t,e){this.isolate(t,e).remove()},t.prototype.formatAt=function(t,e,n,o){var i=this.isolate(t,e);if(null!=r.query(n,r.Scope.BLOT)&&o)i.wrap(n,o);else if(null!=r.query(n,r.Scope.ATTRIBUTE)){var l=r.create(this.statics.scope);i.wrap(l),l.format(n,o)}},t.prototype.insertAt=function(t,e,n){var o=null==n?r.create("text",e):r.create(e,n),i=this.split(t);this.parent.insertBefore(o,i)},t.prototype.insertInto=function(t,e){void 0===e&&(e=null),null!=this.parent&&this.parent.children.remove(this);var n=null;t.children.insertBefore(this,e),null!=e&&(n=e.domNode),this.domNode.parentNode==t.domNode&&this.domNode.nextSibling==n||t.domNode.insertBefore(this.domNode,n),this.parent=t,this.attach()},t.prototype.isolate=function(t,e){var n=this.split(t);return n.split(e),n},t.prototype.length=function(){return 1},t.prototype.offset=function(t){return void 0===t&&(t=this.parent),null==this.parent||this==t?0:this.parent.children.offset(this)+this.parent.offset(t)},t.prototype.optimize=function(t){null!=this.domNode[r.DATA_KEY]&&delete this.domNode[r.DATA_KEY].mutations},t.prototype.remove=function(){null!=this.domNode.parentNode&&this.domNode.parentNode.removeChild(this.domNode),this.detach()},t.prototype.replace=function(t){null!=t.parent&&(t.parent.insertBefore(this,t.next),t.remove())},t.prototype.replaceWith=function(t,e){var n="string"==typeof t?r.create(t,e):t;return n.replace(this),n},t.prototype.split=function(t,e){return 0===t?this:this.next},t.prototype.update=function(t,e){},t.prototype.wrap=function(t,e){var n="string"==typeof t?r.create(t,e):t;return null!=this.parent&&this.parent.insertBefore(n,this.next),n.appendChild(this),n},t.blotName="abstract",t}();e.default=o},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(11),o=n(29),i=n(30),l=n(1),a=function(){function t(t){this.attributes={},this.domNode=t,this.build()}return t.prototype.attribute=function(t,e){e?t.add(this.domNode,e)&&(null!=t.value(this.domNode)?this.attributes[t.attrName]=t:delete this.attributes[t.attrName]):(t.remove(this.domNode),delete this.attributes[t.attrName])},t.prototype.build=function(){var t=this;this.attributes={};var e=r.default.keys(this.domNode),n=o.default.keys(this.domNode),a=i.default.keys(this.domNode);e.concat(n).concat(a).forEach(function(e){var n=l.query(e,l.Scope.ATTRIBUTE);n instanceof r.default&&(t.attributes[n.attrName]=n)})},t.prototype.copy=function(t){var e=this;Object.keys(this.attributes).forEach(function(n){var r=e.attributes[n].value(e.domNode);t.format(n,r)})},t.prototype.move=function(t){var e=this;this.copy(t),Object.keys(this.attributes).forEach(function(t){e.attributes[t].remove(e.domNode)}),this.attributes={}},t.prototype.values=function(){var t=this;return Object.keys(this.attributes).reduce(function(e,n){return e[n]=t.attributes[n].value(t.domNode),e},{})},t}();e.default=a},function(t,e,n){"use strict";function r(t,e){return(t.getAttribute("class")||"").split(/\s+/).filter(function(t){return 0===t.indexOf(e+"-")})}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(11),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return o(e,t),e.keys=function(t){return(t.getAttribute("class")||"").split(/\s+/).map(function(t){return t.split("-").slice(0,-1).join("-")})},e.prototype.add=function(t,e){return!!this.canAdd(t,e)&&(this.remove(t),t.classList.add(this.keyName+"-"+e),!0)},e.prototype.remove=function(t){r(t,this.keyName).forEach(function(e){t.classList.remove(e)}),0===t.classList.length&&t.removeAttribute("class")},e.prototype.value=function(t){var e=r(t,this.keyName)[0]||"",n=e.slice(this.keyName.length+1);return this.canAdd(t,n)?n:""},e}(i.default);e.default=l},function(t,e,n){"use strict";function r(t){var e=t.split("-"),n=e.slice(1).map(function(t){return t[0].toUpperCase()+t.slice(1)}).join("");return e[0]+n}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(11),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return o(e,t),e.keys=function(t){return(t.getAttribute("style")||"").split(";").map(function(t){return t.split(":")[0].trim()})},e.prototype.add=function(t,e){return!!this.canAdd(t,e)&&(t.style[r(this.keyName)]=e,!0)},e.prototype.remove=function(t){t.style[r(this.keyName)]="",t.getAttribute("style")||t.removeAttribute("style")},e.prototype.value=function(t){var e=t.style[r(this.keyName)];return this.canAdd(t,e)?e:""},e}(i.default);e.default=l},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),s=function t(e,n,r){null===e&&(e=Function.prototype);var o=Object.getOwnPropertyDescriptor(e,n);if(void 0===o){var i=Object.getPrototypeOf(e);return null===i?void 0:t(i,n,r)}if("value"in o)return o.value;var l=o.get;if(void 0!==l)return l.call(r)},u=function(){function t(t,e){for(var n=0;n '},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;nr.right&&(i=r.right-o.right,this.root.style.left=e+i+"px"),o.leftr.bottom){var l=o.bottom-o.top,a=t.bottom-t.top+l;this.root.style.top=n-a+"px",this.root.classList.add("ql-flip")}return i}},{key:"show",value:function(){this.root.classList.remove("ql-editing"),this.root.classList.remove("ql-hidden")}}]),t}();e.default=i},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function a(t){var e=t.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtube\.com\/watch.*v=([a-zA-Z0-9_-]+)/)||t.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtu\.be\/([a-zA-Z0-9_-]+)/);return e?(e[1]||"https")+"://www.youtube.com/embed/"+e[2]+"?showinfo=0":(e=t.match(/^(?:(https?):\/\/)?(?:www\.)?vimeo\.com\/(\d+)/))?(e[1]||"https")+"://player.vimeo.com/video/"+e[2]+"/":t}function s(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];e.forEach(function(e){var r=document.createElement("option");e===n?r.setAttribute("selected","selected"):r.setAttribute("value",e),t.appendChild(r)})}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.BaseTooltip=void 0;var u=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:"link",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;this.root.classList.remove("ql-hidden"),this.root.classList.add("ql-editing"),null!=e?this.textbox.value=e:t!==this.root.getAttribute("data-mode")&&(this.textbox.value=""),this.position(this.quill.getBounds(this.quill.selection.savedRange)),this.textbox.select(),this.textbox.setAttribute("placeholder",this.textbox.getAttribute("data-"+t)||""),this.root.setAttribute("data-mode",t)}},{key:"restoreFocus",value:function(){var t=this.quill.scrollingContainer.scrollTop;this.quill.focus(),this.quill.scrollingContainer.scrollTop=t}},{key:"save",value:function(){var t=this.textbox.value;switch(this.root.getAttribute("data-mode")){case"link":var e=this.quill.root.scrollTop;this.linkRange?(this.quill.formatText(this.linkRange,"link",t,v.default.sources.USER),delete this.linkRange):(this.restoreFocus(),this.quill.format("link",t,v.default.sources.USER)),this.quill.root.scrollTop=e;break;case"video":t=a(t);case"formula":if(!t)break;var n=this.quill.getSelection(!0);if(null!=n){var r=n.index+n.length;this.quill.insertEmbed(r,this.root.getAttribute("data-mode"),t,v.default.sources.USER),"formula"===this.root.getAttribute("data-mode")&&this.quill.insertText(r+1," ",v.default.sources.USER),this.quill.setSelection(r+2,v.default.sources.USER)}}this.textbox.value="",this.hide()}}]),e}(A.default);e.BaseTooltip=M,e.default=L},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(46),i=r(o),l=n(34),a=n(36),s=n(62),u=n(63),c=r(u),f=n(64),h=r(f),p=n(65),d=r(p),y=n(35),v=n(24),b=n(37),g=n(38),m=n(39),_=r(m),O=n(66),w=r(O),x=n(15),k=r(x),E=n(67),N=r(E),j=n(68),A=r(j),q=n(69),T=r(q),P=n(70),S=r(P),C=n(71),L=r(C),M=n(13),R=r(M),I=n(72),B=r(I),D=n(73),U=r(D),F=n(74),H=r(F),K=n(26),z=r(K),Z=n(16),V=r(Z),W=n(41),G=r(W),Y=n(42),X=r(Y),$=n(43),Q=r($),J=n(107),tt=r(J),et=n(108),nt=r(et);i.default.register({"attributors/attribute/direction":a.DirectionAttribute,"attributors/class/align":l.AlignClass,"attributors/class/background":y.BackgroundClass,"attributors/class/color":v.ColorClass,"attributors/class/direction":a.DirectionClass,"attributors/class/font":b.FontClass,"attributors/class/size":g.SizeClass,"attributors/style/align":l.AlignStyle,"attributors/style/background":y.BackgroundStyle,"attributors/style/color":v.ColorStyle,"attributors/style/direction":a.DirectionStyle,"attributors/style/font":b.FontStyle,"attributors/style/size":g.SizeStyle},!0),i.default.register({"formats/align":l.AlignClass,"formats/direction":a.DirectionClass,"formats/indent":s.IndentClass,"formats/background":y.BackgroundStyle,"formats/color":v.ColorStyle,"formats/font":b.FontClass,"formats/size":g.SizeClass,"formats/blockquote":c.default,"formats/code-block":R.default,"formats/header":h.default,"formats/list":d.default,"formats/bold":_.default,"formats/code":M.Code,"formats/italic":w.default,"formats/link":k.default,"formats/script":N.default,"formats/strike":A.default,"formats/underline":T.default,"formats/image":S.default,"formats/video":L.default,"formats/list/item":p.ListItem,"modules/formula":B.default,"modules/syntax":U.default,"modules/toolbar":H.default,"themes/bubble":tt.default,"themes/snow":nt.default,"ui/icons":z.default,"ui/picker":V.default,"ui/icon-picker":X.default,"ui/color-picker":G.default,"ui/tooltip":Q.default},!0),e.default=i.default},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(0),i=r(o),l=n(6),a=r(l),s=n(3),u=r(s),c=n(14),f=r(c),h=n(23),p=r(h),d=n(31),y=r(d),v=n(33),b=r(v),g=n(5),m=r(g),_=n(59),O=r(_),w=n(8),x=r(w),k=n(60),E=r(k),N=n(61),j=r(N),A=n(25),q=r(A);a.default.register({"blots/block":u.default,"blots/block/embed":s.BlockEmbed,"blots/break":f.default,"blots/container":p.default,"blots/cursor":y.default,"blots/embed":b.default,"blots/inline":m.default,"blots/scroll":O.default,"blots/text":x.default,"modules/clipboard":E.default,"modules/history":j.default,"modules/keyboard":q.default}),i.default.register(u.default,f.default,y.default,m.default,O.default,x.default),e.default=a.default},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(){this.head=this.tail=null,this.length=0}return t.prototype.append=function(){for(var t=[],e=0;e1&&this.append.apply(this,t.slice(1))},t.prototype.contains=function(t){for(var e,n=this.iterator();e=n();)if(e===t)return!0;return!1},t.prototype.insertBefore=function(t,e){t&&(t.next=e,null!=e?(t.prev=e.prev,null!=e.prev&&(e.prev.next=t),e.prev=t,e===this.head&&(this.head=t)):null!=this.tail?(this.tail.next=t,t.prev=this.tail,this.tail=t):(t.prev=null,this.head=this.tail=t),this.length+=1)},t.prototype.offset=function(t){for(var e=0,n=this.head;null!=n;){if(n===t)return e;e+=n.length(),n=n.next}return-1},t.prototype.remove=function(t){this.contains(t)&&(null!=t.prev&&(t.prev.next=t.next),null!=t.next&&(t.next.prev=t.prev),t===this.head&&(this.head=t.next),t===this.tail&&(this.tail=t.prev),this.length-=1)},t.prototype.iterator=function(t){return void 0===t&&(t=this.head),function(){var e=t;return null!=t&&(t=t.next),e}},t.prototype.find=function(t,e){void 0===e&&(e=!1);for(var n,r=this.iterator();n=r();){var o=n.length();if(ta?n(r,t-a,Math.min(e,a+u-t)):n(r,0,Math.min(u,t+e-a)),a+=u}},t.prototype.map=function(t){return this.reduce(function(e,n){return e.push(t(n)),e},[])},t.prototype.reduce=function(t,e){for(var n,r=this.iterator();n=r();)e=t(e,n);return e},t}();e.default=r},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(17),i=n(1),l={attributes:!0,characterData:!0,characterDataOldValue:!0,childList:!0,subtree:!0},a=function(t){function e(e){var n=t.call(this,e)||this;return n.scroll=n,n.observer=new MutationObserver(function(t){n.update(t)}),n.observer.observe(n.domNode,l),n.attach(),n}return r(e,t),e.prototype.detach=function(){t.prototype.detach.call(this),this.observer.disconnect()},e.prototype.deleteAt=function(e,n){this.update(),0===e&&n===this.length()?this.children.forEach(function(t){t.remove()}):t.prototype.deleteAt.call(this,e,n)},e.prototype.formatAt=function(e,n,r,o){this.update(),t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.insertAt=function(e,n,r){this.update(),t.prototype.insertAt.call(this,e,n,r)},e.prototype.optimize=function(e,n){var r=this;void 0===e&&(e=[]),void 0===n&&(n={}),t.prototype.optimize.call(this,n);for(var l=[].slice.call(this.observer.takeRecords());l.length>0;)e.push(l.pop());for(var a=function(t,e){void 0===e&&(e=!0),null!=t&&t!==r&&null!=t.domNode.parentNode&&(null==t.domNode[i.DATA_KEY].mutations&&(t.domNode[i.DATA_KEY].mutations=[]),e&&a(t.parent))},s=function(t){null!=t.domNode[i.DATA_KEY]&&null!=t.domNode[i.DATA_KEY].mutations&&(t instanceof o.default&&t.children.forEach(s),t.optimize(n))},u=e,c=0;u.length>0;c+=1){if(c>=100)throw new Error("[Parchment] Maximum optimize iterations reached");for(u.forEach(function(t){var e=i.find(t.target,!0);null!=e&&(e.domNode===t.target&&("childList"===t.type?(a(i.find(t.previousSibling,!1)),[].forEach.call(t.addedNodes,function(t){var e=i.find(t,!1);a(e,!1),e instanceof o.default&&e.children.forEach(function(t){a(t,!1)})})):"attributes"===t.type&&a(e.prev)),a(e))}),this.children.forEach(s),u=[].slice.call(this.observer.takeRecords()),l=u.slice();l.length>0;)e.push(l.pop())}},e.prototype.update=function(e,n){var r=this;void 0===n&&(n={}),e=e||this.observer.takeRecords(),e.map(function(t){var e=i.find(t.target,!0);return null==e?null:null==e.domNode[i.DATA_KEY].mutations?(e.domNode[i.DATA_KEY].mutations=[t],e):(e.domNode[i.DATA_KEY].mutations.push(t),null)}).forEach(function(t){null!=t&&t!==r&&null!=t.domNode[i.DATA_KEY]&&t.update(t.domNode[i.DATA_KEY].mutations||[],n)}),null!=this.domNode[i.DATA_KEY].mutations&&t.prototype.update.call(this,this.domNode[i.DATA_KEY].mutations,n),this.optimize(e,n)},e.blotName="scroll",e.defaultChild="block",e.scope=i.Scope.BLOCK_BLOT,e.tagName="DIV",e}(o.default);e.default=a},function(t,e,n){"use strict";function r(t,e){if(Object.keys(t).length!==Object.keys(e).length)return!1;for(var n in t)if(t[n]!==e[n])return!1;return!0}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(18),l=n(1),a=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return o(e,t),e.formats=function(n){if(n.tagName!==e.tagName)return t.formats.call(this,n)},e.prototype.format=function(n,r){var o=this;n!==this.statics.blotName||r?t.prototype.format.call(this,n,r):(this.children.forEach(function(t){t instanceof i.default||(t=t.wrap(e.blotName,!0)),o.attributes.copy(t)}),this.unwrap())},e.prototype.formatAt=function(e,n,r,o){if(null!=this.formats()[r]||l.query(r,l.Scope.ATTRIBUTE)){this.isolate(e,n).format(r,o)}else t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.optimize=function(n){t.prototype.optimize.call(this,n);var o=this.formats();if(0===Object.keys(o).length)return this.unwrap();var i=this.next;i instanceof e&&i.prev===this&&r(o,i.formats())&&(i.moveChildren(this),i.remove())},e.blotName="inline",e.scope=l.Scope.INLINE_BLOT,e.tagName="SPAN",e}(i.default);e.default=a},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(18),i=n(1),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r(e,t),e.formats=function(n){var r=i.query(e.blotName).tagName;if(n.tagName!==r)return t.formats.call(this,n)},e.prototype.format=function(n,r){null!=i.query(n,i.Scope.BLOCK)&&(n!==this.statics.blotName||r?t.prototype.format.call(this,n,r):this.replaceWith(e.blotName))},e.prototype.formatAt=function(e,n,r,o){null!=i.query(r,i.Scope.BLOCK)?this.format(r,o):t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.insertAt=function(e,n,r){if(null==r||null!=i.query(n,i.Scope.INLINE))t.prototype.insertAt.call(this,e,n,r);else{var o=this.split(e),l=i.create(n,r);o.parent.insertBefore(l,o)}},e.prototype.update=function(e,n){navigator.userAgent.match(/Trident/)?this.build():t.prototype.update.call(this,e,n)},e.blotName="block",e.scope=i.Scope.BLOCK_BLOT,e.tagName="P",e}(o.default);e.default=l},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(19),i=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r(e,t),e.formats=function(t){},e.prototype.format=function(e,n){t.prototype.formatAt.call(this,0,this.length(),e,n)},e.prototype.formatAt=function(e,n,r,o){0===e&&n===this.length()?this.format(r,o):t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.formats=function(){return this.statics.formats(this.domNode)},e}(o.default);e.default=i},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(19),i=n(1),l=function(t){function e(e){var n=t.call(this,e)||this;return n.text=n.statics.value(n.domNode),n}return r(e,t),e.create=function(t){return document.createTextNode(t)},e.value=function(t){var e=t.data;return e.normalize&&(e=e.normalize()),e},e.prototype.deleteAt=function(t,e){this.domNode.data=this.text=this.text.slice(0,t)+this.text.slice(t+e)},e.prototype.index=function(t,e){return this.domNode===t?e:-1},e.prototype.insertAt=function(e,n,r){null==r?(this.text=this.text.slice(0,e)+n+this.text.slice(e),this.domNode.data=this.text):t.prototype.insertAt.call(this,e,n,r)},e.prototype.length=function(){return this.text.length},e.prototype.optimize=function(n){t.prototype.optimize.call(this,n),this.text=this.statics.value(this.domNode),0===this.text.length?this.remove():this.next instanceof e&&this.next.prev===this&&(this.insertAt(this.length(),this.next.value()),this.next.remove())},e.prototype.position=function(t,e){return void 0===e&&(e=!1),[this.domNode,t]},e.prototype.split=function(t,e){if(void 0===e&&(e=!1),!e){if(0===t)return this;if(t===this.length())return this.next}var n=i.create(this.domNode.splitText(t));return this.parent.insertBefore(n,this.next),this.text=this.statics.value(this.domNode),n},e.prototype.update=function(t,e){var n=this;t.some(function(t){return"characterData"===t.type&&t.target===n.domNode})&&(this.text=this.statics.value(this.domNode))},e.prototype.value=function(){return this.text},e.blotName="text",e.scope=i.Scope.INLINE_BLOT,e}(o.default);e.default=l},function(t,e,n){"use strict";var r=document.createElement("div");if(r.classList.toggle("test-class",!1),r.classList.contains("test-class")){var o=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(t,e){return arguments.length>1&&!this.contains(t)==!e?e:o.call(this,t)}}String.prototype.startsWith||(String.prototype.startsWith=function(t,e){return e=e||0,this.substr(e,t.length)===t}),String.prototype.endsWith||(String.prototype.endsWith=function(t,e){var n=this.toString();("number"!=typeof e||!isFinite(e)||Math.floor(e)!==e||e>n.length)&&(e=n.length),e-=t.length;var r=n.indexOf(t,e);return-1!==r&&r===e}),Array.prototype.find||Object.defineProperty(Array.prototype,"find",{value:function(t){if(null===this)throw new TypeError("Array.prototype.find called on null or undefined");if("function"!=typeof t)throw new TypeError("predicate must be a function");for(var e,n=Object(this),r=n.length>>>0,o=arguments[1],i=0;ie.length?t:e,l=t.length>e.length?e:t,a=i.indexOf(l);if(-1!=a)return r=[[y,i.substring(0,a)],[v,l],[y,i.substring(a+l.length)]],t.length>e.length&&(r[0][0]=r[2][0]=d),r;if(1==l.length)return[[d,t],[y,e]];var u=s(t,e);if(u){var c=u[0],f=u[1],h=u[2],p=u[3],b=u[4],g=n(c,h),m=n(f,p);return g.concat([[v,b]],m)}return o(t,e)}function o(t,e){for(var n=t.length,r=e.length,o=Math.ceil((n+r)/2),l=o,a=2*o,s=new Array(a),u=new Array(a),c=0;cn)v+=2;else if(x>r)p+=2;else if(h){var k=l+f-_;if(k>=0&&k=E)return i(t,e,O,x)}}}for(var N=-m+b;N<=m-g;N+=2){var E,k=l+N;E=N==-m||N!=m&&u[k-1]n)g+=2;else if(j>r)b+=2;else if(!h){var w=l+f-N;if(w>=0&&w=E)return i(t,e,O,x)}}}}return[[d,t],[y,e]]}function i(t,e,r,o){var i=t.substring(0,r),l=e.substring(0,o),a=t.substring(r),s=e.substring(o),u=n(i,l),c=n(a,s);return u.concat(c)}function l(t,e){if(!t||!e||t.charAt(0)!=e.charAt(0))return 0;for(var n=0,r=Math.min(t.length,e.length),o=r,i=0;n=t.length?[r,o,i,s,f]:null}var r=t.length>e.length?t:e,o=t.length>e.length?e:t;if(r.length<4||2*o.lengthu[4].length?s:u:s;var c,f,h,p;return t.length>e.length?(c=i[0],f=i[1],h=i[2],p=i[3]):(h=i[0],p=i[1],c=i[2],f=i[3]),[c,f,h,p,i[4]]}function u(t){t.push([v,""]);for(var e,n=0,r=0,o=0,i="",s="";n1?(0!==r&&0!==o&&(e=l(s,i),0!==e&&(n-r-o>0&&t[n-r-o-1][0]==v?t[n-r-o-1][1]+=s.substring(0,e):(t.splice(0,0,[v,s.substring(0,e)]),n++),s=s.substring(e),i=i.substring(e)),0!==(e=a(s,i))&&(t[n][1]=s.substring(s.length-e)+t[n][1],s=s.substring(0,s.length-e),i=i.substring(0,i.length-e))),0===r?t.splice(n-o,r+o,[y,s]):0===o?t.splice(n-r,r+o,[d,i]):t.splice(n-r-o,r+o,[d,i],[y,s]),n=n-r-o+(r?1:0)+(o?1:0)+1):0!==n&&t[n-1][0]==v?(t[n-1][1]+=t[n][1],t.splice(n,1)):n++,o=0,r=0,i="",s=""}""===t[t.length-1][1]&&t.pop();var c=!1;for(n=1;n0&&r.splice(o+2,0,[l[0],a]),p(r,o,3)}return t}function h(t){for(var e=!1,n=function(t){return t.charCodeAt(0)>=56320&&t.charCodeAt(0)<=57343},r=2;r=55296&&t.charCodeAt(t.length-1)<=56319}(t[r-2][1])&&t[r-1][0]===d&&n(t[r-1][1])&&t[r][0]===y&&n(t[r][1])&&(e=!0,t[r-1][1]=t[r-2][1].slice(-1)+t[r-1][1],t[r][1]=t[r-2][1].slice(-1)+t[r][1],t[r-2][1]=t[r-2][1].slice(0,-1));if(!e)return t;for(var o=[],r=0;r0&&o.push(t[r]);return o}function p(t,e,n){for(var r=e+n-1;r>=0&&r>=e-1;r--)if(r+1=r&&!a.endsWith("\n")&&(n=!0),e.scroll.insertAt(t,a);var c=e.scroll.line(t),f=u(c,2),h=f[0],p=f[1],y=(0,T.default)({},(0,O.bubbleFormats)(h));if(h instanceof w.default){var b=h.descendant(v.default.Leaf,p),g=u(b,1),m=g[0];y=(0,T.default)(y,(0,O.bubbleFormats)(m))}l=d.default.attributes.diff(y,l)||{}}else if("object"===s(o.insert)){var _=Object.keys(o.insert)[0];if(null==_)return t;e.scroll.insertAt(t,_,o.insert[_])}r+=i}return Object.keys(l).forEach(function(n){e.scroll.formatAt(t,i,n,l[n])}),t+i},0),t.reduce(function(t,n){return"number"==typeof n.delete?(e.scroll.deleteAt(t,n.delete),t):t+(n.retain||n.insert.length||1)},0),this.scroll.batchEnd(),this.update(t)}},{key:"deleteText",value:function(t,e){return this.scroll.deleteAt(t,e),this.update((new h.default).retain(t).delete(e))}},{key:"formatLine",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return this.scroll.update(),Object.keys(r).forEach(function(o){if(null==n.scroll.whitelist||n.scroll.whitelist[o]){var i=n.scroll.lines(t,Math.max(e,1)),l=e;i.forEach(function(e){var i=e.length();if(e instanceof g.default){var a=t-e.offset(n.scroll),s=e.newlineIndex(a+l)-a+1;e.formatAt(a,s,o,r[o])}else e.format(o,r[o]);l-=i})}}),this.scroll.optimize(),this.update((new h.default).retain(t).retain(e,(0,N.default)(r)))}},{key:"formatText",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return Object.keys(r).forEach(function(o){n.scroll.formatAt(t,e,o,r[o])}),this.update((new h.default).retain(t).retain(e,(0,N.default)(r)))}},{key:"getContents",value:function(t,e){return this.delta.slice(t,t+e)}},{key:"getDelta",value:function(){return this.scroll.lines().reduce(function(t,e){return t.concat(e.delta())},new h.default)}},{key:"getFormat",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=[],r=[];0===e?this.scroll.path(t).forEach(function(t){var e=u(t,1),o=e[0];o instanceof w.default?n.push(o):o instanceof v.default.Leaf&&r.push(o)}):(n=this.scroll.lines(t,e),r=this.scroll.descendants(v.default.Leaf,t,e));var o=[n,r].map(function(t){if(0===t.length)return{};for(var e=(0,O.bubbleFormats)(t.shift());Object.keys(e).length>0;){var n=t.shift();if(null==n)return e;e=l((0,O.bubbleFormats)(n),e)}return e});return T.default.apply(T.default,o)}},{key:"getText",value:function(t,e){return this.getContents(t,e).filter(function(t){return"string"==typeof t.insert}).map(function(t){return t.insert}).join("")}},{key:"insertEmbed",value:function(t,e,n){return this.scroll.insertAt(t,e,n),this.update((new h.default).retain(t).insert(o({},e,n)))}},{key:"insertText",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return e=e.replace(/\r\n/g,"\n").replace(/\r/g,"\n"),this.scroll.insertAt(t,e),Object.keys(r).forEach(function(o){n.scroll.formatAt(t,e.length,o,r[o])}),this.update((new h.default).retain(t).insert(e,(0,N.default)(r)))}},{key:"isBlank",value:function(){if(0==this.scroll.children.length)return!0;if(this.scroll.children.length>1)return!1;var t=this.scroll.children.head;return t.statics.blotName===w.default.blotName&&(!(t.children.length>1)&&t.children.head instanceof k.default)}},{key:"removeFormat",value:function(t,e){var n=this.getText(t,e),r=this.scroll.line(t+e),o=u(r,2),i=o[0],l=o[1],a=0,s=new h.default;null!=i&&(a=i instanceof g.default?i.newlineIndex(l)-l+1:i.length()-l,s=i.delta().slice(l,l+a-1).insert("\n"));var c=this.getContents(t,e+a),f=c.diff((new h.default).insert(n).concat(s)),p=(new h.default).retain(t).concat(f);return this.applyDelta(p)}},{key:"update",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,r=this.delta;if(1===e.length&&"characterData"===e[0].type&&e[0].target.data.match(P)&&v.default.find(e[0].target)){var o=v.default.find(e[0].target),i=(0,O.bubbleFormats)(o),l=o.offset(this.scroll),a=e[0].oldValue.replace(_.default.CONTENTS,""),s=(new h.default).insert(a),u=(new h.default).insert(o.value());t=(new h.default).retain(l).concat(s.diff(u,n)).reduce(function(t,e){return e.insert?t.insert(e.insert,i):t.push(e)},new h.default),this.delta=r.compose(t)}else this.delta=this.getDelta(),t&&(0,A.default)(r.compose(t),this.delta)||(t=r.diff(this.delta,n));return t}}]),t}();e.default=S},function(t,e){"use strict";function n(){}function r(t,e,n){this.fn=t,this.context=e,this.once=n||!1}function o(){this._events=new n,this._eventsCount=0}var i=Object.prototype.hasOwnProperty,l="~";Object.create&&(n.prototype=Object.create(null),(new n).__proto__||(l=!1)),o.prototype.eventNames=function(){var t,e,n=[];if(0===this._eventsCount)return n;for(e in t=this._events)i.call(t,e)&&n.push(l?e.slice(1):e);return Object.getOwnPropertySymbols?n.concat(Object.getOwnPropertySymbols(t)):n},o.prototype.listeners=function(t,e){var n=l?l+t:t,r=this._events[n];if(e)return!!r;if(!r)return[];if(r.fn)return[r.fn];for(var o=0,i=r.length,a=new Array(i);o0){if(i instanceof y.BlockEmbed||f instanceof y.BlockEmbed)return void this.optimize();if(i instanceof _.default){var h=i.newlineIndex(i.length(),!0);if(h>-1&&(i=i.split(h+1))===f)return void this.optimize()}else if(f instanceof _.default){var p=f.newlineIndex(0);p>-1&&f.split(p+1)}var d=f.children.head instanceof g.default?null:f.children.head;i.moveChildren(f,d),i.remove()}this.optimize()}},{key:"enable",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.domNode.setAttribute("contenteditable",t)}},{key:"formatAt",value:function(t,n,r,o){(null==this.whitelist||this.whitelist[r])&&(c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"formatAt",this).call(this,t,n,r,o),this.optimize())}},{key:"insertAt",value:function(t,n,r){if(null==r||null==this.whitelist||this.whitelist[n]){if(t>=this.length())if(null==r||null==h.default.query(n,h.default.Scope.BLOCK)){var o=h.default.create(this.statics.defaultChild);this.appendChild(o),null==r&&n.endsWith("\n")&&(n=n.slice(0,-1)),o.insertAt(0,n,r)}else{var i=h.default.create(n,r);this.appendChild(i)}else c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"insertAt",this).call(this,t,n,r);this.optimize()}}},{key:"insertBefore",value:function(t,n){if(t.statics.scope===h.default.Scope.INLINE_BLOT){var r=h.default.create(this.statics.defaultChild);r.appendChild(t),t=r}c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"insertBefore",this).call(this,t,n)}},{key:"leaf",value:function(t){return this.path(t).pop()||[null,-1]}},{key:"line",value:function(t){return t===this.length()?this.line(t-1):this.descendant(a,t)}},{key:"lines",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Number.MAX_VALUE;return function t(e,n,r){var o=[],i=r;return e.children.forEachAt(n,r,function(e,n,r){a(e)?o.push(e):e instanceof h.default.Container&&(o=o.concat(t(e,n,i))),i-=r}),o}(this,t,e)}},{key:"optimize",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};!0!==this.batch&&(c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"optimize",this).call(this,t,n),t.length>0&&this.emitter.emit(d.default.events.SCROLL_OPTIMIZE,t,n))}},{key:"path",value:function(t){return c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"path",this).call(this,t).slice(1)}},{key:"update",value:function(t){if(!0!==this.batch){var n=d.default.sources.USER;"string"==typeof t&&(n=t),Array.isArray(t)||(t=this.observer.takeRecords()),t.length>0&&this.emitter.emit(d.default.events.SCROLL_BEFORE_UPDATE,n,t),c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"update",this).call(this,t.concat([])),t.length>0&&this.emitter.emit(d.default.events.SCROLL_UPDATE,n,t)}}}]),e}(h.default.Scroll);x.blotName="scroll",x.className="ql-editor",x.tagName="DIV",x.defaultChild="block",x.allowedChildren=[v.default,y.BlockEmbed,w.default],e.default=x},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function l(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function s(t,e,n){return"object"===(void 0===e?"undefined":x(e))?Object.keys(e).reduce(function(t,n){return s(t,n,e[n])},t):t.reduce(function(t,r){return r.attributes&&r.attributes[e]?t.push(r):t.insert(r.insert,(0,j.default)({},o({},e,n),r.attributes))},new q.default)}function u(t){if(t.nodeType!==Node.ELEMENT_NODE)return{};return t["__ql-computed-style"]||(t["__ql-computed-style"]=window.getComputedStyle(t))}function c(t,e){for(var n="",r=t.ops.length-1;r>=0&&n.length-1}function h(t,e,n){return t.nodeType===t.TEXT_NODE?n.reduce(function(e,n){return n(t,e)},new q.default):t.nodeType===t.ELEMENT_NODE?[].reduce.call(t.childNodes||[],function(r,o){var i=h(o,e,n);return o.nodeType===t.ELEMENT_NODE&&(i=e.reduce(function(t,e){return e(o,t)},i),i=(o[W]||[]).reduce(function(t,e){return e(o,t)},i)),r.concat(i)},new q.default):new q.default}function p(t,e,n){return s(n,t,!0)}function d(t,e){var n=P.default.Attributor.Attribute.keys(t),r=P.default.Attributor.Class.keys(t),o=P.default.Attributor.Style.keys(t),i={};return n.concat(r).concat(o).forEach(function(e){var n=P.default.query(e,P.default.Scope.ATTRIBUTE);null!=n&&(i[n.attrName]=n.value(t),i[n.attrName])||(n=Y[e],null==n||n.attrName!==e&&n.keyName!==e||(i[n.attrName]=n.value(t)||void 0),null==(n=X[e])||n.attrName!==e&&n.keyName!==e||(n=X[e],i[n.attrName]=n.value(t)||void 0))}),Object.keys(i).length>0&&(e=s(e,i)),e}function y(t,e){var n=P.default.query(t);if(null==n)return e;if(n.prototype instanceof P.default.Embed){var r={},o=n.value(t);null!=o&&(r[n.blotName]=o,e=(new q.default).insert(r,n.formats(t)))}else"function"==typeof n.formats&&(e=s(e,n.blotName,n.formats(t)));return e}function v(t,e){return c(e,"\n")||e.insert("\n"),e}function b(){return new q.default}function g(t,e){var n=P.default.query(t);if(null==n||"list-item"!==n.blotName||!c(e,"\n"))return e;for(var r=-1,o=t.parentNode;!o.classList.contains("ql-clipboard");)"list"===(P.default.query(o)||{}).blotName&&(r+=1),o=o.parentNode;return r<=0?e:e.compose((new q.default).retain(e.length()-1).retain(1,{indent:r}))}function m(t,e){return c(e,"\n")||(f(t)||e.length()>0&&t.nextSibling&&f(t.nextSibling))&&e.insert("\n"),e}function _(t,e){if(f(t)&&null!=t.nextElementSibling&&!c(e,"\n\n")){var n=t.offsetHeight+parseFloat(u(t).marginTop)+parseFloat(u(t).marginBottom);t.nextElementSibling.offsetTop>t.offsetTop+1.5*n&&e.insert("\n")}return e}function O(t,e){var n={},r=t.style||{};return r.fontStyle&&"italic"===u(t).fontStyle&&(n.italic=!0),r.fontWeight&&(u(t).fontWeight.startsWith("bold")||parseInt(u(t).fontWeight)>=700)&&(n.bold=!0),Object.keys(n).length>0&&(e=s(e,n)),parseFloat(r.textIndent||0)>0&&(e=(new q.default).insert("\t").concat(e)),e}function w(t,e){var n=t.data;if("O:P"===t.parentNode.tagName)return e.insert(n.trim());if(0===n.trim().length&&t.parentNode.classList.contains("ql-clipboard"))return e;if(!u(t.parentNode).whiteSpace.startsWith("pre")){var r=function(t,e){return e=e.replace(/[^\u00a0]/g,""),e.length<1&&t?" ":e};n=n.replace(/\r\n/g," ").replace(/\n/g," "),n=n.replace(/\s\s+/g,r.bind(r,!0)),(null==t.previousSibling&&f(t.parentNode)||null!=t.previousSibling&&f(t.previousSibling))&&(n=n.replace(/^\s+/,r.bind(r,!1))),(null==t.nextSibling&&f(t.parentNode)||null!=t.nextSibling&&f(t.nextSibling))&&(n=n.replace(/\s+$/,r.bind(r,!1)))}return e.insert(n)}Object.defineProperty(e,"__esModule",{value:!0}),e.matchText=e.matchSpacing=e.matchNewline=e.matchBlot=e.matchAttributor=e.default=void 0;var x="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},k=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),E=function(){function t(t,e){for(var n=0;n\r?\n +\<"),this.convert();var e=this.quill.getFormat(this.quill.selection.savedRange.index);if(e[F.default.blotName]){var n=this.container.innerText;return this.container.innerHTML="",(new q.default).insert(n,o({},F.default.blotName,e[F.default.blotName]))}var r=this.prepareMatching(),i=k(r,2),l=i[0],a=i[1],s=h(this.container,l,a);return c(s,"\n")&&null==s.ops[s.ops.length-1].attributes&&(s=s.compose((new q.default).retain(s.length()-1).delete(1))),V.log("convert",this.container.innerHTML,s),this.container.innerHTML="",s}},{key:"dangerouslyPasteHTML",value:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:C.default.sources.API;if("string"==typeof t)this.quill.setContents(this.convert(t),e),this.quill.setSelection(0,C.default.sources.SILENT);else{var r=this.convert(e);this.quill.updateContents((new q.default).retain(t).concat(r),n),this.quill.setSelection(t+r.length(),C.default.sources.SILENT)}}},{key:"onPaste",value:function(t){var e=this;if(!t.defaultPrevented&&this.quill.isEnabled()){var n=this.quill.getSelection(),r=(new q.default).retain(n.index),o=this.quill.scrollingContainer.scrollTop;this.container.focus(),this.quill.selection.update(C.default.sources.SILENT),setTimeout(function(){r=r.concat(e.convert()).delete(n.length),e.quill.updateContents(r,C.default.sources.USER),e.quill.setSelection(r.length()-n.length,C.default.sources.SILENT),e.quill.scrollingContainer.scrollTop=o,e.quill.focus()},1)}}},{key:"prepareMatching",value:function(){var t=this,e=[],n=[];return this.matchers.forEach(function(r){var o=k(r,2),i=o[0],l=o[1];switch(i){case Node.TEXT_NODE:n.push(l);break;case Node.ELEMENT_NODE:e.push(l);break;default:[].forEach.call(t.container.querySelectorAll(i),function(t){t[W]=t[W]||[],t[W].push(l)})}}),[e,n]}}]),e}(I.default);$.DEFAULTS={matchers:[],matchVisual:!0},e.default=$,e.matchAttributor=d,e.matchBlot=y,e.matchNewline=m,e.matchSpacing=_,e.matchText=w},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function a(t){var e=t.ops[t.ops.length-1];return null!=e&&(null!=e.insert?"string"==typeof e.insert&&e.insert.endsWith("\n"):null!=e.attributes&&Object.keys(e.attributes).some(function(t){return null!=f.default.query(t,f.default.Scope.BLOCK)}))}function s(t){var e=t.reduce(function(t,e){return t+=e.delete||0},0),n=t.length()-e;return a(t)&&(n-=1),n}Object.defineProperty(e,"__esModule",{value:!0}),e.getLastChangeIndex=e.default=void 0;var u=function(){function t(t,e){for(var n=0;nr&&this.stack.undo.length>0){var o=this.stack.undo.pop();n=n.compose(o.undo),t=o.redo.compose(t)}else this.lastRecorded=r;this.stack.undo.push({redo:t,undo:n}),this.stack.undo.length>this.options.maxStack&&this.stack.undo.shift()}}},{key:"redo",value:function(){this.change("redo","undo")}},{key:"transform",value:function(t){this.stack.undo.forEach(function(e){e.undo=t.transform(e.undo,!0),e.redo=t.transform(e.redo,!0)}),this.stack.redo.forEach(function(e){e.undo=t.transform(e.undo,!0),e.redo=t.transform(e.redo,!0)})}},{key:"undo",value:function(){this.change("undo","redo")}}]),e}(y.default);v.DEFAULTS={delay:1e3,maxStack:100,userOnly:!1},e.default=v,e.getLastChangeIndex=s},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.IndentClass=void 0;var l=function(){function t(t,e){for(var n=0;n0&&this.children.tail.format(t,e)}},{key:"formats",value:function(){return o({},this.statics.blotName,this.statics.formats(this.domNode))}},{key:"insertBefore",value:function(t,n){if(t instanceof v)u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"insertBefore",this).call(this,t,n);else{var r=null==n?this.length():n.offset(this),o=this.split(r);o.parent.insertBefore(t,o)}}},{key:"optimize",value:function(t){u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"optimize",this).call(this,t);var n=this.next;null!=n&&n.prev===this&&n.statics.blotName===this.statics.blotName&&n.domNode.tagName===this.domNode.tagName&&n.domNode.getAttribute("data-checked")===this.domNode.getAttribute("data-checked")&&(n.moveChildren(this),n.remove())}},{key:"replace",value:function(t){if(t.statics.blotName!==this.statics.blotName){var n=f.default.create(this.statics.defaultChild);t.moveChildren(n),this.appendChild(n)}u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"replace",this).call(this,t)}}]),e}(y.default);b.blotName="list",b.scope=f.default.Scope.BLOCK_BLOT,b.tagName=["OL","UL"],b.defaultChild="list-item",b.allowedChildren=[v],e.ListItem=v,e.default=b},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=n(39),a=function(t){return t&&t.__esModule?t:{default:t}}(l),s=function(t){function e(){return r(this,e),o(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return i(e,t),e}(a.default);s.blotName="italic",s.tagName=["EM","I"],e.default=s},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;n-1?n?this.domNode.setAttribute(t,n):this.domNode.removeAttribute(t):a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"format",this).call(this,t,n)}}],[{key:"create",value:function(t){var n=a(e.__proto__||Object.getPrototypeOf(e),"create",this).call(this,t);return"string"==typeof t&&n.setAttribute("src",this.sanitize(t)),n}},{key:"formats",value:function(t){return f.reduce(function(e,n){return t.hasAttribute(n)&&(e[n]=t.getAttribute(n)),e},{})}},{key:"match",value:function(t){return/\.(jpe?g|gif|png)$/.test(t)||/^data:image\/.+;base64/.test(t)}},{key:"sanitize",value:function(t){return(0,c.sanitize)(t,["http","https","data"])?t:"//:0"}},{key:"value",value:function(t){return t.getAttribute("src")}}]),e}(u.default.Embed);h.blotName="image",h.tagName="IMG",e.default=h},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;n-1?n?this.domNode.setAttribute(t,n):this.domNode.removeAttribute(t):a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"format",this).call(this,t,n)}}],[{key:"create",value:function(t){var n=a(e.__proto__||Object.getPrototypeOf(e),"create",this).call(this,t);return n.setAttribute("frameborder","0"),n.setAttribute("allowfullscreen",!0),n.setAttribute("src",this.sanitize(t)),n}},{key:"formats",value:function(t){return f.reduce(function(e,n){return t.hasAttribute(n)&&(e[n]=t.getAttribute(n)),e},{})}},{key:"sanitize",value:function(t){return c.default.sanitize(t)}},{key:"value",value:function(t){return t.getAttribute("src")}}]),e}(s.BlockEmbed);h.blotName="video",h.className="ql-video",h.tagName="IFRAME",e.default=h},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.FormulaBlot=void 0;var a=function(){function t(t,e){for(var n=0;n0||null==this.cachedText)&&(this.domNode.innerHTML=t(e),this.domNode.normalize(),this.attach()),this.cachedText=e)}}]),e}(v.default);b.className="ql-syntax";var g=new c.default.Attributor.Class("token","hljs",{scope:c.default.Scope.INLINE}),m=function(t){function e(t,n){o(this,e);var r=i(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,t,n));if("function"!=typeof r.options.highlight)throw new Error("Syntax module requires highlight.js. Please include the library on the page before Quill.");var l=null;return r.quill.on(h.default.events.SCROLL_OPTIMIZE,function(){clearTimeout(l),l=setTimeout(function(){r.highlight(),l=null},r.options.interval)}),r.highlight(),r}return l(e,t),a(e,null,[{key:"register",value:function(){h.default.register(g,!0),h.default.register(b,!0)}}]),a(e,[{key:"highlight",value:function(){var t=this;if(!this.quill.selection.composing){this.quill.update(h.default.sources.USER);var e=this.quill.getSelection();this.quill.scroll.descendants(b).forEach(function(e){e.highlight(t.options.highlight)}),this.quill.update(h.default.sources.SILENT),null!=e&&this.quill.setSelection(e,h.default.sources.SILENT)}}}]),e}(d.default);m.DEFAULTS={highlight:function(){return null==window.hljs?null:function(t){return window.hljs.highlightAuto(t).value}}(),interval:1e3},e.CodeBlock=b,e.CodeToken=g,e.default=m},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function l(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function s(t,e,n){var r=document.createElement("button");r.setAttribute("type","button"),r.classList.add("ql-"+e),null!=n&&(r.value=n),t.appendChild(r)}function u(t,e){Array.isArray(e[0])||(e=[e]),e.forEach(function(e){var n=document.createElement("span");n.classList.add("ql-formats"),e.forEach(function(t){if("string"==typeof t)s(n,t);else{var e=Object.keys(t)[0],r=t[e];Array.isArray(r)?c(n,e,r):s(n,e,r)}}),t.appendChild(n)})}function c(t,e,n){var r=document.createElement("select");r.classList.add("ql-"+e),n.forEach(function(t){var e=document.createElement("option");!1!==t?e.setAttribute("value",t):e.setAttribute("selected","selected"),r.appendChild(e)}),t.appendChild(r)}Object.defineProperty(e,"__esModule",{value:!0}),e.addControls=e.default=void 0;var f=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),h=function(){function t(t,e){for(var n=0;n '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.BubbleTooltip=void 0;var a=function t(e,n,r){null===e&&(e=Function.prototype);var o=Object.getOwnPropertyDescriptor(e,n);if(void 0===o){var i=Object.getPrototypeOf(e);return null===i?void 0:t(i,n,r)}if("value"in o)return o.value;var l=o.get;if(void 0!==l)return l.call(r)},s=function(){function t(t,e){for(var n=0;n0&&o===h.default.sources.USER){r.show(),r.root.style.left="0px",r.root.style.width="",r.root.style.width=r.root.offsetWidth+"px";var i=r.quill.getLines(e.index,e.length);if(1===i.length)r.position(r.quill.getBounds(e));else{var l=i[i.length-1],a=r.quill.getIndex(l),s=Math.min(l.length()-1,e.index+e.length-a),u=r.quill.getBounds(new y.Range(a,s));r.position(u)}}else document.activeElement!==r.textbox&&r.quill.hasFocus()&&r.hide()}),r}return l(e,t),s(e,[{key:"listen",value:function(){var t=this;a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"listen",this).call(this),this.root.querySelector(".ql-close").addEventListener("click",function(){t.root.classList.remove("ql-editing")}),this.quill.on(h.default.events.SCROLL_OPTIMIZE,function(){setTimeout(function(){if(!t.root.classList.contains("ql-hidden")){var e=t.quill.getSelection();null!=e&&t.position(t.quill.getBounds(e))}},1)})}},{key:"cancel",value:function(){this.show()}},{key:"position",value:function(t){var n=a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"position",this).call(this,t),r=this.root.querySelector(".ql-tooltip-arrow");if(r.style.marginLeft="",0===n)return n;r.style.marginLeft=-1*n-r.offsetWidth/2+"px"}}]),e}(p.BaseTooltip);_.TEMPLATE=['','
    ','','',"
    "].join(""),e.BubbleTooltip=_,e.default=m},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),s=function t(e,n,r){null===e&&(e=Function.prototype);var o=Object.getOwnPropertyDescriptor(e,n);if(void 0===o){var i=Object.getPrototypeOf(e);return null===i?void 0:t(i,n,r)}if("value"in o)return o.value;var l=o.get;if(void 0!==l)return l.call(r)},u=function(){function t(t,e){for(var n=0;n','','',''].join(""),e.default=w}]).default}); \ No newline at end of file From 6ac2b64d7d5ff4fdbde3476991c0eaa0365c19c4 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 22 May 2020 13:39:59 -0400 Subject: [PATCH 258/265] improvements to module creator templates --- Oqtane.Server/Infrastructure/InstallationManager.cs | 9 +-------- .../Templates/External/Package/[Owner].[Module]s.nuspec | 1 - .../wwwroot/Modules/Templates/External/Package/debug.cmd | 1 + .../Templates/External/Server/content/resources.txt | 1 - .../wwwroot/{ => Modules/[Owner].[Module]s}/Module.css | 0 .../Server/wwwroot/Modules/[Owner].[Module]s/Module.js | 1 + .../{ => Modules/[Owner].[Module]s}/resources.txt | 0 7 files changed, 3 insertions(+), 10 deletions(-) delete mode 100644 Oqtane.Server/wwwroot/Modules/Templates/External/Server/content/resources.txt rename Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/{ => Modules/[Owner].[Module]s}/Module.css (100%) create mode 100644 Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/Modules/[Owner].[Module]s/Module.js rename Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/{ => Modules/[Owner].[Module]s}/resources.txt (100%) diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index 793e87a4..c6467c20 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -98,16 +98,9 @@ namespace Oqtane.Infrastructure ExtractFile(entry, filename); break; case "wwwroot": - filename = Path.Combine(sourceFolder, Utilities.PathCombine(entry.FullName.Replace("wwwroot", name).Split('/'))); + filename = Path.Combine(webRootPath, Utilities.PathCombine(entry.FullName.Replace("wwwroot/", "").Split('/'))); ExtractFile(entry, filename); break; - case "content": - if (Path.GetDirectoryName(entry.FullName) != "content") // assets must be in subfolders - { - filename = Path.Combine(webRootPath, Utilities.PathCombine(entry.FullName.Replace("content", "").Split('/'))); - ExtractFile(entry, filename); - } - break; } } } diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].[Module]s.nuspec b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].[Module]s.nuspec index 0835aea4..72d74777 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].[Module]s.nuspec +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].[Module]s.nuspec @@ -27,6 +27,5 @@ - \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.cmd b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.cmd index c70bdba8..7ac65774 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.cmd +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.cmd @@ -4,3 +4,4 @@ XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Server.Oqtane.dll" ". XCOPY "..\Server\bin\Debug\netcoreapp3.1\[Owner].[Module]s.Server.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Shared.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y XCOPY "..\Shared\bin\Debug\netstandard2.1\[Owner].[Module]s.Shared.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\" /Y +XCOPY "..\Server\wwwroot\Modules\[Owner].[Module]s\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\Modules\[Owner].[Module]s\" /Y /S /I diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/content/resources.txt b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/content/resources.txt deleted file mode 100644 index ac84ee50..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/content/resources.txt +++ /dev/null @@ -1 +0,0 @@ -This is the location where static resources for third party libraries should be located ( the third party library assemblies will be included in the /lib folder ). They should be placed in subfolders which match the naming convention of the third party library. When the module package is deployed the static resource subfolders will be extracted under the web root. \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/Module.css b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/Modules/[Owner].[Module]s/Module.css similarity index 100% rename from Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/Module.css rename to Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/Modules/[Owner].[Module]s/Module.css diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/Modules/[Owner].[Module]s/Module.js b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/Modules/[Owner].[Module]s/Module.js new file mode 100644 index 00000000..1b415a08 --- /dev/null +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/Modules/[Owner].[Module]s/Module.js @@ -0,0 +1 @@ +/* Module Script */ \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/resources.txt b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/Modules/[Owner].[Module]s/resources.txt similarity index 100% rename from Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/resources.txt rename to Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/Modules/[Owner].[Module]s/resources.txt From ddbb08ea759d5363b5527932c1e8d186a336938a Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Fri, 22 May 2020 21:29:06 +0200 Subject: [PATCH 259/265] Control panel state persistence --- .../Themes/Controls/ControlPanel.razor | 212 +++++++++--------- 1 file changed, 108 insertions(+), 104 deletions(-) diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor index e80ca085..2da8f847 100644 --- a/Oqtane.Client/Themes/Controls/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor @@ -1,5 +1,5 @@ @namespace Oqtane.Themes.Controls -@inherits ThemeControlBase +@inherits ThemeControlBase @attribute [OqtaneIgnore] @inject NavigationManager NavigationManager @inject IUserService UserService @@ -9,8 +9,9 @@ @inject IPageService PageService @inject IPageModuleService PageModuleService @inject ILogService logger +@inject ISettingService SettingService -@if (_moduleDefinitions != null && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions)) +@if (_moduleDefinitions != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) {
    @@ -31,7 +32,7 @@
    -
    +
    @@ -73,23 +74,23 @@
    } -
    +
    - - @if (_moduleType == "new") + @if (ModuleType == "new") { @if (_moduleDefinitions != null) { - @((MarkupString)@_description) + @((MarkupString) Description) } } else @@ -128,7 +129,7 @@ } - @foreach (Module module in _modules) { @@ -141,13 +142,13 @@
    - +
    - @foreach (string pane in PageState.Page.Panes) { @@ -159,7 +160,7 @@
    - @foreach (KeyValuePair container in _containers) { @@ -168,16 +169,16 @@
    -
    +
    - @((MarkupString) _message) + @((MarkupString) Message)
    } -@if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null)) +@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null)) { @if (PageState.Page.EditMode) { @@ -202,32 +203,52 @@ } } -@if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions)) +@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) { } -@code { +@code{ + private bool _deleteConfirmation = false; - private string _moduleType = "new"; private List _categories = new List(); private List _allModuleDefinitions; private List _moduleDefinitions; private List _pages = new List(); - private string _pageId = "-"; - private string _moduleId = "-"; private List _modules = new List(); private Dictionary _containers = new Dictionary(); - private string _moduleDefinitionName = "-"; - private string _category = "Common"; - private string _description = ""; - private string _pane = ""; - private string _title = ""; - private string _containerType = ""; private string _display = "display: none;"; - private string _message = ""; + private string _category = "Common"; + + protected string PageId { get; private set; } = "-"; + protected string ModuleId { get; private set; } = "-"; + protected string ModuleType { get; private set; } = "new"; + protected string ModuleDefinitionName { get; private set; } = "-"; + + protected string Category + { + get => _category; + private set + { + if (_category != value) + { + _category = value; + _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList(); + ModuleDefinitionName = "-"; + Description = ""; + StateHasChanged(); + _ = UpdateSettingsAsync(); + } + } + } + + protected string Description { get; private set; } = ""; + protected string Pane { get; private set; } = ""; + protected string Title { get; private set; } = ""; + protected string ContainerType { get; private set; } = ""; + protected string Message { get; private set; } = ""; [Parameter] public string ButtonClass { get; set; } @@ -241,7 +262,8 @@ [Parameter] public string BodyClass { get; set; } - protected override async Task OnParametersSetAsync() + + protected override async Task OnInitializedAsync() { if (string.IsNullOrEmpty(ButtonClass)) { @@ -263,131 +285,107 @@ BodyClass = "card-body"; } - if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions)) + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) { _pages?.Clear(); foreach (Page p in PageState.Pages) { - if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, p.Permissions)) + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) { _pages.Add(p); } } + await LoadSettingsAsync(); var panes = PageState.Page.Panes; - _pane = panes.Count() == 1 ? panes.SingleOrDefault() : ""; + Pane = panes.Count() == 1 ? panes.SingleOrDefault() : ""; var themes = await ThemeService.GetThemesAsync(); _containers = ThemeService.GetContainerTypes(themes); - _containerType = PageState.Site.DefaultContainerType; + ContainerType = PageState.Site.DefaultContainerType; _allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); + _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList(); - _categories = new List(); - foreach (ModuleDefinition moduledefinition in _allModuleDefinitions) - { - if (moduledefinition.Categories != "") - { - foreach (string category in moduledefinition.Categories.Split(',')) - { - if (!_categories.Contains(category)) - { - _categories.Add(category); - } - } - } - } - - _category = "Common"; - _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList(); - _moduleDefinitionName = "-"; - _description = ""; + _categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList(); } } private void CategoryChanged(ChangeEventArgs e) { - _category = (string) e.Value; - _moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList(); - _moduleDefinitionName = "-"; - _description = ""; - StateHasChanged(); + Category = (string) e.Value; } private void ModuleChanged(ChangeEventArgs e) { - _moduleDefinitionName = (string)e.Value; - if (_moduleDefinitionName != "-") + ModuleDefinitionName = (string) e.Value; + if (ModuleDefinitionName != "-") { - var moduleDefinition = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == _moduleDefinitionName); - _description = "
    " + moduleDefinition.Description + "
    "; + var moduleDefinition = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == ModuleDefinitionName); + Description = "
    " + moduleDefinition.Description + "
    "; } else { - _description = ""; + Description = ""; } + StateHasChanged(); } private void PageChanged(ChangeEventArgs e) { - _pageId = (string) e.Value; - _modules?.Clear(); - - if (_pageId != "-") + PageId = (string) e.Value; + if (PageId != "-") { - foreach (Module module in PageState.Modules.Where(item => item.PageId == int.Parse(_pageId) && !item.IsDeleted)) - { - if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, module.Permissions)) - { - _modules.Add(module); - } - } + _modules = PageState.Modules + .Where(module => module.PageId == int.Parse(PageId) + && !module.IsDeleted + && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions)) + .ToList(); } - - _moduleId = "-"; + ModuleId = "-"; StateHasChanged(); } private async Task AddModule() { - if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions)) + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) { - if ((_moduleType == "new" && _moduleDefinitionName != "-") || (_moduleType != "new" && _moduleId != "-")) + if ((ModuleType == "new" && ModuleDefinitionName != "-") || (ModuleType != "new" && ModuleId != "-")) { - if (_moduleType == "new") + if (ModuleType == "new") { Module module = new Module(); module.SiteId = PageState.Site.SiteId; module.PageId = PageState.Page.PageId; - module.ModuleDefinitionName = _moduleDefinitionName; + module.ModuleDefinitionName = ModuleDefinitionName; module.AllPages = false; module.Permissions = PageState.Page.Permissions; module = await ModuleService.AddModuleAsync(module); - _moduleId = module.ModuleId.ToString(); + ModuleId = module.ModuleId.ToString(); } var pageModule = new PageModule { PageId = PageState.Page.PageId, - ModuleId = int.Parse(_moduleId), - Title = _title + ModuleId = int.Parse(ModuleId), + Title = Title }; if (pageModule.Title == "") { - if (_moduleType == "new") + if (ModuleType == "new") { - pageModule.Title = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == _moduleDefinitionName)?.Name; + pageModule.Title = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == ModuleDefinitionName)?.Name; } else { - pageModule.Title = _modules.FirstOrDefault(item => item.ModuleId == int.Parse(_moduleId))?.Title; + pageModule.Title = _modules.FirstOrDefault(item => item.ModuleId == int.Parse(ModuleId))?.Title; } } - pageModule.Pane = _pane; + pageModule.Pane = Pane; pageModule.Order = int.MaxValue; - pageModule.ContainerType = _containerType; + pageModule.ContainerType = ContainerType; if (pageModule.ContainerType == PageState.Site.DefaultContainerType) { @@ -397,32 +395,23 @@ await PageModuleService.AddPageModuleAsync(pageModule); await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane); - _message = "
    Module Added To Page
    "; - - _moduleDefinitionName = "-"; - _description = ""; - _pane = ""; - _title = ""; - _containerType = ""; - _pageId = "-"; - _moduleId = "-"; - + Message = "
    Module Added To Page
    "; NavigationManager.NavigateTo(NavigateUrl()); } else { - _message = "
    You Must Select A Module
    "; + Message = "
    You Must Select A Module
    "; } } else { - _message = "
    Not Authorized
    "; + Message = "
    Not Authorized
    "; } } private async Task ToggleEditMode(bool EditMode) { - if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions)) + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) { if (EditMode) { @@ -432,6 +421,7 @@ { PageState.EditMode = true; } + NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0"))); } else @@ -447,14 +437,14 @@ private void ShowControlPanel() { - _message = ""; + Message = ""; _display = "width: 25%; min-width: 375px;"; StateHasChanged(); } private void HideControlPanel() { - _message = ""; + Message = ""; _display = "width: 0%;"; StateHasChanged(); } @@ -466,7 +456,7 @@ switch (location) { case "Admin": - // get admin dashboard moduleid + // get admin dashboard moduleid module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule); if (module != null) @@ -478,7 +468,7 @@ case "Add": case "Edit": string url = ""; - // get page management moduleid + // get page management moduleid module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.PageManagementModule); if (module != null) @@ -536,4 +526,18 @@ } } + private string settingName = "CP-category"; + + private async Task LoadSettingsAsync() + { + Dictionary settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); + _category = SettingService.GetSetting(settings, settingName, "Common"); + } + + private async Task UpdateSettingsAsync() + { + Dictionary settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); + SettingService.SetSetting(settings, settingName, _category); + await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); + } } From 068803615b6a202fb87d9db1471f1db9f2aeb287 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Fri, 22 May 2020 21:29:52 +0200 Subject: [PATCH 260/265] Notification job fill mail subject --- Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs index bd667486..b3a3e7b2 100644 --- a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs +++ b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs @@ -66,7 +66,7 @@ namespace Oqtane.Infrastructure { MailMessage mailMessage = new MailMessage(); mailMessage.From = new MailAddress(settings["SMTPUsername"], site.Name); - + mailMessage.Subject = notification.Subject; if (notification.FromUserId != null) { mailMessage.Body = "From: " + notification.FromUser.DisplayName + "<" + notification.FromUser.Email + ">" + "\n"; From 3339690e2acf154cc062cfe63dc15f25fb2c7003 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sun, 24 May 2020 19:11:35 -0400 Subject: [PATCH 261/265] Improvements to ModuleCreator external template to use Package references and include framework in Nuspec file --- Oqtane.Client/UI/SiteRouter.razor | 2 +- Oqtane.Package/Oqtane.Client.nuspec | 23 +++++++++++++++++++ Oqtane.Package/Oqtane.Framework.nuspec | 12 +++++----- Oqtane.Package/Oqtane.Server.nuspec | 23 +++++++++++++++++++ Oqtane.Package/Oqtane.Shared.nuspec | 23 +++++++++++++++++++ Oqtane.Package/pack.cmd | 3 --- Oqtane.Package/release.cmd | 6 +++++ .../Client/[Owner].[Module]s.Client.csproj | 8 ++----- .../External/Package/[Owner].[Module]s.nuspec | 12 +++++----- .../Server/[Owner].[Module]s.Server.csproj | 9 ++------ .../Shared/[Owner].[Module]s.Shared.csproj | 4 +--- 11 files changed, 93 insertions(+), 32 deletions(-) create mode 100644 Oqtane.Package/Oqtane.Client.nuspec create mode 100644 Oqtane.Package/Oqtane.Server.nuspec create mode 100644 Oqtane.Package/Oqtane.Shared.nuspec delete mode 100644 Oqtane.Package/pack.cmd create mode 100644 Oqtane.Package/release.cmd diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index ee2516c9..f602b83c 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -173,7 +173,7 @@ if (alias.Path != "") { - path = path.Replace(alias.Path + "/", ""); + path = path.Substring(alias.Path.Length + 1); } // extract admin route elements from path diff --git a/Oqtane.Package/Oqtane.Client.nuspec b/Oqtane.Package/Oqtane.Client.nuspec new file mode 100644 index 00000000..72947da2 --- /dev/null +++ b/Oqtane.Package/Oqtane.Client.nuspec @@ -0,0 +1,23 @@ + + + + Oqtane.Client + 1.0.0 + Shaun Walker + .NET Foundation + Oqtane Framework + A modular application framework for Blazor + .NET Foundation + false + MIT + https://github.com/oqtane/oqtane.framework + https://www.oqtane.org/Portals/0/icon.jpg + oqtane framework + Initial Release + A modular application framework for Blazor + + + + + + \ No newline at end of file diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec index cf35e229..b5e83d5a 100644 --- a/Oqtane.Package/Oqtane.Framework.nuspec +++ b/Oqtane.Package/Oqtane.Framework.nuspec @@ -17,12 +17,12 @@ A modular application framework for Blazor - - - - - - + + + + + + \ No newline at end of file diff --git a/Oqtane.Package/Oqtane.Server.nuspec b/Oqtane.Package/Oqtane.Server.nuspec new file mode 100644 index 00000000..c6f9dd6e --- /dev/null +++ b/Oqtane.Package/Oqtane.Server.nuspec @@ -0,0 +1,23 @@ + + + + Oqtane.Server + 1.0.0 + Shaun Walker + .NET Foundation + Oqtane Framework + A modular application framework for Blazor + .NET Foundation + false + MIT + https://github.com/oqtane/oqtane.framework + https://www.oqtane.org/Portals/0/icon.jpg + oqtane framework + Initial Release + A modular application framework for Blazor + + + + + + \ No newline at end of file diff --git a/Oqtane.Package/Oqtane.Shared.nuspec b/Oqtane.Package/Oqtane.Shared.nuspec new file mode 100644 index 00000000..8ac8924f --- /dev/null +++ b/Oqtane.Package/Oqtane.Shared.nuspec @@ -0,0 +1,23 @@ + + + + Oqtane.Shared + 1.0.0 + Shaun Walker + .NET Foundation + Oqtane Framework + A modular application framework for Blazor + .NET Foundation + false + MIT + https://github.com/oqtane/oqtane.framework + https://www.oqtane.org/Portals/0/icon.jpg + oqtane framework + Initial Release + A modular application framework for Blazor + + + + + + \ No newline at end of file diff --git a/Oqtane.Package/pack.cmd b/Oqtane.Package/pack.cmd deleted file mode 100644 index aea8d441..00000000 --- a/Oqtane.Package/pack.cmd +++ /dev/null @@ -1,3 +0,0 @@ -DEL "*.nupkg" -nuget.exe pack Oqtane.Framework.nuspec - \ No newline at end of file diff --git a/Oqtane.Package/release.cmd b/Oqtane.Package/release.cmd new file mode 100644 index 00000000..fef9bc80 --- /dev/null +++ b/Oqtane.Package/release.cmd @@ -0,0 +1,6 @@ +DEL "*.nupkg" +nuget.exe pack Oqtane.Framework.nuspec +nuget.exe pack Oqtane.Client.nuspec +nuget.exe pack Oqtane.Server.nuspec +nuget.exe pack Oqtane.Shared.nuspec + \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].[Module]s.Client.csproj b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].[Module]s.Client.csproj index d0fc65b9..2cd5a042 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].[Module]s.Client.csproj +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].[Module]s.Client.csproj @@ -25,12 +25,8 @@ - - ..\..\[RootFolder]\Oqtane.Client\bin\Debug\netstandard2.1\Oqtane.Client.dll - - - ..\..\[RootFolder]\Oqtane.Client\bin\Debug\netstandard2.1\Oqtane.Shared.dll - + + diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].[Module]s.nuspec b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].[Module]s.nuspec index 72d74777..6fa789e8 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].[Module]s.nuspec +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].[Module]s.nuspec @@ -20,12 +20,12 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].[Module]s.Server.csproj b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].[Module]s.Server.csproj index 6668cf0f..4d0ae486 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].[Module]s.Server.csproj +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].[Module]s.Server.csproj @@ -31,12 +31,7 @@ - - ..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\Oqtane.Server.dll - - - ..\..\[RootFolder]\Oqtane.Server\bin\Debug\netcoreapp3.1\Oqtane.Shared.dll - + + - diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Shared/[Owner].[Module]s.Shared.csproj b/Oqtane.Server/wwwroot/Modules/Templates/External/Shared/[Owner].[Module]s.Shared.csproj index fb4d620d..72da9ca8 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Shared/[Owner].[Module]s.Shared.csproj +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Shared/[Owner].[Module]s.Shared.csproj @@ -17,9 +17,7 @@ - - ..\..\[RootFolder]\Oqtane.Shared\bin\Debug\netstandard2.1\Oqtane.Shared.dll - + From 2f272ef4b6fa42d1a61619932c2af722d0e4a81e Mon Sep 17 00:00:00 2001 From: Cody Date: Sun, 24 May 2020 17:47:27 -0700 Subject: [PATCH 262/265] style module actions dropdown text #fff --- .../wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css index 693c8e7f..b8056c79 100644 --- a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css +++ b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.OqtaneTheme/Theme.css @@ -46,6 +46,10 @@ body { margin-left: auto; } +div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu { + color:#ffffff; +} + @media (max-width: 767px) { .app-menu { @@ -76,4 +80,4 @@ body { position: relative; top: 60px; } -} \ No newline at end of file +} From 97df6736091e7d0cccfa6079eba2fed7926ade1c Mon Sep 17 00:00:00 2001 From: Jim Spillane Date: Sun, 24 May 2020 23:04:55 -0400 Subject: [PATCH 263/265] Change JavaScript namespace from interop to Oqtane --- .../Modules/Controls/RichTextEditorInterop.cs | 14 +++++------ Oqtane.Client/UI/Interop.cs | 24 +++++++++---------- Oqtane.Server/wwwroot/js/interop.js | 4 +++- Oqtane.Server/wwwroot/js/quill-interop.js | 4 +++- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/RichTextEditorInterop.cs b/Oqtane.Client/Modules/Controls/RichTextEditorInterop.cs index c9b442d4..a5bd4ffa 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditorInterop.cs +++ b/Oqtane.Client/Modules/Controls/RichTextEditorInterop.cs @@ -24,7 +24,7 @@ namespace Oqtane.Modules.Controls try { _jsRuntime.InvokeAsync( - "interop.createQuill", + "Oqtane.RichTextEditor.createQuill", quillElement, toolbar, readOnly, placeholder, theme, debugLevel); return Task.CompletedTask; @@ -40,7 +40,7 @@ namespace Oqtane.Modules.Controls try { return _jsRuntime.InvokeAsync( - "interop.getQuillText", + "Oqtane.RichTextEditor.getQuillText", quillElement); } catch @@ -54,7 +54,7 @@ namespace Oqtane.Modules.Controls try { return _jsRuntime.InvokeAsync( - "interop.getQuillHTML", + "Oqtane.RichTextEditor.getQuillHTML", quillElement); } catch @@ -68,7 +68,7 @@ namespace Oqtane.Modules.Controls try { return _jsRuntime.InvokeAsync( - "interop.getQuillContent", + "Oqtane.RichTextEditor.getQuillContent", quillElement); } catch @@ -82,7 +82,7 @@ namespace Oqtane.Modules.Controls try { _jsRuntime.InvokeAsync( - "interop.loadQuillContent", + "Oqtane.RichTextEditor.loadQuillContent", quillElement, content); return Task.CompletedTask; } @@ -97,7 +97,7 @@ namespace Oqtane.Modules.Controls try { _jsRuntime.InvokeAsync( - "interop.enableQuillEditor", quillElement, mode); + "Oqtane.RichTextEditor.enableQuillEditor", quillElement, mode); return Task.CompletedTask; } catch @@ -111,7 +111,7 @@ namespace Oqtane.Modules.Controls try { _jsRuntime.InvokeAsync( - "interop.insertQuillImage", + "Oqtane.RichTextEditor.insertQuillImage", quillElement, imageUrl); return Task.CompletedTask; } diff --git a/Oqtane.Client/UI/Interop.cs b/Oqtane.Client/UI/Interop.cs index 5c95ade6..c098ad34 100644 --- a/Oqtane.Client/UI/Interop.cs +++ b/Oqtane.Client/UI/Interop.cs @@ -17,7 +17,7 @@ namespace Oqtane.UI try { _jsRuntime.InvokeAsync( - "interop.setCookie", + "Oqtane.Interop.setCookie", name, value, days); return Task.CompletedTask; } @@ -32,7 +32,7 @@ namespace Oqtane.UI try { return _jsRuntime.InvokeAsync( - "interop.getCookie", + "Oqtane.Interop.getCookie", name); } catch @@ -46,7 +46,7 @@ namespace Oqtane.UI try { _jsRuntime.InvokeAsync( - "interop.updateTitle", + "Oqtane.Interop.updateTitle", title); return Task.CompletedTask; } @@ -61,7 +61,7 @@ namespace Oqtane.UI try { _jsRuntime.InvokeAsync( - "interop.includeMeta", + "Oqtane.Interop.includeMeta", id, attribute, name, content); return Task.CompletedTask; } @@ -76,7 +76,7 @@ namespace Oqtane.UI try { _jsRuntime.InvokeAsync( - "interop.includeLink", + "Oqtane.Interop.includeLink", id, rel, url, type, integrity, crossorigin); return Task.CompletedTask; } @@ -91,7 +91,7 @@ namespace Oqtane.UI try { _jsRuntime.InvokeAsync( - "interop.includeScript", + "Oqtane.Interop.includeScript", id, src, content, location, integrity, crossorigin); return Task.CompletedTask; } @@ -106,7 +106,7 @@ namespace Oqtane.UI try { _jsRuntime.InvokeAsync( - "interop.includeLink", + "Oqtane.Interop.includeLink", id, "stylesheet", url, "text/css"); return Task.CompletedTask; } @@ -121,7 +121,7 @@ namespace Oqtane.UI try { _jsRuntime.InvokeAsync( - "interop.removeElementsById", + "Oqtane.Interop.removeElementsById", prefix, first, last); return Task.CompletedTask; } @@ -137,7 +137,7 @@ namespace Oqtane.UI try { return _jsRuntime.InvokeAsync( - "interop.getElementByName", + "Oqtane.Interop.getElementByName", name); } catch @@ -151,7 +151,7 @@ namespace Oqtane.UI try { _jsRuntime.InvokeAsync( - "interop.submitForm", + "Oqtane.Interop.submitForm", path, fields); return Task.CompletedTask; } @@ -166,7 +166,7 @@ namespace Oqtane.UI try { return _jsRuntime.InvokeAsync( - "interop.getFiles", + "Oqtane.Interop.getFiles", id); } catch @@ -180,7 +180,7 @@ namespace Oqtane.UI try { _jsRuntime.InvokeAsync( - "interop.uploadFiles", + "Oqtane.Interop.uploadFiles", posturl, folder, id); return Task.CompletedTask; } diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index 01762d32..4317b07f 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -1,4 +1,6 @@ -window.interop = { +var Oqtane = Oqtane || {}; + +Oqtane.Interop = { setCookie: function (name, value, days) { var d = new Date(); d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000)); diff --git a/Oqtane.Server/wwwroot/js/quill-interop.js b/Oqtane.Server/wwwroot/js/quill-interop.js index 4d34231d..abb1b35f 100644 --- a/Oqtane.Server/wwwroot/js/quill-interop.js +++ b/Oqtane.Server/wwwroot/js/quill-interop.js @@ -1,4 +1,6 @@ -window.interop = { +var Oqtane = Oqtane || {}; + +Oqtane.RichTextEditor = { createQuill: function ( quillElement, toolBar, readOnly, placeholder, theme, debugLevel) { From 795f591da227f827ba4a02bf97fb8e57153ff131 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Tue, 26 May 2020 09:04:15 +0200 Subject: [PATCH 264/265] Generate nugets in correct format --- Oqtane.Client/Oqtane.Client.csproj | 2 +- Oqtane.Package/release.cmd | 9 ++++----- Oqtane.Server/Oqtane.Server.csproj | 3 ++- Oqtane.Upgrade/Oqtane.Upgrade.csproj | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index a551efc9..a712520f 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -17,6 +17,7 @@ Git Not for production use. Oqtane + true @@ -37,5 +38,4 @@ - diff --git a/Oqtane.Package/release.cmd b/Oqtane.Package/release.cmd index fef9bc80..34df2891 100644 --- a/Oqtane.Package/release.cmd +++ b/Oqtane.Package/release.cmd @@ -1,6 +1,5 @@ DEL "*.nupkg" -nuget.exe pack Oqtane.Framework.nuspec -nuget.exe pack Oqtane.Client.nuspec -nuget.exe pack Oqtane.Server.nuspec -nuget.exe pack Oqtane.Shared.nuspec - \ No newline at end of file +dotnet clean -c Release ..\Oqtane.sln +dotnet build -c Release ..\Oqtane.sln +dotnet pack -o .\ -c Release ..\Oqtane.sln +nuget.exe pack Oqtane.Framework.nuspec diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index a26ff88e..ccc27cd2 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -15,6 +15,7 @@ Git Not for production use. Oqtane + true @@ -43,4 +44,4 @@ - \ No newline at end of file + diff --git a/Oqtane.Upgrade/Oqtane.Upgrade.csproj b/Oqtane.Upgrade/Oqtane.Upgrade.csproj index b4d55166..587d5a9d 100644 --- a/Oqtane.Upgrade/Oqtane.Upgrade.csproj +++ b/Oqtane.Upgrade/Oqtane.Upgrade.csproj @@ -15,6 +15,7 @@ Git Not for production use. Oqtane + false From 963148c639a2bbfddbbbad937b8bf8cdd6df89ce Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 27 May 2020 16:03:38 -0400 Subject: [PATCH 265/265] Refactor Javascript and Stylesheet loading --- Oqtane.Client/UI/Interop.cs | 35 ++++++++++----- Oqtane.Client/UI/ThemeBuilder.razor | 28 ++++++------ Oqtane.Package/Oqtane.Client.nuspec | 2 +- Oqtane.Package/Oqtane.Server.nuspec | 2 +- Oqtane.Package/Oqtane.Shared.nuspec | 2 +- Oqtane.Server/Pages/_Host.cshtml | 6 +-- Oqtane.Server/wwwroot/js/interop.js | 69 ++++++++++++++++++++++------- 7 files changed, 97 insertions(+), 47 deletions(-) diff --git a/Oqtane.Client/UI/Interop.cs b/Oqtane.Client/UI/Interop.cs index c098ad34..1c05fe41 100644 --- a/Oqtane.Client/UI/Interop.cs +++ b/Oqtane.Client/UI/Interop.cs @@ -1,4 +1,5 @@ using Microsoft.JSInterop; +using Oqtane.Models; using System.Threading.Tasks; namespace Oqtane.UI @@ -56,13 +57,13 @@ namespace Oqtane.UI } } - public Task IncludeMeta(string id, string attribute, string name, string content) + public Task IncludeMeta(string id, string attribute, string name, string content, string key) { try { _jsRuntime.InvokeAsync( "Oqtane.Interop.includeMeta", - id, attribute, name, content); + id, attribute, name, content, key); return Task.CompletedTask; } catch @@ -71,13 +72,13 @@ namespace Oqtane.UI } } - public Task IncludeLink(string id, string rel, string url, string type, string integrity, string crossorigin) + public Task IncludeLink(string id, string rel, string href, string type, string integrity, string crossorigin, string key) { try { _jsRuntime.InvokeAsync( "Oqtane.Interop.includeLink", - id, rel, url, type, integrity, crossorigin); + id, rel, href, type, integrity, crossorigin, key); return Task.CompletedTask; } catch @@ -86,13 +87,28 @@ namespace Oqtane.UI } } - public Task IncludeScript(string id, string src, string content, string location, string integrity, string crossorigin) + public Task IncludeLinks(object[] links) + { + try + { + _jsRuntime.InvokeAsync( + "Oqtane.Interop.includeLinks", + (object) links); + return Task.CompletedTask; + } + catch + { + return Task.CompletedTask; + } + } + + public Task IncludeScript(string id, string src, string integrity, string crossorigin, string content, string location, string key) { try { _jsRuntime.InvokeAsync( "Oqtane.Interop.includeScript", - id, src, content, location, integrity, crossorigin); + id, src, integrity, crossorigin, content, location, key); return Task.CompletedTask; } catch @@ -101,13 +117,13 @@ namespace Oqtane.UI } } - public Task IncludeCSS(string id, string url) + public Task IncludeScripts(object[] scripts) { try { _jsRuntime.InvokeAsync( - "Oqtane.Interop.includeLink", - id, "stylesheet", url, "text/css"); + "Oqtane.Interop.includeScripts", + (object)scripts); return Task.CompletedTask; } catch @@ -131,7 +147,6 @@ namespace Oqtane.UI } } - public ValueTask GetElementByName(string name) { try diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index ffe91273..2b40e59e 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -23,31 +23,31 @@ await interop.UpdateTitle(PageState.Site.Name + " - " + PageState.Page.Name); } - // update page resources - int stylesheet = 0; - int script = 0; + // include page resources + var links = new List(); + var scripts = new List(); foreach (Resource resource in PageState.Page.Resources) { switch (resource.ResourceType) { case ResourceType.Stylesheet: - stylesheet += 1; - await interop.IncludeLink("app-stylesheet" + stylesheet.ToString("00"), "stylesheet", resource.Url, "text/css", resource.Integrity ?? "", resource.CrossOrigin ?? ""); + links.Add(new { id = "app-stylesheet" + links.Count.ToString("00"), rel = "stylesheet", href = resource.Url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", key = "" }); break; case ResourceType.Script: - script += 1; - await interop.IncludeScript("app-script" + script.ToString("00"), resource.Url, "", "body", resource.Integrity ?? "", resource.CrossOrigin ?? ""); + scripts.Add(new { id = "app-script" + scripts.Count.ToString("00"), src = resource.Url, integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", content = "", location = "body", key = "" }); break; } } - // remove any page resources references which are no longer required for this page - await interop.RemoveElementsById("app-stylesheet", "app-stylesheet" + (stylesheet + 1).ToString("00"), ""); - await interop.RemoveElementsById("app-script", "app-script" + (script + 1).ToString("00"), ""); + await interop.IncludeLinks(links.ToArray()); + await interop.IncludeScripts(scripts.ToArray()); + // remove any page resource references which are no longer required for this page + await interop.RemoveElementsById("app-stylesheet", "app-stylesheet" + links.Count.ToString("00"), ""); + await interop.RemoveElementsById("app-script", "app-script" + scripts.Count.ToString("00"), ""); // add favicon if (PageState.Site.FaviconFileId != null) { - await interop.IncludeLink("fav-icon", "shortcut icon", Utilities.ContentUrl(PageState.Alias, PageState.Site.FaviconFileId.Value), "image/x-icon", "", ""); + await interop.IncludeLink("app-favicon", "shortcut icon", Utilities.ContentUrl(PageState.Alias, PageState.Site.FaviconFileId.Value), "image/x-icon", "", "", "id"); } // add PWA support if (PageState.Site.PwaIsEnabled) @@ -97,10 +97,10 @@ "const serialized = JSON.stringify(manifest); " + "const blob = new Blob([serialized], {type: 'application/javascript'}); " + "const url = URL.createObjectURL(blob); " + - "document.getElementById('pwa-manifest').setAttribute('href', url); " + + "document.getElementById('app-manifest').setAttribute('href', url); " + "} " + ", 1000);"; - await interop.IncludeScript("pwa-manifestscript", "", manifest, "body", "", ""); + await interop.IncludeScript("app-pwa", "", "", "", manifest, "body", "id"); // service worker must be in root of site string serviceworker = "if ('serviceWorker' in navigator) { " + @@ -110,6 +110,6 @@ "console.log('ServiceWorker Registration Failed ', err); " + "}); " + "}"; - await interop.IncludeScript("pwa-serviceworker", "", serviceworker, "body", "", ""); + await interop.IncludeScript("app-serviceworker", "", "", "", serviceworker, "body", "id"); } } diff --git a/Oqtane.Package/Oqtane.Client.nuspec b/Oqtane.Package/Oqtane.Client.nuspec index 72947da2..b826a37d 100644 --- a/Oqtane.Package/Oqtane.Client.nuspec +++ b/Oqtane.Package/Oqtane.Client.nuspec @@ -12,7 +12,7 @@ MIT https://github.com/oqtane/oqtane.framework https://www.oqtane.org/Portals/0/icon.jpg - oqtane framework + oqtane Initial Release A modular application framework for Blazor diff --git a/Oqtane.Package/Oqtane.Server.nuspec b/Oqtane.Package/Oqtane.Server.nuspec index c6f9dd6e..2645e9c2 100644 --- a/Oqtane.Package/Oqtane.Server.nuspec +++ b/Oqtane.Package/Oqtane.Server.nuspec @@ -12,7 +12,7 @@ MIT https://github.com/oqtane/oqtane.framework https://www.oqtane.org/Portals/0/icon.jpg - oqtane framework + oqtane Initial Release A modular application framework for Blazor diff --git a/Oqtane.Package/Oqtane.Shared.nuspec b/Oqtane.Package/Oqtane.Shared.nuspec index 8ac8924f..8419aef2 100644 --- a/Oqtane.Package/Oqtane.Shared.nuspec +++ b/Oqtane.Package/Oqtane.Shared.nuspec @@ -12,7 +12,7 @@ MIT https://github.com/oqtane/oqtane.framework https://www.oqtane.org/Portals/0/icon.jpg - oqtane framework + oqtane Initial Release A modular application framework for Blazor diff --git a/Oqtane.Server/Pages/_Host.cshtml b/Oqtane.Server/Pages/_Host.cshtml index a3ed9580..e8795611 100644 --- a/Oqtane.Server/Pages/_Host.cshtml +++ b/Oqtane.Server/Pages/_Host.cshtml @@ -11,11 +11,11 @@ Oqtane - + - + - + @(Html.AntiForgeryToken()) diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index 4317b07f..f94c3472 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -27,9 +27,9 @@ Oqtane.Interop = { document.title = title; } }, - includeMeta: function (id, attribute, name, content) { + includeMeta: function (id, attribute, name, content, key) { var meta; - if (id !== "") { + if (id !== "" && key === "id") { meta = document.getElementById(id); } else { @@ -50,13 +50,13 @@ Oqtane.Interop = { } } }, - includeLink: function (id, rel, url, type, integrity, crossorigin) { + includeLink: function (id, rel, href, type, integrity, crossorigin, key) { var link; - if (id !== "") { + if (id !== "" && key === "id") { link = document.getElementById(id); } else { - link = document.querySelector("link[href=\"" + CSS.escape(url) + "\"]"); + link = document.querySelector("link[href=\"" + CSS.escape(href) + "\"]"); } if (link === null) { link = document.createElement("link"); @@ -67,7 +67,7 @@ Oqtane.Interop = { if (type !== "") { link.type = type; } - link.href = url; + link.href = href; if (integrity !== "") { link.integrity = integrity; } @@ -87,10 +87,10 @@ Oqtane.Interop = { } else { link.removeAttribute('type'); } - if (link.href !== url) { + if (link.href !== this.getAbsoluteUrl(href)) { link.removeAttribute('integrity'); link.removeAttribute('crossorigin'); - link.setAttribute('href', url); + link.setAttribute('href', href); } if (integrity !== "") { if (link.integrity !== integrity) { @@ -108,11 +108,19 @@ Oqtane.Interop = { } } }, - includeScript: function (id, src, content, location, integrity, crossorigin) { + includeLinks: function (links) { + for (let i = 0; i < links.length; i++) { + this.includeLink(links[i].id, links[i].rel, links[i].href, links[i].type, links[i].integrity, links[i].crossorigin, links[i].key); + } + }, + includeScript: function (id, src, integrity, crossorigin, content, location, key) { var script; - if (id !== "") { + if (id !== "" && key === "id") { script = document.getElementById(id); } + else { + script = document.querySelector("script[src=\"" + CSS.escape(src) + "\"]"); + } if (script === null) { script = document.createElement("script"); if (id !== "") { @@ -131,16 +139,17 @@ Oqtane.Interop = { script.innerHTML = content; } script.async = false; - if (location === 'head') { - document.head.appendChild(script); - } - if (location === 'body') { - document.body.appendChild(script); - } + this.loadScript(script, location) + .then(() => { + console.log(src + ' loaded'); + }) + .catch(() => { + console.error(src + ' failed'); + }); } else { if (src !== "") { - if (script.src !== src) { + if (script.src !== this.getAbsoluteUrl(src)) { script.removeAttribute('integrity'); script.removeAttribute('crossorigin'); script.src = src; @@ -167,6 +176,32 @@ Oqtane.Interop = { } } }, + loadScript: function (script, location) { + if (location === 'head') { + document.head.appendChild(script); + } + if (location === 'body') { + document.body.appendChild(script); + } + + return new Promise((res, rej) => { + script.onload = res(); + script.onerror = rej(); + }); + }, + includeScripts: function (scripts) { + for (let i = 0; i < scripts.length; i++) { + this.includeScript(scripts[i].id, scripts[i].src, scripts[i].integrity, scripts[i].crossorigin, scripts[i].content, scripts[i].location, scripts[i].key); + } + }, + getAbsoluteUrl: function (url) { + var a = document.createElement('a'); + getAbsoluteUrl = function (url) { + a.href = url; + return a.href; + } + return getAbsoluteUrl(url); + }, removeElementsById: function (prefix, first, last) { var elements = document.querySelectorAll('[id^=' + prefix + ']'); for (var i = elements.length - 1; i >= 0; i--) {