From b71f0079813934a1fa7555aad642329236059833 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 14 Aug 2019 09:34:35 -0400 Subject: [PATCH] Support for third party modules, improved error handling, standardardized enum naming, reorganized interface definitions, support for DB script upgrades, added Settings entity --- Oqtane.Client/App.razor | 2 +- Oqtane.Client/Modules/Admin/Login/Index.razor | 71 +++++----- .../Admin/ModuleDefinitions/Index.razor | 2 +- .../Modules/Admin/ModuleSettings/Index.razor | 34 ++++- Oqtane.Client/Modules/Admin/Pages/Add.razor | 2 +- .../Modules/Admin/Pages/Delete.razor | 2 +- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 2 +- Oqtane.Client/Modules/Admin/Pages/Index.razor | 2 +- .../Modules/Admin/Register/Index.razor | 3 +- Oqtane.Client/Modules/Admin/Sites/Add.razor | 2 +- Oqtane.Client/Modules/Admin/Sites/Index.razor | 2 +- .../Modules/Admin/Themes/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Users/Index.razor | 2 +- .../Modules/Controls/ActionLink.razor | 17 ++- .../Modules/Controls/ModuleMessage.razor | 44 +++++++ Oqtane.Client/Modules/HtmlText/Edit.razor | 2 +- Oqtane.Client/Modules/HtmlText/Index.razor | 2 +- Oqtane.Client/Modules/IModuleControl.cs | 2 +- Oqtane.Client/Modules/MessageType.cs | 10 ++ Oqtane.Client/Modules/ModuleBase.cs | 9 +- Oqtane.Client/Modules/SecurityAccessLevel.cs | 2 +- Oqtane.Client/Properties/launchSettings.json | 2 +- .../{ => Interfaces}/IAliasService.cs | 0 .../{ => Interfaces}/IInstallationService.cs | 0 .../IModuleDefinitionService.cs | 0 .../{ => Interfaces}/IModuleService.cs | 0 .../{ => Interfaces}/IPageModuleService.cs | 1 + .../Services/{ => Interfaces}/IPageService.cs | 0 .../Services/Interfaces/ISettingService.cs | 27 ++++ .../Services/{ => Interfaces}/ISiteService.cs | 0 .../{ => Interfaces}/ITenantService.cs | 0 .../IThemeService.cs} | 0 .../Services/{ => Interfaces}/IUserService.cs | 0 .../{ => Interfaces}/InstallationService.cs | 0 Oqtane.Client/Services/PageModuleService.cs | 5 + Oqtane.Client/Services/SettingService.cs | 92 +++++++++++++ Oqtane.Client/Shared/Constants.cs | 1 + Oqtane.Client/Shared/Container.razor | 7 +- Oqtane.Client/Shared/Installer.razor | 119 +++++++++-------- Oqtane.Client/Shared/ModuleInstance.razor | 17 ++- Oqtane.Client/Shared/Pane.razor | 12 +- Oqtane.Client/Shared/SiteRouter.razor | 12 +- Oqtane.Client/Shared/Theme.razor | 7 +- Oqtane.Client/Shared/Utilities.cs | 8 +- Oqtane.Client/Startup.cs | 4 +- Oqtane.Client/Themes/ContainerBase.cs | 7 +- Oqtane.Client/Themes/Controls/Logo.razor | 3 +- Oqtane.Client/Themes/Controls/Menu.razor | 3 +- .../Themes/Controls/ModuleTitle.razor | 3 +- Oqtane.Client/Themes/ThemeBase.cs | 7 +- Oqtane.Client/Themes/ThemeObjectBase.cs | 7 +- .../Controllers/InstallationController.cs | 66 +++++++++- .../Controllers/SettingController.cs | 61 +++++++++ Oqtane.Server/Controllers/UserController.cs | 47 ++++--- Oqtane.Server/Modules/MVCModuleExtension.cs | 30 +++++ Oqtane.Server/Oqtane.Server.csproj | 6 +- .../Repository/{ => Context}/DBContextBase.cs | 0 .../{ => Context}/MasterDBContext.cs | 0 .../{ => Context}/TenantDBContext.cs | 1 + .../{ => Interfaces}/IAliasRepository.cs | 0 .../IModuleDefinitionRepository.cs | 0 .../{ => Interfaces}/IModuleRepository.cs | 0 .../{ => Interfaces}/IPageModuleRepository.cs | 0 .../{ => Interfaces}/IPageRepository.cs | 0 .../Interfaces/ISettingRepository.cs | 14 ++ .../{ => Interfaces}/ISiteRepository.cs | 0 .../{ => Interfaces}/ISiteUserRepository.cs | 0 .../{ => Interfaces}/ITenantRepository.cs | 0 .../{ => Interfaces}/ITenantResolver.cs | 0 .../{ => Interfaces}/IThemeRepository.cs | 0 .../{ => Interfaces}/IUserRepository.cs | 0 .../Repository/ModuleDefinitionRepository.cs | 10 +- Oqtane.Server/Repository/SettingRepository.cs | 85 ++++++++++++ Oqtane.Server/Repository/ThemeRepository.cs | 8 +- .../Scripts/{Tenant.sql => 00.00.00.sql} | 43 ++++++- .../Scripts/{Identity.sql => 00.00.01.sql} | 0 Oqtane.Server/Startup.cs | 121 ++++++++---------- Oqtane.Shared/Models/Setting.cs | 18 +++ 78 files changed, 809 insertions(+), 261 deletions(-) create mode 100644 Oqtane.Client/Modules/Controls/ModuleMessage.razor create mode 100644 Oqtane.Client/Modules/MessageType.cs rename Oqtane.Client/Services/{ => Interfaces}/IAliasService.cs (100%) rename Oqtane.Client/Services/{ => Interfaces}/IInstallationService.cs (100%) rename Oqtane.Client/Services/{ => Interfaces}/IModuleDefinitionService.cs (100%) rename Oqtane.Client/Services/{ => Interfaces}/IModuleService.cs (100%) rename Oqtane.Client/Services/{ => Interfaces}/IPageModuleService.cs (86%) rename Oqtane.Client/Services/{ => Interfaces}/IPageService.cs (100%) create mode 100644 Oqtane.Client/Services/Interfaces/ISettingService.cs rename Oqtane.Client/Services/{ => Interfaces}/ISiteService.cs (100%) rename Oqtane.Client/Services/{ => Interfaces}/ITenantService.cs (100%) rename Oqtane.Client/Services/{IthemeService.cs => Interfaces/IThemeService.cs} (100%) rename Oqtane.Client/Services/{ => Interfaces}/IUserService.cs (100%) rename Oqtane.Client/Services/{ => Interfaces}/InstallationService.cs (100%) create mode 100644 Oqtane.Client/Services/SettingService.cs create mode 100644 Oqtane.Server/Controllers/SettingController.cs create mode 100644 Oqtane.Server/Modules/MVCModuleExtension.cs rename Oqtane.Server/Repository/{ => Context}/DBContextBase.cs (100%) rename Oqtane.Server/Repository/{ => Context}/MasterDBContext.cs (100%) rename Oqtane.Server/Repository/{ => Context}/TenantDBContext.cs (92%) rename Oqtane.Server/Repository/{ => Interfaces}/IAliasRepository.cs (100%) rename Oqtane.Server/Repository/{ => Interfaces}/IModuleDefinitionRepository.cs (100%) rename Oqtane.Server/Repository/{ => Interfaces}/IModuleRepository.cs (100%) rename Oqtane.Server/Repository/{ => Interfaces}/IPageModuleRepository.cs (100%) rename Oqtane.Server/Repository/{ => Interfaces}/IPageRepository.cs (100%) create mode 100644 Oqtane.Server/Repository/Interfaces/ISettingRepository.cs rename Oqtane.Server/Repository/{ => Interfaces}/ISiteRepository.cs (100%) rename Oqtane.Server/Repository/{ => Interfaces}/ISiteUserRepository.cs (100%) rename Oqtane.Server/Repository/{ => Interfaces}/ITenantRepository.cs (100%) rename Oqtane.Server/Repository/{ => Interfaces}/ITenantResolver.cs (100%) rename Oqtane.Server/Repository/{ => Interfaces}/IThemeRepository.cs (100%) rename Oqtane.Server/Repository/{ => Interfaces}/IUserRepository.cs (100%) create mode 100644 Oqtane.Server/Repository/SettingRepository.cs rename Oqtane.Server/Scripts/{Tenant.sql => 00.00.00.sql} (95%) rename Oqtane.Server/Scripts/{Identity.sql => 00.00.01.sql} (100%) create mode 100644 Oqtane.Shared/Models/Setting.cs diff --git a/Oqtane.Client/App.razor b/Oqtane.Client/App.razor index 6ad2be28..8deaf8a0 100644 --- a/Oqtane.Client/App.razor +++ b/Oqtane.Client/App.razor @@ -5,7 +5,7 @@ @if (!Installed) { - + } else { diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index b419ab98..1e0dde3e 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -42,50 +42,51 @@ @code { -public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Anonymous; } } -public string Message { get; set; } = ""; -public string Username { get; set; } = ""; -public string Password { get; set; } = ""; -public bool Remember { get; set; } = false; + public string Message { get; set; } = ""; + public string Username { get; set; } = ""; + public string Password { get; set; } = ""; + public bool Remember { get; set; } = false; -private async Task Login() -{ - User user = new User(); - user.Username = Username; - user.Password = Password; - user.IsPersistent = Remember; - user = await UserService.LoginUserAsync(user); - if (user.IsAuthenticated) + private async Task Login() { - string ReturnUrl = PageState.QueryString["returnurl"]; - - var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); - if (authstateprovider == null) + User user = new User(); + user.SiteId = PageState.Site.SiteId; + user.Username = Username; + user.Password = Password; + user.IsPersistent = Remember; + user = await UserService.LoginUserAsync(user); + if (user.IsAuthenticated) { - // server-side Blazor - var interop = new Interop(jsRuntime); - string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken"); - var fields = new { __RequestVerificationToken = antiforgerytoken, username = Username, password = Password, remember = Remember, returnurl = ReturnUrl }; - await interop.SubmitForm("/login/", fields); + string ReturnUrl = PageState.QueryString["returnurl"]; + + var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); + if (authstateprovider == null) + { + // server-side Blazor + var interop = new Interop(jsRuntime); + string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken"); + var fields = new { __RequestVerificationToken = antiforgerytoken, username = Username, password = Password, remember = Remember, returnurl = ReturnUrl }; + await interop.SubmitForm("/login/", fields); + } + else + { + // client-side Blazor + authstateprovider.NotifyAuthenticationChanged(); + PageState.Reload = Constants.ReloadPage; + UriHelper.NavigateTo(NavigateUrl(ReturnUrl)); + } } else { - // client-side Blazor - authstateprovider.NotifyAuthenticationChanged(); - PageState.Reload = Constants.ReloadPage; - UriHelper.NavigateTo(NavigateUrl(ReturnUrl)); + Message = "
Login Failed. Please Remember That Passwords Are Case Sensitive.
"; } } - else + + private void Cancel() { - Message = "
Login Failed. Please Remember That Passwords Are Case Sensitive.
"; + string ReturnUrl = PageState.QueryString["returnurl"]; + UriHelper.NavigateTo(ReturnUrl); } } - -private void Cancel() -{ - string ReturnUrl = PageState.QueryString["returnurl"]; - UriHelper.NavigateTo(ReturnUrl); -} -} diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor index 55b017d4..475c5e2a 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor @@ -30,7 +30,7 @@ else } @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Host; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } List moduledefinitions; diff --git a/Oqtane.Client/Modules/Admin/ModuleSettings/Index.razor b/Oqtane.Client/Modules/Admin/ModuleSettings/Index.razor index a6e472d4..c0c9925f 100644 --- a/Oqtane.Client/Modules/Admin/ModuleSettings/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleSettings/Index.razor @@ -63,11 +63,15 @@ + +@DynamicComponent + Cancel + @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Edit; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Edit; } } public override string Title { get { return "Module Settings"; } } Dictionary containers = new Dictionary(); @@ -77,6 +81,9 @@ string editpermissions; string pageid; + RenderFragment DynamicComponent { get; set; } + object settings; + protected override async Task OnInitAsync() { title = ModuleState.Title; @@ -85,6 +92,17 @@ viewpermissions = ModuleState.ViewPermissions; editpermissions = ModuleState.EditPermissions; pageid = ModuleState.PageId.ToString(); + + DynamicComponent = builder => + { + Type moduleType = Type.GetType(ModuleState.ModuleType); + if (moduleType != null) + { + builder.OpenComponent(0, moduleType); + builder.AddComponentReferenceCapture(1, inst => { settings = Convert.ChangeType(inst, moduleType); }); + builder.CloseComponent(); + } + }; } private async Task SaveModule() @@ -94,17 +112,19 @@ module.EditPermissions = editpermissions; await ModuleService.UpdateModuleAsync(module); - PageModule pagemodule = new PageModule(); - pagemodule.PageModuleId = ModuleState.PageModuleId; - pagemodule.PageId = Int32.Parse(pageid); - pagemodule.ModuleId = ModuleState.ModuleId; + PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); pagemodule.Title = title; - pagemodule.Pane = ModuleState.Pane; - pagemodule.Order = ModuleState.Order; pagemodule.ContainerType = containertype; await PageModuleService.UpdatePageModuleAsync(pagemodule); + Type moduleType = Type.GetType(ModuleState.ModuleType); + if (moduleType != null) + { + moduleType.GetMethod("UpdateSettings").Invoke(settings, null); // method must be public in settings component + } + PageState.Reload = Constants.ReloadPage; UriHelper.NavigateTo(NavigateUrl()); } + } diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 2337a59e..d808e08b 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -115,7 +115,7 @@ Cancel @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Admin; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } Dictionary themes = new Dictionary(); Dictionary panelayouts = new Dictionary(); diff --git a/Oqtane.Client/Modules/Admin/Pages/Delete.razor b/Oqtane.Client/Modules/Admin/Pages/Delete.razor index 964b1f32..e3ef602a 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Delete.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Delete.razor @@ -115,7 +115,7 @@ Cancel @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Admin; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } Dictionary themes = new Dictionary(); Dictionary panelayouts = new Dictionary(); diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 269695b9..c5734f56 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -116,7 +116,7 @@ Cancel @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Admin; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } Dictionary themes = new Dictionary(); Dictionary panelayouts = new Dictionary(); diff --git a/Oqtane.Client/Modules/Admin/Pages/Index.razor b/Oqtane.Client/Modules/Admin/Pages/Index.razor index 73c6856f..e1c55bf9 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Index.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Index.razor @@ -36,5 +36,5 @@ else } @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Admin; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } } } \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index 4a5a680f..a3ed92a7 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -20,7 +20,7 @@ @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Anonymous; } } public string Username { get; set; } = ""; public string Password { get; set; } = ""; @@ -28,6 +28,7 @@ private async Task RegisterUser() { User user = new User(); + user.SiteId = PageState.Site.SiteId; user.Username = Username; user.DisplayName = Username; user.Roles = "Administrators;"; diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 9db5663c..e67505c0 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -60,7 +60,7 @@ else } @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Host; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } List tenants; string tenantid; diff --git a/Oqtane.Client/Modules/Admin/Sites/Index.razor b/Oqtane.Client/Modules/Admin/Sites/Index.razor index bc54b094..264f3987 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Index.razor @@ -31,7 +31,7 @@ else } @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Host; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } List sites; diff --git a/Oqtane.Client/Modules/Admin/Themes/Index.razor b/Oqtane.Client/Modules/Admin/Themes/Index.razor index 4c606d1a..c06f1ba4 100644 --- a/Oqtane.Client/Modules/Admin/Themes/Index.razor +++ b/Oqtane.Client/Modules/Admin/Themes/Index.razor @@ -29,7 +29,7 @@ else } @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Host; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } List Themes; diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index 5445251b..cb0ca1a6 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -30,7 +30,7 @@ else } @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Host; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } } List Users; diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index bccc2d14..0954b864 100644 --- a/Oqtane.Client/Modules/Controls/ActionLink.razor +++ b/Oqtane.Client/Modules/Controls/ActionLink.razor @@ -54,23 +54,26 @@ if (moduleType != null) { var moduleobject = Activator.CreateInstance(moduleType); - SecurityAccessLevelEnum SecurityAccessLevel = (SecurityAccessLevelEnum)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); + SecurityAccessLevel SecurityAccessLevel = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); switch (SecurityAccessLevel) { - case SecurityAccessLevelEnum.Anonymous: + case SecurityAccessLevel.Anonymous: authorized = true; break; - case SecurityAccessLevelEnum.View: + case SecurityAccessLevel.View: authorized = UserService.IsAuthorized(PageState.User, ModuleState.ViewPermissions); break; - case SecurityAccessLevelEnum.Edit: + case SecurityAccessLevel.Edit: authorized = UserService.IsAuthorized(PageState.User, ModuleState.EditPermissions); break; - case SecurityAccessLevelEnum.Admin: + case SecurityAccessLevel.Admin: authorized = UserService.IsAuthorized(PageState.User, Constants.AdminRole); break; - case SecurityAccessLevelEnum.Host: - authorized = PageState.User.IsSuperUser; + case SecurityAccessLevel.Host: + if (PageState.User != null) + { + authorized = PageState.User.IsSuperUser; + } break; } } diff --git a/Oqtane.Client/Modules/Controls/ModuleMessage.razor b/Oqtane.Client/Modules/Controls/ModuleMessage.razor new file mode 100644 index 00000000..3aec02f3 --- /dev/null +++ b/Oqtane.Client/Modules/Controls/ModuleMessage.razor @@ -0,0 +1,44 @@ +@using Oqtane.Modules +@inherits ModuleBase + +@if (authorized) +{ +
+ @Message +
+} + +@code { + [Parameter] + private MessageType Type { get; set; } + + [Parameter] + private string Message { get; set; } + + string type = "alert alert-success"; // optional + bool authorized = false; + + protected override void OnInit() + { + if (PageState.User != null) + { + authorized = PageState.User.IsSuperUser; + } + + switch (Type) + { + case MessageType.Success: + type = "alert alert-success"; + break; + case MessageType.Info: + type = "alert alert-info"; + break; + case MessageType.Warning: + type = "alert alert-warning"; + break; + case MessageType.Error: + type = "alert alert-danger"; + break; + } + } +} diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor index 2668df87..5f34a2a6 100644 --- a/Oqtane.Client/Modules/HtmlText/Edit.razor +++ b/Oqtane.Client/Modules/HtmlText/Edit.razor @@ -25,7 +25,7 @@ @code { - public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Edit; } } + public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Edit; } } public override string Title { get { return "Edit Html/Text"; } } HtmlTextInfo htmltext; diff --git a/Oqtane.Client/Modules/HtmlText/Index.razor b/Oqtane.Client/Modules/HtmlText/Index.razor index 90d86153..e39d1148 100644 --- a/Oqtane.Client/Modules/HtmlText/Index.razor +++ b/Oqtane.Client/Modules/HtmlText/Index.razor @@ -16,7 +16,7 @@ @code { string content; - protected override async Task OnInitAsync() + protected override async Task OnParametersSetAsync() { HtmlTextService htmltextservice = new HtmlTextService(http, sitestate, UriHelper); List htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId); diff --git a/Oqtane.Client/Modules/IModuleControl.cs b/Oqtane.Client/Modules/IModuleControl.cs index 7f1eb4b6..ebb8dcd4 100644 --- a/Oqtane.Client/Modules/IModuleControl.cs +++ b/Oqtane.Client/Modules/IModuleControl.cs @@ -3,7 +3,7 @@ public interface IModuleControl { string Title { get; } - SecurityAccessLevelEnum SecurityAccessLevel { get; } + SecurityAccessLevel SecurityAccessLevel { get; } string Actions { get; } // can be specified as a comma delimited set of values } } diff --git a/Oqtane.Client/Modules/MessageType.cs b/Oqtane.Client/Modules/MessageType.cs new file mode 100644 index 00000000..a78f95f9 --- /dev/null +++ b/Oqtane.Client/Modules/MessageType.cs @@ -0,0 +1,10 @@ +namespace Oqtane.Modules +{ + public enum MessageType + { + Success, + Info, + Warning, + Error + } +} diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index d654eabd..06948293 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -14,7 +14,7 @@ namespace Oqtane.Modules public virtual string Title { get { return ""; } } - public virtual SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.View; } set { } } // default security + public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security public virtual string Actions { get { return ""; } } @@ -25,7 +25,12 @@ namespace Oqtane.Modules public string NavigateUrl(string path) { - return Utilities.NavigateUrl(PageState.Alias.Path, path); + return NavigateUrl(path, ""); + } + + public string NavigateUrl(string path, string parameters) + { + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); } public string EditUrl(string action) diff --git a/Oqtane.Client/Modules/SecurityAccessLevel.cs b/Oqtane.Client/Modules/SecurityAccessLevel.cs index 241507f8..abaf4f6e 100644 --- a/Oqtane.Client/Modules/SecurityAccessLevel.cs +++ b/Oqtane.Client/Modules/SecurityAccessLevel.cs @@ -1,6 +1,6 @@ namespace Oqtane.Modules { - public enum SecurityAccessLevelEnum + public enum SecurityAccessLevel { Anonymous, View, diff --git a/Oqtane.Client/Properties/launchSettings.json b/Oqtane.Client/Properties/launchSettings.json index 2b36d1d7..4899842e 100644 --- a/Oqtane.Client/Properties/launchSettings.json +++ b/Oqtane.Client/Properties/launchSettings.json @@ -21,7 +21,7 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "applicationUrl": "http://localhost:14246/" + "applicationUrl": "http://localhost:44358/" } } } \ No newline at end of file diff --git a/Oqtane.Client/Services/IAliasService.cs b/Oqtane.Client/Services/Interfaces/IAliasService.cs similarity index 100% rename from Oqtane.Client/Services/IAliasService.cs rename to Oqtane.Client/Services/Interfaces/IAliasService.cs diff --git a/Oqtane.Client/Services/IInstallationService.cs b/Oqtane.Client/Services/Interfaces/IInstallationService.cs similarity index 100% rename from Oqtane.Client/Services/IInstallationService.cs rename to Oqtane.Client/Services/Interfaces/IInstallationService.cs diff --git a/Oqtane.Client/Services/IModuleDefinitionService.cs b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs similarity index 100% rename from Oqtane.Client/Services/IModuleDefinitionService.cs rename to Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs diff --git a/Oqtane.Client/Services/IModuleService.cs b/Oqtane.Client/Services/Interfaces/IModuleService.cs similarity index 100% rename from Oqtane.Client/Services/IModuleService.cs rename to Oqtane.Client/Services/Interfaces/IModuleService.cs diff --git a/Oqtane.Client/Services/IPageModuleService.cs b/Oqtane.Client/Services/Interfaces/IPageModuleService.cs similarity index 86% rename from Oqtane.Client/Services/IPageModuleService.cs rename to Oqtane.Client/Services/Interfaces/IPageModuleService.cs index e574ad34..9efd8183 100644 --- a/Oqtane.Client/Services/IPageModuleService.cs +++ b/Oqtane.Client/Services/Interfaces/IPageModuleService.cs @@ -7,6 +7,7 @@ namespace Oqtane.Services public interface IPageModuleService { Task> GetPageModulesAsync(); + Task GetPageModuleAsync(int PageModuleId); Task AddPageModuleAsync(PageModule PageModule); Task UpdatePageModuleAsync(PageModule PageModule); Task DeletePageModuleAsync(int PageModuleId); diff --git a/Oqtane.Client/Services/IPageService.cs b/Oqtane.Client/Services/Interfaces/IPageService.cs similarity index 100% rename from Oqtane.Client/Services/IPageService.cs rename to Oqtane.Client/Services/Interfaces/IPageService.cs diff --git a/Oqtane.Client/Services/Interfaces/ISettingService.cs b/Oqtane.Client/Services/Interfaces/ISettingService.cs new file mode 100644 index 00000000..aa9834fd --- /dev/null +++ b/Oqtane.Client/Services/Interfaces/ISettingService.cs @@ -0,0 +1,27 @@ +using Oqtane.Models; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Oqtane.Services +{ + public interface ISettingService + { + Task> GetModuleSettingsAsync(int ModuleId); + + Task UpdateModuleSettingsAsync(List ModuleSettings, int ModuleId, string SettingName, string SettingValue); + + + Task> GetSettingsAsync(string EntityName, int EntityId); + + Task GetSettingAsync(int SettingId); + + Task AddSettingAsync(Setting Setting); + + Task UpdateSettingAsync(Setting Setting); + + Task DeleteSettingAsync(int SettingId); + + + string GetSetting(List Settings, string SettingName, string DefaultValue); + } +} diff --git a/Oqtane.Client/Services/ISiteService.cs b/Oqtane.Client/Services/Interfaces/ISiteService.cs similarity index 100% rename from Oqtane.Client/Services/ISiteService.cs rename to Oqtane.Client/Services/Interfaces/ISiteService.cs diff --git a/Oqtane.Client/Services/ITenantService.cs b/Oqtane.Client/Services/Interfaces/ITenantService.cs similarity index 100% rename from Oqtane.Client/Services/ITenantService.cs rename to Oqtane.Client/Services/Interfaces/ITenantService.cs diff --git a/Oqtane.Client/Services/IthemeService.cs b/Oqtane.Client/Services/Interfaces/IThemeService.cs similarity index 100% rename from Oqtane.Client/Services/IthemeService.cs rename to Oqtane.Client/Services/Interfaces/IThemeService.cs diff --git a/Oqtane.Client/Services/IUserService.cs b/Oqtane.Client/Services/Interfaces/IUserService.cs similarity index 100% rename from Oqtane.Client/Services/IUserService.cs rename to Oqtane.Client/Services/Interfaces/IUserService.cs diff --git a/Oqtane.Client/Services/InstallationService.cs b/Oqtane.Client/Services/Interfaces/InstallationService.cs similarity index 100% rename from Oqtane.Client/Services/InstallationService.cs rename to Oqtane.Client/Services/Interfaces/InstallationService.cs diff --git a/Oqtane.Client/Services/PageModuleService.cs b/Oqtane.Client/Services/PageModuleService.cs index 3c7dbf66..fb6390e1 100644 --- a/Oqtane.Client/Services/PageModuleService.cs +++ b/Oqtane.Client/Services/PageModuleService.cs @@ -31,6 +31,11 @@ namespace Oqtane.Services return await http.GetJsonAsync>(apiurl); } + public async Task GetPageModuleAsync(int PageModuleId) + { + return await http.GetJsonAsync(apiurl + "/" + PageModuleId.ToString()); + } + public async Task AddPageModuleAsync(PageModule PageModule) { return await http.PostJsonAsync(apiurl, PageModule); diff --git a/Oqtane.Client/Services/SettingService.cs b/Oqtane.Client/Services/SettingService.cs new file mode 100644 index 00000000..cea5f994 --- /dev/null +++ b/Oqtane.Client/Services/SettingService.cs @@ -0,0 +1,92 @@ +using Oqtane.Models; +using System.Threading.Tasks; +using System.Net.Http; +using System.Linq; +using Microsoft.AspNetCore.Components; +using System.Collections.Generic; +using Oqtane.Shared; +using System; + +namespace Oqtane.Services +{ + public class SettingService : ServiceBase, ISettingService + { + private readonly HttpClient http; + private readonly SiteState sitestate; + private readonly IUriHelper urihelper; + + public SettingService(HttpClient http, SiteState sitestate, IUriHelper urihelper) + { + this.http = http; + this.sitestate = sitestate; + this.urihelper = urihelper; + } + + private string apiurl + { + get { return CreateApiUrl(sitestate.Alias, urihelper.GetAbsoluteUri(), "Setting"); } + } + + public async Task> GetModuleSettingsAsync(int ModuleId) + { + return await GetSettingsAsync("Module", ModuleId); + } + + public async Task UpdateModuleSettingsAsync(List ModuleSettings, int ModuleId, string SettingName, string SettingValue) + { + Setting setting = ModuleSettings.Where(item => item.SettingName == SettingName).FirstOrDefault(); + if (setting == null) + { + setting = new Setting(); + setting.EntityName = "Module"; + setting.EntityId = ModuleId; + setting.SettingName = SettingName; + setting.SettingValue = SettingValue; + setting = await AddSettingAsync(setting); + } + else + { + setting.SettingValue = SettingValue; + setting = await UpdateSettingAsync(setting); + } + return setting; + } + + public async Task> GetSettingsAsync(string EntityName, int EntityId) + { + List Settings = await http.GetJsonAsync>(apiurl + "?entityname=" + EntityName + "&entityid=" + EntityId.ToString()); + return Settings.OrderBy(item => item.SettingName).ToList(); + } + + public async Task GetSettingAsync(int SettingId) + { + return await http.GetJsonAsync(apiurl + "/" + SettingId.ToString()); + } + + public async Task AddSettingAsync(Setting Setting) + { + return await http.PostJsonAsync(apiurl, Setting); + } + + public async Task UpdateSettingAsync(Setting Setting) + { + return await http.PutJsonAsync(apiurl + "/" + Setting.SettingId.ToString(), Setting); + } + public async Task DeleteSettingAsync(int SettingId) + { + await http.DeleteAsync(apiurl + "/" + SettingId.ToString()); + } + + + public string GetSetting(List Settings, string SettingName, string DefaultValue) + { + string value = DefaultValue; + Setting setting = Settings.Where(item => item.SettingName == SettingName).FirstOrDefault(); + if (setting != null) + { + value = setting.SettingValue; + } + return value; + } + } +} diff --git a/Oqtane.Client/Shared/Constants.cs b/Oqtane.Client/Shared/Constants.cs index 80d3070a..4c63ff43 100644 --- a/Oqtane.Client/Shared/Constants.cs +++ b/Oqtane.Client/Shared/Constants.cs @@ -7,6 +7,7 @@ public const string DefaultAdminContainer = "Oqtane.Client.Themes.AdminContainer, Oqtane.Client"; public const string DefaultSettingsControl = "Oqtane.Client.Modules.Admin.ModuleSettings.Index, Oqtane.Client"; public const string PageManagementModule = "Oqtane.Client.Modules.Admin.Pages, Oqtane.Client"; + public const string ModuleMessageControl = "Oqtane.Client.Modules.Controls.ModuleMessage, Oqtane.Client"; public const string DefaultControl = "Index"; public const string AdminPane = "Admin"; diff --git a/Oqtane.Client/Shared/Container.razor b/Oqtane.Client/Shared/Container.razor index 0efcdcc3..a1b17bb5 100644 --- a/Oqtane.Client/Shared/Container.razor +++ b/Oqtane.Client/Shared/Container.razor @@ -1,5 +1,6 @@ @using Oqtane.Models @using Oqtane.Shared +@using Oqtane.Modules @DynamicComponent @@ -26,12 +27,16 @@ Type containerType = Type.GetType(container); if (containerType != null) { - builder.OpenComponent(ModuleState.ModuleId, containerType); // set sequence to moduleid so that component tree is able to differentiate + builder.OpenComponent(0, containerType); builder.CloseComponent(); } else { // container does not exist with type specified + builder.OpenComponent(0, Type.GetType(Constants.ModuleMessageControl)); + builder.AddAttribute(1, "Type", MessageType.Error); + builder.AddAttribute(2, "Message", "Error Loading Module Container " + container); + builder.CloseComponent(); } } }; diff --git a/Oqtane.Client/Shared/Installer.razor b/Oqtane.Client/Shared/Installer.razor index 1c0f497a..7950974a 100644 --- a/Oqtane.Client/Shared/Installer.razor +++ b/Oqtane.Client/Shared/Installer.razor @@ -113,85 +113,80 @@ } @code { + [Parameter] + private bool Installed { get; set; } -private string DatabaseType = "LocalDB"; -private string ServerName = "(LocalDb)\\MSSQLLocalDB"; -private string DatabaseName = "Oqtane-" + DateTime.Now.ToString("yyyyMMddHHmm"); -private string Username = ""; -private string Password = ""; -private string HostUsername = "host"; -private string HostPassword = ""; -private string Message = ""; + private string DatabaseType = "LocalDB"; + private string ServerName = "(LocalDb)\\MSSQLLocalDB"; + private string DatabaseName = "Oqtane-" + DateTime.Now.ToString("yyyyMMddHHmm"); + private string Username = ""; + private string Password = ""; + private string HostUsername = "host"; + private string HostPassword = ""; + private string Message = ""; -private string IntegratedSecurityDisplay = "display:none;"; -private string LoadingDisplay = "display:none;"; + private string IntegratedSecurityDisplay = "display:none;"; + private string LoadingDisplay = "display:none;"; -private bool Installed = true; - -protected override async Task OnInitAsync() -{ - var response = await InstallationService.IsInstalled(); - Installed = response.Success; -} - -private void SetIntegratedSecurity(UIChangeEventArgs e) -{ - if (Convert.ToBoolean(e.Value)) + private void SetIntegratedSecurity(UIChangeEventArgs e) { - IntegratedSecurityDisplay = "display:none;"; - } - else - { - IntegratedSecurityDisplay = ""; - } -} - -private async Task Install() -{ - if (HostPassword.Length >= 6) - { - LoadingDisplay = ""; - StateHasChanged(); - - string connectionstring = ""; - if (DatabaseType == "LocalDB") + if (Convert.ToBoolean(e.Value)) { - connectionstring = "Data Source=" + ServerName + ";AttachDbFilename=|DataDirectory|\\" + DatabaseName + ".mdf;Initial Catalog=" + DatabaseName + ";Integrated Security=SSPI;"; + IntegratedSecurityDisplay = "display:none;"; } else { - connectionstring = "Data Source=" + ServerName + ";Initial Catalog=" + DatabaseName + ";"; - if (IntegratedSecurityDisplay == "display:none;") + IntegratedSecurityDisplay = ""; + } + } + + private async Task Install() + { + if (HostPassword.Length >= 6) + { + LoadingDisplay = ""; + StateHasChanged(); + + string connectionstring = ""; + if (DatabaseType == "LocalDB") { - connectionstring += "Integrated Security=SSPI;"; + connectionstring = "Data Source=" + ServerName + ";AttachDbFilename=|DataDirectory|\\" + DatabaseName + ".mdf;Initial Catalog=" + DatabaseName + ";Integrated Security=SSPI;"; } else { - connectionstring += "User ID=" + Username + ";Password=" + Password; + connectionstring = "Data Source=" + ServerName + ";Initial Catalog=" + DatabaseName + ";"; + if (IntegratedSecurityDisplay == "display:none;") + { + connectionstring += "Integrated Security=SSPI;"; + } + else + { + connectionstring += "User ID=" + Username + ";Password=" + Password; + } + } + GenericResponse response = await InstallationService.Install(connectionstring); + if (response.Success) + { + User user = new User(); + user.SiteId = 1; + user.Username = HostUsername; + user.DisplayName = HostUsername; + user.Password = HostPassword; + user.IsSuperUser = true; + user.Roles = ""; + await UserService.AddUserAsync(user); + UriHelper.NavigateTo("", true); + } + else + { + Message = "
" + response.Message + "
"; + LoadingDisplay = "display:none;"; } - } - GenericResponse response = await InstallationService.Install(connectionstring); - if (response.Success) - { - User user = new User(); - user.Username = HostUsername; - user.DisplayName = HostUsername; - user.Password = HostPassword; - user.IsSuperUser = true; - user.Roles = ""; - await UserService.AddUserAsync(user); - UriHelper.NavigateTo("", true); } else { - Message = "
" + response.Message + "
"; - LoadingDisplay = "display:none;"; + Message = "
Password Must Be 6 Characters Or Greater
"; } } - else - { - Message = "
Password Must Be 6 Characters Or Greater
"; - } -} } diff --git a/Oqtane.Client/Shared/ModuleInstance.razor b/Oqtane.Client/Shared/ModuleInstance.razor index a1fb24ab..5f75ac6c 100644 --- a/Oqtane.Client/Shared/ModuleInstance.razor +++ b/Oqtane.Client/Shared/ModuleInstance.razor @@ -1,5 +1,6 @@ @using Oqtane.Models @using Oqtane.Shared +@using Oqtane.Modules @DynamicComponent @@ -18,18 +19,26 @@ { string typename = ModuleState.ModuleType; if (PageState.Control == "Settings") // module settings are a core component - { + { typename = Constants.DefaultSettingsControl; } - Type moduleType = Type.GetType(typename); + Type moduleType = null; + if (typename != null) + { + moduleType = Type.GetType(typename); + } if (moduleType != null) { - builder.OpenComponent(ModuleState.ModuleId, moduleType); // set sequence to moduleid so that component tree is able to differentiate + builder.OpenComponent(0, moduleType); builder.CloseComponent(); } else { - // module control does not exist with typename specified + // module does not exist with typename specified + builder.OpenComponent(0, Type.GetType(Constants.ModuleMessageControl)); + builder.AddAttribute(1, "Type", MessageType.Error); + builder.AddAttribute(2, "Message", "Error Loading Component For Module " + ModuleState.ModuleDefinitionName); + builder.CloseComponent(); } }; } diff --git a/Oqtane.Client/Shared/Pane.razor b/Oqtane.Client/Shared/Pane.razor index 89776819..42d41431 100644 --- a/Oqtane.Client/Shared/Pane.razor +++ b/Oqtane.Client/Shared/Pane.razor @@ -52,23 +52,23 @@ { var moduleobject = Activator.CreateInstance(moduleType); // verify security access level for this module control - SecurityAccessLevelEnum SecurityAccessLevel = (SecurityAccessLevelEnum)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); + SecurityAccessLevel SecurityAccessLevel = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); bool authorized = false; switch (SecurityAccessLevel) { - case SecurityAccessLevelEnum.Anonymous: + case SecurityAccessLevel.Anonymous: authorized = true; break; - case SecurityAccessLevelEnum.View: + case SecurityAccessLevel.View: authorized = UserService.IsAuthorized(PageState.User, module.ViewPermissions); break; - case SecurityAccessLevelEnum.Edit: + case SecurityAccessLevel.Edit: authorized = UserService.IsAuthorized(PageState.User, module.EditPermissions); break; - case SecurityAccessLevelEnum.Admin: + case SecurityAccessLevel.Admin: authorized = UserService.IsAuthorized(PageState.User, Constants.AdminRole); break; - case SecurityAccessLevelEnum.Host: + case SecurityAccessLevel.Host: authorized = PageState.User.IsSuperUser; break; } diff --git a/Oqtane.Client/Shared/SiteRouter.razor b/Oqtane.Client/Shared/SiteRouter.razor index edd0ff7e..8bc36116 100644 --- a/Oqtane.Client/Shared/SiteRouter.razor +++ b/Oqtane.Client/Shared/SiteRouter.razor @@ -55,10 +55,18 @@ } protected override async Task OnParametersSetAsync() - { + { if (PageState == null) { - await Refresh(); + // misconfigured api calls should not be processed through the router + if (!_absoluteUri.Contains("~/api/")) + { + await Refresh(); + } + else + { + System.Diagnostics.Debug.WriteLine(this.GetType().FullName + ": Error: " + _absoluteUri + " is not mapped to a Controller"); + } } } diff --git a/Oqtane.Client/Shared/Theme.razor b/Oqtane.Client/Shared/Theme.razor index b059255c..0d41a0b9 100644 --- a/Oqtane.Client/Shared/Theme.razor +++ b/Oqtane.Client/Shared/Theme.razor @@ -1,4 +1,5 @@ @using Oqtane.Shared +@using Oqtane.Modules @DynamicComponent @@ -19,7 +20,11 @@ } else { - // theme does not exist with type specified + // theme does not exist with type specified + builder.OpenComponent(0, Type.GetType(Constants.ModuleMessageControl)); + builder.AddAttribute(1, "Type", MessageType.Error); + builder.AddAttribute(2, "Message", "Error Loading Page Theme " + PageState.Page.ThemeType); + builder.CloseComponent(); } }; } diff --git a/Oqtane.Client/Shared/Utilities.cs b/Oqtane.Client/Shared/Utilities.cs index f9282acc..72518271 100644 --- a/Oqtane.Client/Shared/Utilities.cs +++ b/Oqtane.Client/Shared/Utilities.cs @@ -6,7 +6,7 @@ namespace Oqtane.Shared public class Utilities { - public static string NavigateUrl(string alias, string path) + public static string NavigateUrl(string alias, string path, string parameters) { string url = ""; if (alias != "") @@ -17,6 +17,10 @@ namespace Oqtane.Shared { url += path + "/"; } + if (!string.IsNullOrEmpty(parameters)) + { + url += "?" + parameters; + } if (!url.StartsWith("/")) { url = "/" + url; @@ -26,7 +30,7 @@ namespace Oqtane.Shared public static string EditUrl(string alias, string path, int moduleid, string action, string parameters) { - string url = NavigateUrl(alias, path); + string url = NavigateUrl(alias, path, ""); if ( url == "/" ) { url = ""; diff --git a/Oqtane.Client/Startup.cs b/Oqtane.Client/Startup.cs index 1c64ca52..07c3b631 100644 --- a/Oqtane.Client/Startup.cs +++ b/Oqtane.Client/Startup.cs @@ -46,7 +46,7 @@ namespace Oqtane.Client services.AddScoped(); services.AddScoped(); services.AddScoped(); - + services.AddScoped(); // dynamically register module contexts and repository services Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); @@ -57,7 +57,7 @@ namespace Oqtane.Client .ToArray(); foreach (Type implementationtype in implementationtypes) { - Type servicetype = Type.GetType(implementationtype.FullName.Replace(implementationtype.Name, "I" + implementationtype.Name)); + Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name)); if (servicetype != null) { services.AddScoped(servicetype, implementationtype); // traditional service interface diff --git a/Oqtane.Client/Themes/ContainerBase.cs b/Oqtane.Client/Themes/ContainerBase.cs index 7eb915da..6a939f5b 100644 --- a/Oqtane.Client/Themes/ContainerBase.cs +++ b/Oqtane.Client/Themes/ContainerBase.cs @@ -21,14 +21,13 @@ namespace Oqtane.Themes public string NavigateUrl(string path) { - return Utilities.NavigateUrl(PageState.Alias.Path, path); + return NavigateUrl(path, ""); } - public string EditUrl(string action) + public string NavigateUrl(string path, string parameters) { - return EditUrl(ModuleState.ModuleId, action); + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); } - public string EditUrl(string action, string parameters) { return EditUrl(ModuleState.ModuleId, action, parameters); diff --git a/Oqtane.Client/Themes/Controls/Logo.razor b/Oqtane.Client/Themes/Controls/Logo.razor index 3f77d859..5970ce31 100644 --- a/Oqtane.Client/Themes/Controls/Logo.razor +++ b/Oqtane.Client/Themes/Controls/Logo.razor @@ -6,11 +6,12 @@ @code { string logo = ""; - protected override void OnInit() + protected override Task OnParametersSetAsync() { if (PageState.Site.Logo != "") { logo = "\"""; } + return Task.CompletedTask; } } \ No newline at end of file diff --git a/Oqtane.Client/Themes/Controls/Menu.razor b/Oqtane.Client/Themes/Controls/Menu.razor index 4a5536dd..1d841837 100644 --- a/Oqtane.Client/Themes/Controls/Menu.razor +++ b/Oqtane.Client/Themes/Controls/Menu.razor @@ -34,7 +34,7 @@ List pages; Page parent = null; - protected override void OnInit() + protected override Task OnParametersSetAsync() { // if current page has no children if (PageState.Pages.Where(item => item.ParentId == PageState.Page.PageId).FirstOrDefault() == null) @@ -54,5 +54,6 @@ // current page is parent parent = PageState.Pages.Where(item => item.ParentId == PageState.Page.ParentId).FirstOrDefault(); } + return Task.CompletedTask; } } diff --git a/Oqtane.Client/Themes/Controls/ModuleTitle.razor b/Oqtane.Client/Themes/Controls/ModuleTitle.razor index 886ae21b..fa7e92a9 100644 --- a/Oqtane.Client/Themes/Controls/ModuleTitle.razor +++ b/Oqtane.Client/Themes/Controls/ModuleTitle.razor @@ -6,12 +6,13 @@ @code { string title = ""; - protected override void OnInit() + protected override Task OnParametersSetAsync() { title = ModuleState.Title; if (PageState.Control == "Settings") { title = PageState.Control; } + return Task.CompletedTask; } } diff --git a/Oqtane.Client/Themes/ThemeBase.cs b/Oqtane.Client/Themes/ThemeBase.cs index e922247e..2ad2956c 100644 --- a/Oqtane.Client/Themes/ThemeBase.cs +++ b/Oqtane.Client/Themes/ThemeBase.cs @@ -17,7 +17,12 @@ namespace Oqtane.Themes public string NavigateUrl(string path) { - return Utilities.NavigateUrl(PageState.Alias.Path, path); + return NavigateUrl(path, ""); + } + + public string NavigateUrl(string path, string parameters) + { + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); } } diff --git a/Oqtane.Client/Themes/ThemeObjectBase.cs b/Oqtane.Client/Themes/ThemeObjectBase.cs index 332f1889..efa59f63 100644 --- a/Oqtane.Client/Themes/ThemeObjectBase.cs +++ b/Oqtane.Client/Themes/ThemeObjectBase.cs @@ -15,7 +15,12 @@ namespace Oqtane.Themes public string NavigateUrl(string path) { - return Utilities.NavigateUrl(PageState.Alias.Path, path); + return NavigateUrl(path, ""); + } + + public string NavigateUrl(string path, string parameters) + { + return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); } public string EditUrl(int moduleid, string action) diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index 3297c572..06defd92 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -1,11 +1,15 @@ using DbUp; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Oqtane.Models; +using Oqtane.Repository; using System; +using System.Collections.Generic; using System.Data.SqlClient; using System.IO; +using System.Linq; using System.Reflection; using System.Threading; @@ -182,11 +186,71 @@ namespace Oqtane.Controllers response.Success = !dbUpgrade.IsUpgradeRequired(); if (!response.Success) { - response.Message = "Scripts Have Not Been Run"; + response.Message = "Master Installation Scripts Have Not Been Executed"; + } + else + { + 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()) + { + // upgrade framework + dbUpgradeConfig = DeployChanges.To.SqlDatabase(tenant.DBConnectionString) + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly()); + dbUpgrade = dbUpgradeConfig.Build(); + if (dbUpgrade.IsUpgradeRequired()) + { + var result = dbUpgrade.PerformUpgrade(); + if (!result.Successful) + { + // TODO: log result.Error.Message; + } + } + // iterate through Oqtane module assemblies and execute any database scripts + foreach (Assembly assembly in assemblies) + { + InstallModule(assembly, tenant.DBConnectionString); + } + } + } } } return response; } + + 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; + } + } + } + } + + 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 Tenant { get; set; } } } diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs new file mode 100644 index 00000000..2f90efd9 --- /dev/null +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; +using Oqtane.Repository; +using Oqtane.Models; + +namespace Oqtane.Controllers +{ + [Route("{site}/api/[controller]")] + public class SettingController : Controller + { + private readonly ISettingRepository Settings; + + public SettingController(ISettingRepository Settings) + { + this.Settings = Settings; + } + + // GET: api/ + [HttpGet] + public IEnumerable Get(string entityname, int entityid) + { + return Settings.GetSettings(entityname, entityid); + } + + // GET api//5 + [HttpGet("{id}")] + public Setting Get(int id) + { + return Settings.GetSetting(id); + } + + // POST api/ + [HttpPost] + public Setting Post([FromBody] Setting Setting) + { + if (ModelState.IsValid) + { + Setting = Settings.AddSetting(Setting); + } + return Setting; + } + + // PUT api//5 + [HttpPut("{id}")] + public Setting Put(int id, [FromBody] Setting Setting) + { + if (ModelState.IsValid) + { + Setting = Settings.UpdateSetting(Setting); + } + return Setting; + } + + // DELETE api//5 + [HttpDelete("{id}")] + public void Delete(int id) + { + Settings.DeleteSetting(id); + } + } +} diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 00f4fb9b..209a90cc 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -4,6 +4,7 @@ using Oqtane.Repository; using Oqtane.Models; using Microsoft.AspNetCore.Identity; using System.Threading.Tasks; +using System.Linq; namespace Oqtane.Controllers { @@ -11,12 +12,14 @@ namespace Oqtane.Controllers public class UserController : Controller { private readonly IUserRepository users; + private readonly ISiteUserRepository siteusers; private readonly UserManager identityUserManager; private readonly SignInManager identitySignInManager; - public UserController(IUserRepository Users, UserManager IdentityUserManager, SignInManager IdentitySignInManager) + public UserController(IUserRepository Users, ISiteUserRepository SiteUsers, UserManager IdentityUserManager, SignInManager IdentitySignInManager) { users = Users; + siteusers = SiteUsers; identityUserManager = IdentityUserManager; identitySignInManager = IdentitySignInManager; } @@ -39,6 +42,8 @@ namespace Oqtane.Controllers [HttpPost] public async Task Post([FromBody] User User) { + User user = null; + if (ModelState.IsValid) { IdentityUser identityuser = await identityUserManager.FindByNameAsync(User.Username); @@ -50,11 +55,17 @@ namespace Oqtane.Controllers var result = await identityUserManager.CreateAsync(identityuser, User.Password); if (result.Succeeded) { - User = users.AddUser(User); + user = users.AddUser(User); + SiteUser SiteUser = new SiteUser(); + SiteUser.SiteId = User.SiteId; + SiteUser.UserId = user.UserId; + SiteUser.IsAuthorized = true; + siteusers.AddSiteUser(SiteUser); } } } - return User; + + return user; } // PUT api//5 @@ -84,36 +95,38 @@ namespace Oqtane.Controllers // POST api//login [HttpPost("login")] - public async Task Login([FromBody] User user) + public async Task Login([FromBody] User User) { + User user = new Models.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) { - await identitySignInManager.SignInAsync(identityuser, user.IsPersistent); user = users.GetUser(identityuser.UserName); - user.IsAuthenticated = true; + if (user != null) + { + SiteUser siteuser = siteusers.GetSiteUsers(User.SiteId, user.UserId).FirstOrDefault(); + if (siteuser.IsAuthorized) + { + await identitySignInManager.SignInAsync(identityuser, User.IsPersistent); + user.IsAuthenticated = true; + } + } } - else - { - user = new Models.User { Username = user.Username, IsAuthenticated = false }; - } - } - else - { - user = new Models.User { Username = user.Username, IsAuthenticated = false }; } } + return user; } // POST api//logout [HttpPost("logout")] - public async Task Logout([FromBody] User user) + public async Task Logout([FromBody] User User) { await identitySignInManager.SignOutAsync(); } diff --git a/Oqtane.Server/Modules/MVCModuleExtension.cs b/Oqtane.Server/Modules/MVCModuleExtension.cs new file mode 100644 index 00000000..fdbaec4a --- /dev/null +++ b/Oqtane.Server/Modules/MVCModuleExtension.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationParts; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class MvcModuleExtensions + { + public static IMvcBuilder AddModuleAssemblies(this IMvcBuilder mvcBuilder, List assemblies) + { + // load MVC application parts from module assemblies + foreach (Assembly assembly in assemblies) + { + // check if assembly contains MVC Controllers + if (assembly.GetTypes().Where(item => item.IsSubclassOf(typeof(Controller))).ToArray().Length > 0) + { + var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly); + foreach (var part in partFactory.GetApplicationParts(assembly)) + { + mvcBuilder.PartManager.ApplicationParts.Add(part); + } + } + } + return mvcBuilder; + } + } +} \ No newline at end of file diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index efb5ecaf..05779e3f 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -26,14 +26,14 @@ - + - - + + diff --git a/Oqtane.Server/Repository/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs similarity index 100% rename from Oqtane.Server/Repository/DBContextBase.cs rename to Oqtane.Server/Repository/Context/DBContextBase.cs diff --git a/Oqtane.Server/Repository/MasterDBContext.cs b/Oqtane.Server/Repository/Context/MasterDBContext.cs similarity index 100% rename from Oqtane.Server/Repository/MasterDBContext.cs rename to Oqtane.Server/Repository/Context/MasterDBContext.cs diff --git a/Oqtane.Server/Repository/TenantDBContext.cs b/Oqtane.Server/Repository/Context/TenantDBContext.cs similarity index 92% rename from Oqtane.Server/Repository/TenantDBContext.cs rename to Oqtane.Server/Repository/Context/TenantDBContext.cs index a6f83e57..3e0c1ca7 100644 --- a/Oqtane.Server/Repository/TenantDBContext.cs +++ b/Oqtane.Server/Repository/Context/TenantDBContext.cs @@ -12,6 +12,7 @@ namespace Oqtane.Repository public virtual DbSet Module { get; set; } public virtual DbSet User { get; set; } public virtual DbSet SiteUser { get; set; } + public virtual DbSet Setting { get; set; } public TenantDBContext(ITenantResolver TenantResolver, IHttpContextAccessor accessor) : base(TenantResolver, accessor) { diff --git a/Oqtane.Server/Repository/IAliasRepository.cs b/Oqtane.Server/Repository/Interfaces/IAliasRepository.cs similarity index 100% rename from Oqtane.Server/Repository/IAliasRepository.cs rename to Oqtane.Server/Repository/Interfaces/IAliasRepository.cs diff --git a/Oqtane.Server/Repository/IModuleDefinitionRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs similarity index 100% rename from Oqtane.Server/Repository/IModuleDefinitionRepository.cs rename to Oqtane.Server/Repository/Interfaces/IModuleDefinitionRepository.cs diff --git a/Oqtane.Server/Repository/IModuleRepository.cs b/Oqtane.Server/Repository/Interfaces/IModuleRepository.cs similarity index 100% rename from Oqtane.Server/Repository/IModuleRepository.cs rename to Oqtane.Server/Repository/Interfaces/IModuleRepository.cs diff --git a/Oqtane.Server/Repository/IPageModuleRepository.cs b/Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs similarity index 100% rename from Oqtane.Server/Repository/IPageModuleRepository.cs rename to Oqtane.Server/Repository/Interfaces/IPageModuleRepository.cs diff --git a/Oqtane.Server/Repository/IPageRepository.cs b/Oqtane.Server/Repository/Interfaces/IPageRepository.cs similarity index 100% rename from Oqtane.Server/Repository/IPageRepository.cs rename to Oqtane.Server/Repository/Interfaces/IPageRepository.cs diff --git a/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs b/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs new file mode 100644 index 00000000..0ffad860 --- /dev/null +++ b/Oqtane.Server/Repository/Interfaces/ISettingRepository.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Oqtane.Models; + +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); + } +} diff --git a/Oqtane.Server/Repository/ISiteRepository.cs b/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs similarity index 100% rename from Oqtane.Server/Repository/ISiteRepository.cs rename to Oqtane.Server/Repository/Interfaces/ISiteRepository.cs diff --git a/Oqtane.Server/Repository/ISiteUserRepository.cs b/Oqtane.Server/Repository/Interfaces/ISiteUserRepository.cs similarity index 100% rename from Oqtane.Server/Repository/ISiteUserRepository.cs rename to Oqtane.Server/Repository/Interfaces/ISiteUserRepository.cs diff --git a/Oqtane.Server/Repository/ITenantRepository.cs b/Oqtane.Server/Repository/Interfaces/ITenantRepository.cs similarity index 100% rename from Oqtane.Server/Repository/ITenantRepository.cs rename to Oqtane.Server/Repository/Interfaces/ITenantRepository.cs diff --git a/Oqtane.Server/Repository/ITenantResolver.cs b/Oqtane.Server/Repository/Interfaces/ITenantResolver.cs similarity index 100% rename from Oqtane.Server/Repository/ITenantResolver.cs rename to Oqtane.Server/Repository/Interfaces/ITenantResolver.cs diff --git a/Oqtane.Server/Repository/IThemeRepository.cs b/Oqtane.Server/Repository/Interfaces/IThemeRepository.cs similarity index 100% rename from Oqtane.Server/Repository/IThemeRepository.cs rename to Oqtane.Server/Repository/Interfaces/IThemeRepository.cs diff --git a/Oqtane.Server/Repository/IUserRepository.cs b/Oqtane.Server/Repository/Interfaces/IUserRepository.cs similarity index 100% rename from Oqtane.Server/Repository/IUserRepository.cs rename to Oqtane.Server/Repository/Interfaces/IUserRepository.cs diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index a16572fe..ca612fae 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -22,12 +22,11 @@ namespace Oqtane.Repository List moduledefinitions = new List(); // iterate through Oqtane module assemblies - foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() + .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray(); + foreach (Assembly assembly in assemblies) { - if (assembly.FullName.StartsWith("Oqtane.Client") || assembly.FullName.StartsWith("Oqtane.Module.")) - { - moduledefinitions = LoadModuleDefinitionsFromAssembly(moduledefinitions, assembly); - } + moduledefinitions = LoadModuleDefinitionsFromAssembly(moduledefinitions, assembly); } return moduledefinitions; @@ -119,6 +118,5 @@ namespace Oqtane.Repository return moduledefinitions; } - } } diff --git a/Oqtane.Server/Repository/SettingRepository.cs b/Oqtane.Server/Repository/SettingRepository.cs new file mode 100644 index 00000000..b5a17bd5 --- /dev/null +++ b/Oqtane.Server/Repository/SettingRepository.cs @@ -0,0 +1,85 @@ +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using Oqtane.Models; + +namespace Oqtane.Repository +{ + public class SettingRepository : ISettingRepository + { + private TenantDBContext db; + + public SettingRepository(TenantDBContext context) + { + db = context; + } + + public IEnumerable GetSettings(string EntityName, int EntityId) + { + try + { + return db.Setting.Where(item => item.EntityName == EntityName) + .Where(item => item.EntityId == EntityId).ToList(); + } + catch + { + throw; + } + } + + public Setting AddSetting(Setting Setting) + { + try + { + db.Setting.Add(Setting); + db.SaveChanges(); + return Setting; + } + catch + { + throw; + } + } + + public Setting UpdateSetting(Setting Setting) + { + try + { + db.Entry(Setting).State = EntityState.Modified; + db.SaveChanges(); + return Setting; + } + catch + { + throw; + } + } + + public Setting GetSetting(int SettingId) + { + try + { + Setting Setting = db.Setting.Find(SettingId); + return Setting; + } + catch + { + throw; + } + } + + public void DeleteSetting(int SettingId) + { + try + { + Setting Setting = db.Setting.Find(SettingId); + db.Setting.Remove(Setting); + db.SaveChanges(); + } + catch + { + throw; + } + } + } +} diff --git a/Oqtane.Server/Repository/ThemeRepository.cs b/Oqtane.Server/Repository/ThemeRepository.cs index aec59868..9eb25aa6 100644 --- a/Oqtane.Server/Repository/ThemeRepository.cs +++ b/Oqtane.Server/Repository/ThemeRepository.cs @@ -22,13 +22,11 @@ namespace Oqtane.Repository List themes = new List(); // iterate through Oqtane theme assemblies - // TODO: Remove restriction on assembly + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() + .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Theme.")).ToArray(); foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { - if (assembly.FullName.StartsWith("Oqtane.Client") || assembly.FullName.StartsWith("Oqtane.Theme.")) - { - themes = LoadThemesFromAssembly(themes, assembly); - } + themes = LoadThemesFromAssembly(themes, assembly); } return themes; diff --git a/Oqtane.Server/Scripts/Tenant.sql b/Oqtane.Server/Scripts/00.00.00.sql similarity index 95% rename from Oqtane.Server/Scripts/Tenant.sql rename to Oqtane.Server/Scripts/00.00.00.sql index ed1626be..c5d950d8 100644 --- a/Oqtane.Server/Scripts/Tenant.sql +++ b/Oqtane.Server/Scripts/00.00.00.sql @@ -127,6 +127,23 @@ CREATE TABLE [dbo].[SiteUser]( ) ) GO + +CREATE TABLE [dbo].[Setting]( + [SettingId] [int] IDENTITY(1,1) NOT NULL, + [EntityName] [nvarchar](50) NOT NULL, + [EntityId] [int] NOT NULL, + [SettingName] [nvarchar](50) NOT NULL, + [SettingValue] [nvarchar](max) NOT NULL, + [CreatedBy] [nvarchar](256) NOT NULL, + [CreatedOn] [datetime] NOT NULL, + [ModifiedBy] [nvarchar](256) NOT NULL, + [ModifiedOn] [datetime] NOT NULL, + CONSTRAINT [PK_Setting] PRIMARY KEY CLUSTERED + ( + [SettingId] ASC + ) +) +GO /* Create foreign key relationships @@ -167,6 +184,20 @@ GO /* +Create indexes + +*/ + +CREATE UNIQUE NONCLUSTERED INDEX IX_Setting ON dbo.Setting + ( + EntityName, + EntityId, + SettingName + ) ON [PRIMARY] +GO + +/* + Create seed data */ @@ -303,19 +334,19 @@ INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane] VALUES (2, 1, 2, N'Counter', N'Left', 1, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) GO INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (3, 1, 3, N'Text', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) +VALUES (3, 1, 3, N'Lorem ipsum', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) GO INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) VALUES (4, 2, 4, N'Weather', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) GO INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (5, 2, 5, N'Text', N'Top', 1, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) +VALUES (5, 2, 5, N'Enim sed', N'Top', 1, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) GO INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (6, 3, 6, N'Text', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) +VALUES (6, 3, 6, N'Id consectetur', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) GO INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (7, 3, 7, N'Text', N'Right', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) +VALUES (7, 3, 7, N'Ornare arcu', N'Right', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) GO INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) VALUES (8, 5, 8, N'Page Management', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) @@ -342,10 +373,10 @@ INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane] VALUES (15, 11, 15, N'Theme Management', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) GO INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (16, 12, 16, N'Text', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) +VALUES (16, 12, 16, N'Id consectetur', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) GO INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (17, 13, 17, N'Text', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) +VALUES (17, 13, 17, N'Lorem ipsum', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) GO INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) VALUES (18, 14, 18, N'Login', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) diff --git a/Oqtane.Server/Scripts/Identity.sql b/Oqtane.Server/Scripts/00.00.01.sql similarity index 100% rename from Oqtane.Server/Scripts/Identity.sql rename to Oqtane.Server/Scripts/00.00.01.sql diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 10c2a516..3ca8f994 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.ResponseCompression; +using Microsoft.AspNetCore.ResponseCompression; // needed for WASM using Microsoft.Extensions.DependencyInjection; using System.Linq; using Microsoft.Extensions.Configuration; @@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Components; using Oqtane.Shared; using Microsoft.AspNetCore.Identity; using System.Threading.Tasks; +using System.Collections.Generic; namespace Oqtane.Server { @@ -77,27 +78,7 @@ namespace Oqtane.Server 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.FullName.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 - } - } - } + services.AddScoped(); services.AddSingleton(); @@ -139,9 +120,26 @@ namespace Oqtane.Server }; }); - services.AddMemoryCache(); + // get list of loaded assemblies + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - services.AddMvc().AddNewtonsoftJson(); + // iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process ) + string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + DirectoryInfo folder = new DirectoryInfo(path); + List moduleassemblies = new List(); + foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll")) + { + // check if assembly is already loaded + Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault(); + if (assembly == null) + { + // load assembly ( as long as dependencies are in /bin they will load as well ) + assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName); + moduleassemblies.Add(assembly); + } + } + + services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson(); // register singleton scoped core services services.AddSingleton(Configuration); @@ -157,26 +155,12 @@ namespace Oqtane.Server services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); - // get list of loaded assemblies - assemblies = AppDomain.CurrentDomain.GetAssemblies(); - // get path to /bin - string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - DirectoryInfo folder = new DirectoryInfo(path); - // iterate through Oqtane assemblies in /bin ( filter is narrow to optimize loading process ) - foreach (FileInfo file in folder.EnumerateFiles("Oqtane.*.dll")) - { - // check if assembly is already loaded - Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault(); - if (assembly == null) - { - // load assembly ( as long as dependencies are in /bin they will load as well ) - assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName); - } - } - - // dynamically register module contexts and repository services - assemblies = AppDomain.CurrentDomain.GetAssemblies(); + // dynamically register module services, contexts, and repository classes + assemblies = AppDomain.CurrentDomain.GetAssemblies() + .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray(); foreach (Assembly assembly in assemblies) { Type[] implementationtypes = assembly.GetTypes() @@ -184,7 +168,7 @@ namespace Oqtane.Server .ToArray(); foreach (Type implementationtype in implementationtypes) { - Type servicetype = Type.GetType(implementationtype.FullName.Replace(implementationtype.Name, "I" + implementationtype.Name)); + Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name)); if (servicetype != null) { services.AddScoped(servicetype, implementationtype); // traditional service interface @@ -273,9 +257,26 @@ namespace Oqtane.Server }; }); - services.AddMemoryCache(); + // get list of loaded assemblies + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - services.AddMvc().AddNewtonsoftJson(); + // iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process ) + string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + DirectoryInfo folder = new DirectoryInfo(path); + List moduleassemblies = new List(); + foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll")) + { + // check if assembly is already loaded + Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault(); + if (assembly == null) + { + // load assembly ( as long as dependencies are in /bin they will load as well ) + assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName); + moduleassemblies.Add(assembly); + } + } + + services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson(); // register singleton scoped core services services.AddSingleton(Configuration); @@ -291,26 +292,12 @@ namespace Oqtane.Server services.AddTransient(); services.AddTransient(); services.AddTransient(); - - // get list of loaded assemblies - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - // get path to /bin - string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - DirectoryInfo folder = new DirectoryInfo(path); - // iterate through Oqtane assemblies in /bin ( filter is narrow to optimize loading process ) - foreach (FileInfo file in folder.EnumerateFiles("Oqtane.*.dll")) - { - // check if assembly is already loaded - Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault(); - if (assembly == null) - { - // load assembly ( as long as dependencies are in /bin they will load as well ) - assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName); - } - } - - // dynamically register module contexts and repository services - assemblies = AppDomain.CurrentDomain.GetAssemblies(); + services.AddTransient(); + services.AddTransient(); + + // dynamically register module services, contexts, and repository classes + assemblies = AppDomain.CurrentDomain.GetAssemblies() + .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray(); foreach (Assembly assembly in assemblies) { Type[] implementationtypes = assembly.GetTypes() @@ -318,7 +305,7 @@ namespace Oqtane.Server .ToArray(); foreach (Type implementationtype in implementationtypes) { - Type servicetype = Type.GetType(implementationtype.FullName.Replace(implementationtype.Name, "I" + implementationtype.Name)); + Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name)); if (servicetype != null) { services.AddScoped(servicetype, implementationtype); // traditional service interface diff --git a/Oqtane.Shared/Models/Setting.cs b/Oqtane.Shared/Models/Setting.cs new file mode 100644 index 00000000..d7742c84 --- /dev/null +++ b/Oqtane.Shared/Models/Setting.cs @@ -0,0 +1,18 @@ +using System; + +namespace Oqtane.Models +{ + public class Setting : IAuditable + { + public int SettingId { get; set; } + public string EntityName { get; set; } + public int EntityId { get; set; } + public string SettingName { get; set; } + public string SettingValue { get; set; } + + public string CreatedBy { get; set; } + public DateTime CreatedOn { get; set; } + public string ModifiedBy { get; set; } + public DateTime ModifiedOn { get; set; } + } +}