diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json index d4e51eeadf..eb49443d6e 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json @@ -8,6 +8,7 @@ "FrameworkDocumentation": "Framework documentation", "OfficialBlog": "Official blog", "CommercialHomePage": "Commercial home page", - "CommercialSupportWebSite": "Commercial support web site" + "CommercialSupportWebSite": "Commercial support web site", + "CommunityWebSite": "ABP community web site" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json index 9c36cd611b..e403e72cf6 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json @@ -8,6 +8,7 @@ "FrameworkDocumentation": "Framework Dokümantasyon", "OfficialBlog": "Resmi blog", "CommercialHomePage": "Kurumsal ana sayfa", - "CommercialSupportWebSite": "Kurumsal destek web sitesi" + "CommercialSupportWebSite": "Kurumsal destek web sitesi", + "CommunityWebSite": "ABP topluluk web sitesi" } } \ No newline at end of file diff --git a/docs/en/Unit-Of-Work.md b/docs/en/Unit-Of-Work.md index 907e601d30..743aa515bb 100644 --- a/docs/en/Unit-Of-Work.md +++ b/docs/en/Unit-Of-Work.md @@ -325,7 +325,7 @@ public async Task CreateAsync(string name) If your intent is just to save the changes after creating/updating/deleting an entity, it is suggested to use the `autoSave` option instead of manually using the `CurrentUnitOfWork.SaveChangesAsync()`. -> **Note-1**: All changes are automatically saved when a unit of work ends without any error. So, don't call `SaveChangesAsync()` unless you really need it. +> **Note-1**: All changes are automatically saved when a unit of work ends without any error. So, don't call `SaveChangesAsync()` and don't set `autoSave` to `true` unless you really need it. > > **Note-2**: If you use `Guid` as the primary key, you never need to save changes on insert to just get the generated id, because `Guid` keys are set in the application and are immediately available once you create a new entity. diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index bd364b91b8..a3ee894572 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -307,6 +307,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Azure. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EntityFrameworkCore.Oracle", "src\Volo.Abp.EntityFrameworkCore.Oracle\Volo.Abp.EntityFrameworkCore.Oracle.csproj", "{1738845A-5348-4EB8-B736-CD1D22A808B4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Caching.StackExchangeRedis", "src\Volo.Abp.Caching.StackExchangeRedis\Volo.Abp.Caching.StackExchangeRedis.csproj", "{2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Caching.StackExchangeRedis.Tests", "test\Volo.Abp.Caching.StackExchangeRedis.Tests\Volo.Abp.Caching.StackExchangeRedis.Tests.csproj", "{60D0E384-965E-4F81-9D71-B28F419254FC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -913,6 +917,14 @@ Global {1738845A-5348-4EB8-B736-CD1D22A808B4}.Debug|Any CPU.Build.0 = Debug|Any CPU {1738845A-5348-4EB8-B736-CD1D22A808B4}.Release|Any CPU.ActiveCfg = Release|Any CPU {1738845A-5348-4EB8-B736-CD1D22A808B4}.Release|Any CPU.Build.0 = Release|Any CPU + {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}.Release|Any CPU.Build.0 = Release|Any CPU + {60D0E384-965E-4F81-9D71-B28F419254FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60D0E384-965E-4F81-9D71-B28F419254FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60D0E384-965E-4F81-9D71-B28F419254FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60D0E384-965E-4F81-9D71-B28F419254FC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1068,6 +1080,8 @@ Global {C44242F7-D55D-4867-AAF4-A786E404312E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {A80E9A0B-8932-4B5D-83FB-6751708FD484} = {447C8A77-E5F0-4538-8687-7383196D04EA} {1738845A-5348-4EB8-B736-CD1D22A808B4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {60D0E384-965E-4F81-9D71-B28F419254FC} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Properties/launchSettings.json deleted file mode 100644 index fcbf86698d..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53450/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Authentication.OAuth": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53451/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Properties/launchSettings.json deleted file mode 100644 index 67c494aa28..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53760/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.MultiTenancy": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53763/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Properties/launchSettings.json deleted file mode 100644 index e17b14d87f..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:52302/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.Client": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:52303/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs index 2197d7b08e..ce08bebe87 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs @@ -14,5 +14,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations public string UserName { get; set; } public string Email { get; set; } + + public string[] Roles { get; set; } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Properties/launchSettings.json deleted file mode 100644 index c2dcf8fae6..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:61183/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Bootstrap": { - "commandName": "Project", - "launchBrowser": true, - "launchUrl": "http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Properties/launchSettings.json deleted file mode 100644 index 7bcd0a8aff..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53762/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Bundling": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53765/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Properties/launchSettings.json deleted file mode 100644 index 6870af4120..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:56918/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.TenantSwitch": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:56919/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Properties/launchSettings.json deleted file mode 100644 index 4ae9c1d2f1..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53766/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Packages": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53768/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyScriptContributor.cs new file mode 100644 index 0000000000..691f377c6c --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyScriptContributor.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Uppy +{ + public class UppyScriptContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.AddIfNotContains("/libs/uppy/uppy.min.js"); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyStyleContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyStyleContributor.cs new file mode 100644 index 0000000000..c10509515d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyStyleContributor.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Uppy +{ + public class UppyStyleContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.AddIfNotContains("/libs/uppy/uppy.min.css"); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Properties/launchSettings.json deleted file mode 100644 index 763367a2a8..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:50398/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:50399/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj index c390cfe58c..206224f918 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj @@ -22,8 +22,6 @@ - - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj index 078563ace3..8c098c3300 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj @@ -18,8 +18,6 @@ - - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Properties/launchSettings.json deleted file mode 100644 index f928a8ea06..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:51544/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:51545/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj index 71e1257a4d..4b0fbc24cf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj @@ -23,9 +23,7 @@ - - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Properties/launchSettings.json deleted file mode 100644 index e9779c3c37..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53761/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Widgets": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53764/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Properties/launchSettings.json deleted file mode 100644 index 3b3fd9ea46..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55913/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI": { - "commandName": "Project", - "launchBrowser": true, - "launchUrl": "http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc/Properties/launchSettings.json deleted file mode 100644 index 3c84fd21f2..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53767/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53769/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index 263178e0ed..e3cee4e72b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -16,6 +16,7 @@ using System.Net; using System.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.DataAnnotations; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; @@ -25,6 +26,7 @@ using Microsoft.Extensions.Localization; using Volo.Abp.ApiVersioning; using Volo.Abp.AspNetCore.Mvc.ApiExploring; using Volo.Abp.AspNetCore.Mvc.Conventions; +using Volo.Abp.AspNetCore.Mvc.DataAnnotations; using Volo.Abp.AspNetCore.Mvc.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Json; using Volo.Abp.AspNetCore.Mvc.Localization; @@ -164,6 +166,9 @@ namespace Volo.Abp.AspNetCore.Mvc partManager.FeatureProviders.Add(new AbpConventionalControllerFeatureProvider(application)); partManager.ApplicationParts.AddIfNotContains(typeof(AbpAspNetCoreMvcModule).Assembly); + context.Services.Replace(ServiceDescriptor.Singleton()); + context.Services.AddSingleton(); + Configure(mvcOptions => { mvcOptions.AddAbp(context.Services); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs index 4aa183a43d..59166d3e1d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs @@ -5,7 +5,6 @@ using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.ExceptionHandling; using Volo.Abp.AspNetCore.Mvc.Features; using Volo.Abp.AspNetCore.Mvc.ModelBinding; -using Volo.Abp.AspNetCore.Mvc.ModelBinding.Metadata; using Volo.Abp.AspNetCore.Mvc.Response; using Volo.Abp.AspNetCore.Mvc.Uow; using Volo.Abp.AspNetCore.Mvc.Validation; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs index 5aa8f07059..54815d6ff5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs @@ -117,7 +117,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations Id = _currentUser.Id, TenantId = _currentUser.TenantId, UserName = _currentUser.UserName, - Email = _currentUser.Email + Email = _currentUser.Email, + Roles = _currentUser.Roles }; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs new file mode 100644 index 0000000000..eaceddba70 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.Extensions.Localization; +using Volo.Abp.Validation; + +namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations +{ + public class AbpValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider + { + private readonly ValidationAttributeAdapterProvider _defaultAdapter; + + public AbpValidationAttributeAdapterProvider(ValidationAttributeAdapterProvider defaultAdapter) + { + _defaultAdapter = defaultAdapter; + } + + public virtual IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer) + { + var type = attribute.GetType(); + + if (type == typeof(DynamicStringLengthAttribute)) + { + return new DynamicStringLengthAttributeAdapter((DynamicStringLengthAttribute) attribute, stringLocalizer); + } + + return _defaultAdapter.GetAttributeAdapter(attribute, stringLocalizer); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicStringLengthAttributeAdapter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicStringLengthAttributeAdapter.cs new file mode 100644 index 0000000000..d4c46b85f3 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicStringLengthAttributeAdapter.cs @@ -0,0 +1,54 @@ +using System; +using System.Globalization; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.Localization; +using Volo.Abp.Validation; + +namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations +{ + public class DynamicStringLengthAttributeAdapter : AttributeAdapterBase + { + private readonly string _max; + private readonly string _min; + + public DynamicStringLengthAttributeAdapter( + DynamicStringLengthAttribute attribute, + IStringLocalizer stringLocalizer) + : base(attribute, stringLocalizer) + { + _max = Attribute.MaximumLength.ToString(CultureInfo.InvariantCulture); + _min = Attribute.MinimumLength.ToString(CultureInfo.InvariantCulture); + } + + public override void AddValidation(ClientModelValidationContext context) + { + Check.NotNull(context, nameof(context)); + + MergeAttribute(context.Attributes, "data-val", "true"); + MergeAttribute(context.Attributes, "data-val-length", GetErrorMessage(context)); + + if (Attribute.MaximumLength != int.MaxValue) + { + MergeAttribute(context.Attributes, "data-val-length-max", _max); + } + + if (Attribute.MinimumLength != 0) + { + MergeAttribute(context.Attributes, "data-val-length-min", _min); + } + } + + public override string GetErrorMessage(ModelValidationContextBase validationContext) + { + Check.NotNull(validationContext, nameof(validationContext)); + + return GetErrorMessage( + validationContext.ModelMetadata, + validationContext.ModelMetadata.GetDisplayName(), + Attribute.MaximumLength, + Attribute.MinimumLength + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Serilog/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Serilog/Properties/launchSettings.json deleted file mode 100644 index cb1bd6546c..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Serilog/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:61851/", - "sslPort": 44301 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Serilog": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.SignalR/Properties/launchSettings.json deleted file mode 100644 index 418c7f2621..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53900/", - "sslPort": 44362 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.SignalR": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.TestBase/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.TestBase/Properties/launchSettings.json deleted file mode 100644 index fca5708e7b..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.TestBase/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53783/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.TestBase": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53784/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore/Properties/launchSettings.json deleted file mode 100644 index 4098e788cd..0000000000 --- a/framework/src/Volo.Abp.AspNetCore/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53374/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53375/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs index 945c930175..3b3cbc4743 100644 --- a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs @@ -15,6 +15,7 @@ namespace Volo.Abp.BlobStoring.Azure Action azureConfigureAction) { containerConfiguration.ProviderType = typeof(AzureBlobProvider); + containerConfiguration.NamingNormalizers.TryAdd(); azureConfigureAction(new AzureBlobProviderConfiguration(containerConfiguration)); diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobNamingNormalizer.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobNamingNormalizer.cs new file mode 100644 index 0000000000..43b479e08e --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobNamingNormalizer.cs @@ -0,0 +1,52 @@ +using System.Text.RegularExpressions; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlobStoring.Azure +{ + public class AzureBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency + { + /// + ///https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names + /// + public virtual string NormalizeContainerName(string containerName) + { + // All letters in a container name must be lowercase. + containerName = containerName.ToLower(); + + // Container names can contain only letters, numbers, and the dash (-) character. + containerName = Regex.Replace(containerName, "[^a-z0-9-]", string.Empty); + + // Every dash (-) character must be immediately preceded and followed by a letter or number; + // consecutive dashes are not permitted in container names. + // Container names must start or end with a letter or number + containerName = Regex.Replace(containerName, "-{2,}", "-"); + containerName = Regex.Replace(containerName, "^-", string.Empty); + containerName = Regex.Replace(containerName, "-$", string.Empty); + + // Container names must be from 3 through 63 characters long. + if (containerName.Length < 3) + { + var length = containerName.Length; + for (var i = 0; i < 3 - length; i++) + { + containerName += "0"; + } + } + + if (containerName.Length > 63) + { + containerName = containerName.Substring(0, 63); + } + + return containerName; + } + + /// + ///https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#blob-names + /// + public virtual string NormalizeBlobName(string blobName) + { + return blobName; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs index 24357554eb..2bb818da56 100644 --- a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs @@ -9,16 +9,17 @@ namespace Volo.Abp.BlobStoring.FileSystem { return new FileSystemBlobProviderConfiguration(containerConfiguration); } - + public static BlobContainerConfiguration UseFileSystem( this BlobContainerConfiguration containerConfiguration, Action fileSystemConfigureAction) { containerConfiguration.ProviderType = typeof(FileSystemBlobProvider); - + containerConfiguration.NamingNormalizers.TryAdd(); + fileSystemConfigureAction(new FileSystemBlobProviderConfiguration(containerConfiguration)); - + return containerConfiguration; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobNamingNormalizer.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobNamingNormalizer.cs new file mode 100644 index 0000000000..f7d273dca7 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobNamingNormalizer.cs @@ -0,0 +1,41 @@ +using System; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + public class FileSystemBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency + { + private readonly IOSPlatformProvider _iosPlatformProvider; + + public FileSystemBlobNamingNormalizer(IOSPlatformProvider iosPlatformProvider) + { + _iosPlatformProvider = iosPlatformProvider; + } + + public virtual string NormalizeContainerName(string containerName) + { + return Normalize(containerName); + } + + public virtual string NormalizeBlobName(string blobName) + { + return Normalize(blobName); + } + + protected virtual string Normalize(string fileName) + { + var os = _iosPlatformProvider.GetCurrentOSPlatform(); + if (os == OSPlatform.Windows) + { + // A filename cannot contain any of the following characters: \ / : * ? " < > | + // In order to support the directory included in the blob name, remove / and \ + fileName = Regex.Replace(fileName, "[:\\*\\?\"<>\\|]", string.Empty); + } + + return fileName; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs index d165a40316..cd2287e964 100644 --- a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs @@ -1,7 +1,9 @@ using System; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.MultiTenancy; using Volo.Abp.Threading; @@ -84,18 +86,22 @@ namespace Volo.Abp.BlobStoring protected ICancellationTokenProvider CancellationTokenProvider { get; } + protected IServiceProvider ServiceProvider { get; } + public BlobContainer( string containerName, BlobContainerConfiguration configuration, IBlobProvider provider, ICurrentTenant currentTenant, - ICancellationTokenProvider cancellationTokenProvider) + ICancellationTokenProvider cancellationTokenProvider, + IServiceProvider serviceProvider) { ContainerName = containerName; Configuration = configuration; Provider = provider; CurrentTenant = currentTenant; CancellationTokenProvider = cancellationTokenProvider; + ServiceProvider = serviceProvider; } public virtual async Task SaveAsync( @@ -106,11 +112,13 @@ namespace Volo.Abp.BlobStoring { using (CurrentTenant.Change(GetTenantIdOrNull())) { + var (normalizedContainerName, normalizedBlobName) = NormalizeNaming(ContainerName, name); + await Provider.SaveAsync( new BlobProviderSaveArgs( - ContainerName, + normalizedContainerName, Configuration, - name, + normalizedBlobName, stream, overrideExisting, CancellationTokenProvider.FallbackToProvider(cancellationToken) @@ -125,11 +133,14 @@ namespace Volo.Abp.BlobStoring { using (CurrentTenant.Change(GetTenantIdOrNull())) { + var (normalizedContainerName, normalizedBlobName) = + NormalizeNaming(ContainerName, name); + return await Provider.DeleteAsync( new BlobProviderDeleteArgs( - ContainerName, + normalizedContainerName, Configuration, - name, + normalizedBlobName, CancellationTokenProvider.FallbackToProvider(cancellationToken) ) ); @@ -142,11 +153,14 @@ namespace Volo.Abp.BlobStoring { using (CurrentTenant.Change(GetTenantIdOrNull())) { + var (normalizedContainerName, normalizedBlobName) = + NormalizeNaming(ContainerName, name); + return await Provider.ExistsAsync( new BlobProviderExistsArgs( - ContainerName, + normalizedContainerName, Configuration, - name, + normalizedBlobName, CancellationTokenProvider.FallbackToProvider(cancellationToken) ) ); @@ -158,7 +172,7 @@ namespace Volo.Abp.BlobStoring CancellationToken cancellationToken = default) { var stream = await GetOrNullAsync(name, cancellationToken); - + if (stream == null) { //TODO: Consider to throw some type of "not found" exception and handle on the HTTP status side @@ -175,11 +189,14 @@ namespace Volo.Abp.BlobStoring { using (CurrentTenant.Change(GetTenantIdOrNull())) { + var (normalizedContainerName, normalizedBlobName) = + NormalizeNaming(ContainerName, name); + return await Provider.GetOrNullAsync( new BlobProviderGetArgs( - ContainerName, + normalizedContainerName, Configuration, - name, + normalizedBlobName, CancellationTokenProvider.FallbackToProvider(cancellationToken) ) ); @@ -195,5 +212,28 @@ namespace Volo.Abp.BlobStoring return CurrentTenant.Id; } + + protected virtual (string, string) NormalizeNaming(string containerName, string blobName) + { + if (!Configuration.NamingNormalizers.Any()) + { + return (containerName, blobName); + } + + using (var scope = ServiceProvider.CreateScope()) + { + foreach (var normalizerType in Configuration.NamingNormalizers) + { + var normalizer = scope.ServiceProvider + .GetRequiredService(normalizerType) + .As(); + + containerName = normalizer.NormalizeContainerName(containerName); + blobName = normalizer.NormalizeBlobName(blobName); + } + + return (containerName, blobName); + } + } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs index 46f13a1c3d..7ca94b4b42 100644 --- a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Generic; using JetBrains.Annotations; +using Volo.Abp.Collections; namespace Volo.Abp.BlobStoring { @@ -18,17 +19,20 @@ namespace Volo.Abp.BlobStoring /// then the container is shared by all tenants in the system. /// /// This can be true even if your application is not multi-tenant. - /// + /// /// Default: true. /// public bool IsMultiTenant { get; set; } = true; + public ITypeList NamingNormalizers { get; } + [NotNull] private readonly Dictionary _properties; [CanBeNull] private readonly BlobContainerConfiguration _fallbackConfiguration; public BlobContainerConfiguration(BlobContainerConfiguration fallbackConfiguration = null) { + NamingNormalizers = new TypeList(); _fallbackConfiguration = fallbackConfiguration; _properties = new Dictionary(); } @@ -68,4 +72,4 @@ namespace Volo.Abp.BlobStoring return this; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs index ab27aab01f..7a4da39ef9 100644 --- a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs @@ -1,4 +1,5 @@ -using Volo.Abp.DependencyInjection; +using System; +using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; using Volo.Abp.Threading; @@ -7,36 +8,41 @@ namespace Volo.Abp.BlobStoring public class BlobContainerFactory : IBlobContainerFactory, ITransientDependency { protected IBlobProviderSelector ProviderSelector { get; } - + protected IBlobContainerConfigurationProvider ConfigurationProvider { get; } protected ICurrentTenant CurrentTenant { get; } - + protected ICancellationTokenProvider CancellationTokenProvider { get; } + protected IServiceProvider ServiceProvider { get; } + public BlobContainerFactory( IBlobContainerConfigurationProvider configurationProvider, ICurrentTenant currentTenant, - ICancellationTokenProvider cancellationTokenProvider, - IBlobProviderSelector providerSelector) + ICancellationTokenProvider cancellationTokenProvider, + IBlobProviderSelector providerSelector, + IServiceProvider serviceProvider) { ConfigurationProvider = configurationProvider; CurrentTenant = currentTenant; CancellationTokenProvider = cancellationTokenProvider; ProviderSelector = providerSelector; + ServiceProvider = serviceProvider; } - + public virtual IBlobContainer Create(string name) { var configuration = ConfigurationProvider.Get(name); - + return new BlobContainer( name, configuration, ProviderSelector.Get(name), CurrentTenant, - CancellationTokenProvider + CancellationTokenProvider, + ServiceProvider ); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobNamingNormalizer.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobNamingNormalizer.cs new file mode 100644 index 0000000000..2bdc03507d --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobNamingNormalizer.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.BlobStoring +{ + public interface IBlobNamingNormalizer + { + string NormalizeContainerName(string containerName); + + string NormalizeBlobName(string blobName); + } +} diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xml b/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xsd b/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj new file mode 100644 index 0000000000..c362c16bff --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj @@ -0,0 +1,25 @@ + + + + + + + netstandard2.0 + Volo.Abp.Caching.StackExchangeRedis + Volo.Abp.Caching.StackExchangeRedis + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs new file mode 100644 index 0000000000..b0f7d68ee4 --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + [DependsOn( + typeof(AbpCachingModule) + )] + public class AbpCachingStackExchangeRedisModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + + context.Services.AddStackExchangeRedisCache(options => + { + var redisConfiguration = configuration["Redis:Configuration"]; + if (!redisConfiguration.IsNullOrEmpty()) + { + options.Configuration = configuration["Redis:Configuration"]; + } + }); + + context.Services.Replace(ServiceDescriptor.Singleton()); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs new file mode 100644 index 0000000000..c3cb34f30c --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Caching.StackExchangeRedis; +using Microsoft.Extensions.Options; +using StackExchange.Redis; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + [DisableConventionalRegistration] + public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems + { + protected static readonly string SetScript; + protected static readonly string AbsoluteExpirationKey; + protected static readonly string SlidingExpirationKey; + protected static readonly string DataKey; + protected static readonly long NotPresent; + + private static readonly FieldInfo RedisDatabaseField; + private static readonly MethodInfo ConnectMethod; + private static readonly MethodInfo ConnectAsyncMethod; + private static readonly MethodInfo MapMetadataMethod; + private static readonly MethodInfo GetAbsoluteExpirationMethod; + private static readonly MethodInfo GetExpirationInSecondsMethod; + + protected IDatabase RedisDatabase => GetRedisDatabase(); + private IDatabase _redisDatabase; + + protected string Instance { get; } + + static AbpRedisCache() + { + var type = typeof(RedisCache); + + RedisDatabaseField = type.GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic); + + ConnectMethod = type.GetMethod("Connect", BindingFlags.Instance | BindingFlags.NonPublic); + + ConnectAsyncMethod = type.GetMethod("ConnectAsync", BindingFlags.Instance | BindingFlags.NonPublic); + + MapMetadataMethod = type.GetMethod("MapMetadata", BindingFlags.Instance | BindingFlags.NonPublic); + + GetAbsoluteExpirationMethod = type.GetMethod("GetAbsoluteExpiration", BindingFlags.Static | BindingFlags.NonPublic); + + GetExpirationInSecondsMethod = type.GetMethod("GetExpirationInSeconds", BindingFlags.Static | BindingFlags.NonPublic); + + SetScript = type.GetField("SetScript", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString(); + + AbsoluteExpirationKey = type.GetField("AbsoluteExpirationKey", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString(); + + SlidingExpirationKey = type.GetField("SlidingExpirationKey", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString(); + + DataKey = type.GetField("DataKey", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString(); + + NotPresent = type.GetField("NotPresent", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).To(); + } + + public AbpRedisCache(IOptions optionsAccessor) + : base(optionsAccessor) + { + Instance = optionsAccessor.Value.InstanceName ?? string.Empty; + } + + protected virtual void Connect() + { + if (GetRedisDatabase() != null) + { + return; + } + + ConnectMethod.Invoke(this, Array.Empty()); + } + + protected virtual Task ConnectAsync(CancellationToken token = default) + { + if (GetRedisDatabase() != null) + { + return Task.CompletedTask; + } + + return (Task) ConnectAsyncMethod.Invoke(this, new object[] {token}); + } + + public byte[][] GetMany( + IEnumerable keys) + { + keys = Check.NotNull(keys, nameof(keys)); + + return GetAndRefreshMany(keys, true); + } + + public async Task GetManyAsync( + IEnumerable keys, + CancellationToken token = default) + { + keys = Check.NotNull(keys, nameof(keys)); + + return await GetAndRefreshManyAsync(keys, true, token); + } + + public void SetMany( + IEnumerable> items, + DistributedCacheEntryOptions options) + { + Connect(); + + Task.WaitAll(PipelineSetMany(items, options)); + } + + public async Task SetManyAsync( + IEnumerable> items, + DistributedCacheEntryOptions options, + CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + + await ConnectAsync(token); + + await Task.WhenAll(PipelineSetMany(items, options)); + } + + protected virtual byte[][] GetAndRefreshMany( + IEnumerable keys, + bool getData) + { + Connect(); + + var keyArray = keys.Select(key => Instance + key).ToArray(); + RedisValue[][] results; + + if (getData) + { + results = RedisDatabase.HashMemberGetMany(keyArray, AbsoluteExpirationKey, + SlidingExpirationKey, DataKey); + } + else + { + results = RedisDatabase.HashMemberGetMany(keyArray, AbsoluteExpirationKey, + SlidingExpirationKey); + } + + Task.WaitAll(PipelineRefreshManyAndOutData(keyArray, results, out var bytes)); + + return bytes; + } + + protected virtual async Task GetAndRefreshManyAsync( + IEnumerable keys, + bool getData, + CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + + await ConnectAsync(token); + + var keyArray = keys.Select(key => Instance + key).ToArray(); + RedisValue[][] results; + + if (getData) + { + results = await RedisDatabase.HashMemberGetManyAsync(keyArray, AbsoluteExpirationKey, + SlidingExpirationKey, DataKey); + } + else + { + results = await RedisDatabase.HashMemberGetManyAsync(keyArray, AbsoluteExpirationKey, + SlidingExpirationKey); + } + + await Task.WhenAll(PipelineRefreshManyAndOutData(keyArray, results, out var bytes)); + + return bytes; + } + + protected virtual Task[] PipelineRefreshManyAndOutData( + string[] keys, + RedisValue[][] results, + out byte[][] bytes) + { + bytes = new byte[keys.Length][]; + var tasks = new Task[keys.Length]; + + for (var i = 0; i < keys.Length; i++) + { + if (results[i].Length >= 2) + { + MapMetadata(results[i], out DateTimeOffset? absExpr, out TimeSpan? sldExpr); + + if (sldExpr.HasValue) + { + TimeSpan? expr; + + if (absExpr.HasValue) + { + var relExpr = absExpr.Value - DateTimeOffset.Now; + expr = relExpr <= sldExpr.Value ? relExpr : sldExpr; + } + else + { + expr = sldExpr; + } + + tasks[i] = RedisDatabase.KeyExpireAsync(keys[i], expr); + } + else + { + tasks[i] = Task.CompletedTask; + } + } + + if (results[i].Length >= 3 && results[i][2].HasValue) + { + bytes[i] = results[i][2]; + } + else + { + bytes[i] = null; + } + } + + return tasks; + } + + protected virtual Task[] PipelineSetMany( + IEnumerable> items, + DistributedCacheEntryOptions options) + { + items = Check.NotNull(items, nameof(items)); + options = Check.NotNull(options, nameof(options)); + + var itemArray = items.ToArray(); + var tasks = new Task[itemArray.Length]; + var creationTime = DateTimeOffset.UtcNow; + var absoluteExpiration = GetAbsoluteExpiration(creationTime, options); + + for (var i = 0; i < itemArray.Length; i++) + { + tasks[i] = RedisDatabase.ScriptEvaluateAsync(SetScript, new RedisKey[] {Instance + itemArray[i].Key}, + new RedisValue[] + { + absoluteExpiration?.Ticks ?? NotPresent, + options.SlidingExpiration?.Ticks ?? NotPresent, + GetExpirationInSeconds(creationTime, absoluteExpiration, options) ?? NotPresent, + itemArray[i].Value + }); + } + + return tasks; + } + + protected virtual void MapMetadata( + RedisValue[] results, + out DateTimeOffset? absoluteExpiration, + out TimeSpan? slidingExpiration) + { + var parameters = new object[] {results, null, null}; + MapMetadataMethod.Invoke(this, parameters); + + absoluteExpiration = (DateTimeOffset?) parameters[1]; + slidingExpiration = (TimeSpan?) parameters[2]; + } + + protected virtual long? GetExpirationInSeconds( + DateTimeOffset creationTime, + DateTimeOffset? absoluteExpiration, + DistributedCacheEntryOptions options) + { + return (long?) GetExpirationInSecondsMethod.Invoke(null, + new object[] {creationTime, absoluteExpiration, options}); + } + + protected virtual DateTimeOffset? GetAbsoluteExpiration( + DateTimeOffset creationTime, + DistributedCacheEntryOptions options) + { + return (DateTimeOffset?) GetAbsoluteExpirationMethod.Invoke(null, new object[] {creationTime, options}); + } + + private IDatabase GetRedisDatabase() + { + if (_redisDatabase == null) + { + _redisDatabase = RedisDatabaseField.GetValue(this) as IDatabase; + } + + return _redisDatabase; + } + } +} diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisExtensions.cs b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisExtensions.cs new file mode 100644 index 0000000000..ad077a7440 --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisExtensions.cs @@ -0,0 +1,47 @@ +using System.Linq; +using System.Threading.Tasks; +using StackExchange.Redis; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + public static class AbpRedisExtensions + { + public static RedisValue[][] HashMemberGetMany( + this IDatabase cache, + string[] keys, + params string[] members) + { + var tasks = new Task[keys.Length]; + var fields = members.Select(member => (RedisValue) member).ToArray(); + var results = new RedisValue[keys.Length][]; + + for (var i = 0; i < keys.Length; i++) + { + tasks[i] = cache.HashGetAsync((RedisKey) keys[i], fields); + } + + for (var i = 0; i < tasks.Length; i++) + { + results[i] = cache.Wait(tasks[i]); + } + + return results; + } + + public static async Task HashMemberGetManyAsync( + this IDatabase cache, + string[] keys, + params string[] members) + { + var tasks = new Task[keys.Length]; + var fields = members.Select(member => (RedisValue) member).ToArray(); + + for (var i = 0; i < keys.Length; i++) + { + tasks[i] = cache.HashGetAsync((RedisKey) keys[i], fields); + } + + return await Task.WhenAll(tasks); + } + } +} diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs index 267ab259f5..5bb1dd2481 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -28,16 +31,16 @@ namespace Volo.Abp.Caching IDistributedCacheSerializer serializer, IDistributedCacheKeyNormalizer keyNormalizer, IHybridServiceScopeFactory serviceScopeFactory) : base( - distributedCacheOption: distributedCacheOption, - cache: cache, - cancellationTokenProvider: cancellationTokenProvider, - serializer: serializer, - keyNormalizer: keyNormalizer, - serviceScopeFactory: serviceScopeFactory) + distributedCacheOption: distributedCacheOption, + cache: cache, + cancellationTokenProvider: cancellationTokenProvider, + serializer: serializer, + keyNormalizer: keyNormalizer, + serviceScopeFactory: serviceScopeFactory) { } - } + /// /// Represents a distributed cache of type. /// Uses a generic cache key type of type. @@ -148,19 +151,151 @@ namespace Volo.Abp.Caching { if (hideErrors == true) { - AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); + HandleException(ex); return null; } throw; } - if (cachedBytes == null) + return ToCacheItem(cachedBytes); + } + + public virtual KeyValuePair[] GetMany( + IEnumerable keys, + bool? hideErrors = null) + { + var keyArray = keys.ToArray(); + + var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems; + if (cacheSupportsMultipleItems == null) { - return null; + return GetManyFallback( + keyArray, + hideErrors + ); } - return Serializer.Deserialize(cachedBytes); + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + byte[][] cachedBytes; + + try + { + cachedBytes = cacheSupportsMultipleItems.GetMany(keyArray.Select(NormalizeKey)); + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return ToCacheItemsWithDefaultValues(keyArray); + } + + throw; + } + + return ToCacheItems(cachedBytes, keyArray); + } + + protected virtual KeyValuePair[] GetManyFallback( + TCacheKey[] keys, + bool? hideErrors = null) + { + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + return keys + .Select(key => new KeyValuePair( + key, + Get(key, hideErrors: false) + ) + ).ToArray(); + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return ToCacheItemsWithDefaultValues(keys); + } + + throw; + } + } + + public virtual async Task[]> GetManyAsync( + IEnumerable keys, + bool? hideErrors = null, + CancellationToken token = default) + { + var keyArray = keys.ToArray(); + + var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems; + if (cacheSupportsMultipleItems == null) + { + return await GetManyFallbackAsync( + keyArray, + hideErrors, + token + ); + } + + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + byte[][] cachedBytes; + + try + { + cachedBytes = await cacheSupportsMultipleItems.GetManyAsync( + keyArray.Select(NormalizeKey), + CancellationTokenProvider.FallbackToProvider(token) + ); + } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return ToCacheItemsWithDefaultValues(keyArray); + } + + throw; + } + + return ToCacheItems(cachedBytes, keyArray); + } + + protected virtual async Task[]> GetManyFallbackAsync( + TCacheKey[] keys, + bool? hideErrors = null, + CancellationToken token = default) + { + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + var result = new List>(); + + foreach (var key in keys) + { + result.Add(new KeyValuePair( + key, + await GetAsync(key, false, token)) + ); + } + + return result.ToArray(); + } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return ToCacheItemsWithDefaultValues(keys); + } + + throw; + } } /// @@ -307,7 +442,7 @@ namespace Volo.Abp.Caching { if (hideErrors == true) { - AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); + HandleException(ex); return; } @@ -354,14 +489,156 @@ namespace Volo.Abp.Caching } } - /// - /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. - /// - /// The key of cached item to be retrieved from the cache. - /// Indicates to throw or hide the exceptions for the distributed cache. + public void SetMany( + IEnumerable> items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null) + { + var itemsArray = items.ToArray(); + + var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems; + if (cacheSupportsMultipleItems == null) + { + SetManyFallback( + itemsArray, + options, + hideErrors + ); + + return; + } + + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + cacheSupportsMultipleItems.SetMany( + ToRawCacheItems(itemsArray), + options ?? DefaultCacheOptions + ); + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return; + } + + throw; + } + } + + protected virtual void SetManyFallback( + KeyValuePair[] items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null) + { + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + foreach (var item in items) + { + Set( + item.Key, + item.Value, + options: options, + hideErrors: false + ); + } + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return; + } + + throw; + } + } + + public virtual async Task SetManyAsync( + IEnumerable> items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null, + CancellationToken token = default) + { + var itemsArray = items.ToArray(); + + var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems; + if (cacheSupportsMultipleItems == null) + { + await SetManyFallbackAsync( + itemsArray, + options, + hideErrors, + token + ); + + return; + } + + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + await cacheSupportsMultipleItems.SetManyAsync( + ToRawCacheItems(itemsArray), + options ?? DefaultCacheOptions, + CancellationTokenProvider.FallbackToProvider(token) + ); + } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return; + } + + throw; + } + } + + protected virtual async Task SetManyFallbackAsync( + KeyValuePair[] items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null, + CancellationToken token = default) + { + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + foreach (var item in items) + { + await SetAsync( + item.Key, + item.Value, + options: options, + hideErrors: false, + token: token + ); + } + } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return; + } + + throw; + } + } + public virtual void Refresh( TCacheKey key, bool? - hideErrors = null) + hideErrors = null) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -373,20 +650,14 @@ namespace Volo.Abp.Caching { if (hideErrors == true) { - AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); + HandleException(ex); return; } throw; } } - /// - /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. - /// - /// The key of cached item to be retrieved from the cache. - /// Indicates to throw or hide the exceptions for the distributed cache. - /// The for the task. - /// The indicating that the operation is asynchronous. + public virtual async Task RefreshAsync( TCacheKey key, bool? hideErrors = null, @@ -410,11 +681,6 @@ namespace Volo.Abp.Caching } } - /// - /// Removes the cache item for given key from cache. - /// - /// The key of cached item to be retrieved from the cache. - /// Indicates to throw or hide the exceptions for the distributed cache. public virtual void Remove( TCacheKey key, bool? hideErrors = null) @@ -429,7 +695,7 @@ namespace Volo.Abp.Caching { if (hideErrors == true) { - AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); + HandleException(ex); return; } @@ -437,13 +703,6 @@ namespace Volo.Abp.Caching } } - /// - /// Removes the cache item for given key from cache. - /// - /// The key of cached item to be retrieved from the cache. - /// Indicates to throw or hide the exceptions for the distributed cache. - /// The for the task. - /// The indicating that the operation is asynchronous. public virtual async Task RemoveAsync( TCacheKey key, bool? hideErrors = null, @@ -467,6 +726,11 @@ namespace Volo.Abp.Caching } } + protected virtual void HandleException(Exception ex) + { + AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); + } + protected virtual async Task HandleExceptionAsync(Exception ex) { Logger.LogException(ex, LogLevel.Warning); @@ -478,5 +742,56 @@ namespace Volo.Abp.Caching .NotifyAsync(new ExceptionNotificationContext(ex, LogLevel.Warning)); } } + + protected virtual KeyValuePair[] ToCacheItems(byte[][] itemBytes, TCacheKey[] itemKeys) + { + if (itemBytes.Length != itemKeys.Length) + { + throw new AbpException("count of the item bytes should be same with the count of the given keys"); + } + + var result = new List>(); + + for (int i = 0; i < itemKeys.Length; i++) + { + result.Add( + new KeyValuePair( + itemKeys[i], + ToCacheItem(itemBytes[i]) + ) + ); + } + + return result.ToArray(); + } + + [CanBeNull] + protected virtual TCacheItem ToCacheItem([CanBeNull] byte[] bytes) + { + if (bytes == null) + { + return null; + } + + return Serializer.Deserialize(bytes); + } + + + protected virtual KeyValuePair[] ToRawCacheItems(KeyValuePair[] items) + { + return items + .Select(i => new KeyValuePair( + NormalizeKey(i.Key), + Serializer.Serialize(i.Value) + ) + ).ToArray(); + } + + private static KeyValuePair[] ToCacheItemsWithDefaultValues(TCacheKey[] keys) + { + return keys + .Select(key => new KeyValuePair(key, default)) + .ToArray(); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs new file mode 100644 index 0000000000..af6f87021b --- /dev/null +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Distributed; + +namespace Volo.Abp.Caching +{ + public interface ICacheSupportsMultipleItems + { + byte[][] GetMany( + IEnumerable keys + ); + + Task GetManyAsync( + IEnumerable keys, + CancellationToken token = default + ); + + void SetMany( + IEnumerable> items, + DistributedCacheEntryOptions options + ); + + Task SetManyAsync( + IEnumerable> items, + DistributedCacheEntryOptions options, + CancellationToken token = default + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs index 163da05632..80fe124785 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; @@ -34,6 +35,39 @@ namespace Volo.Abp.Caching TCacheKey key, bool? hideErrors = null ); + + /// + /// Gets multiple cache items with the given keys. + /// + /// The returned list contains exactly the same count of items specified in the given keys. + /// An item in the return list can not be null, but an item in the list has null value + /// if the related key not found in the cache. + /// + /// The keys of cached items to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// List of cache items. + KeyValuePair[] GetMany( + IEnumerable keys, + bool? hideErrors = null + ); + + /// + /// Gets multiple cache items with the given keys. + /// + /// The returned list contains exactly the same count of items specified in the given keys. + /// An item in the return list can not be null, but an item in the list has null value + /// if the related key not found in the cache. + /// + /// + /// The keys of cached items to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// /// The for the task. + /// List of cache items. + Task[]> GetManyAsync( + IEnumerable keys, + bool? hideErrors = null, + CancellationToken token = default + ); /// /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. @@ -113,6 +147,35 @@ namespace Volo.Abp.Caching CancellationToken token = default ); + /// + /// Sets multiple cache items. + /// Based on the implementation, this can be more efficient than setting multiple items individually. + /// + /// Items to set on the cache + /// The cache options for the value. + /// Indicates to throw or hide the exceptions for the distributed cache. + void SetMany( + IEnumerable> items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null + ); + + /// + /// Sets multiple cache items. + /// Based on the implementation, this can be more efficient than setting multiple items individually. + /// + /// Items to set on the cache + /// The cache options for the value. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The indicating that the operation is asynchronous. + Task SetManyAsync( + IEnumerable> items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null, + CancellationToken token = default + ); + /// /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. /// diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs index 2b524d9272..d148346836 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs @@ -51,7 +51,7 @@ namespace Volo.Abp.Cli.Commands var apiUrl = commandLineArgs.Options.GetOrNull(Options.ApiUrl.Short, Options.ApiUrl.Long); if (string.IsNullOrWhiteSpace(apiUrl)) { - var environmentJson = File.ReadAllText("src/environments/environment.ts").Split("export const environment = ")[1].Replace(";", " "); + var environmentJson = File.ReadAllText("projects/dev-app/src/environments/environment.ts").Split("export const environment = ")[1].Replace(";", " "); var environment = JObject.Parse(environmentJson); apiUrl = environment["apis"]["default"]["url"].ToString(); } @@ -61,7 +61,7 @@ namespace Volo.Abp.Cli.Commands output = commandLineArgs.Options.GetOrNull(Options.Output.Short, Options.Output.Long); if (!string.IsNullOrWhiteSpace(output) && !(output.EndsWith("/") || output.EndsWith("\\"))) - { + { output += "/"; } @@ -110,7 +110,7 @@ namespace Volo.Abp.Cli.Commands if (rootPath != "app") { Logger.LogInformation($"{rootPath} directory is creating"); - } + } if (rootPath == "app") { @@ -536,7 +536,7 @@ namespace Volo.Abp.Cli.Commands modelFileText.AppendLine(Environment.NewLine); modelFileText.AppendLine($"import {{ {baseTypeName} }} from '{baseTypeKebabCase}';"); - + extends = "extends " + (!string.IsNullOrWhiteSpace(customBaseTypeName) ? customBaseTypeName : baseTypeName); var modelIndex = CreateType(data, baseType, rootPath, modelIndexList, controllerPathName); @@ -628,7 +628,7 @@ namespace Volo.Abp.Cli.Commands { from = "./" + propertyTypeKebabCase; } - + modelFileText.Insert(0, $"import {{ {propertyType} }} from '{from}';"); modelFileText.Insert(0, Environment.NewLine); modelIndexList.Add(modelIndex); @@ -816,4 +816,4 @@ namespace Volo.Abp.Cli.Commands public bool IsOptional { get; set; } public string BindingSourceId { get; set; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs index b4a4ff8bdd..fbe7fa675f 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs @@ -59,15 +59,33 @@ namespace Volo.Abp.Cli.Commands return; } - var result = CmdHelper.RunCmd("dotnet tool install " + SuitePackageName + " --add-source " + nugetIndexUrl + " -g"); + try + { + var result = CmdHelper.RunCmd("dotnet tool install " + SuitePackageName + " --add-source " + nugetIndexUrl + " -g"); - if (result == 0) + if (result == 0) + { + Logger.LogInformation("ABP Suite has been successfully installed."); + Logger.LogInformation("You can run it with the CLI command \"abp suite\""); + } + else + { + ShowSuiteManualInstallCommand(); + } + } + catch (Exception e) { - Logger.LogInformation("ABP Suite has been successfully installed."); - Logger.LogInformation("You can run it with the CLI command \"abp suite\""); + Logger.LogError("Couldn't install ABP Suite." + e.Message); + ShowSuiteManualInstallCommand(); } } + private void ShowSuiteManualInstallCommand() + { + Logger.LogInformation("You can also run the following command to install ABP Suite."); + Logger.LogInformation("dotnet tool install -g Volo.Abp.Suite"); + } + private async Task UpdateSuiteAsync() { var nugetIndexUrl = await _nuGetIndexUrlService.GetAsync(); @@ -77,7 +95,26 @@ namespace Volo.Abp.Cli.Commands return; } - CmdHelper.RunCmd("dotnet tool update " + SuitePackageName + " --add-source " + nugetIndexUrl + " -g"); + try + { + var result = CmdHelper.RunCmd("dotnet tool update " + SuitePackageName + " --add-source " + nugetIndexUrl + " -g"); + + if (result != 0) + { + ShowSuiteManualUpdateCommand(); + } + } + catch (Exception ex) + { + Logger.LogError("Couldn't update ABP Suite." + ex.Message); + ShowSuiteManualUpdateCommand(); + } + } + + private void ShowSuiteManualUpdateCommand() + { + Logger.LogError("You can also run the following command to update ABP Suite."); + Logger.LogError("dotnet tool update -g Volo.Abp.Suite"); } private static void RemoveSuite() diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AngularEnvironmentFilePortChangeForSeparatedIdentityServersStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AngularEnvironmentFilePortChangeForSeparatedIdentityServersStep.cs index 9a76f9ee21..b69ce479ac 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AngularEnvironmentFilePortChangeForSeparatedIdentityServersStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AngularEnvironmentFilePortChangeForSeparatedIdentityServersStep.cs @@ -10,9 +10,9 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App { var fileEntries = context.Files.Where(x => !x.IsDirectory && - (x.Name.EndsWith("angular/src/environments/environment.ts", StringComparison.InvariantCultureIgnoreCase) || - x.Name.EndsWith("angular/src/environments/environment.hmr.ts", StringComparison.InvariantCultureIgnoreCase) || - x.Name.EndsWith("angular/src/environments/environment.prod.ts", StringComparison.InvariantCultureIgnoreCase)) + (x.Name.EndsWith("angular/projects/dev-app/src/environments/environment.ts", StringComparison.InvariantCultureIgnoreCase) || + x.Name.EndsWith("angular/projects/dev-app/src/environments/environment.hmr.ts", StringComparison.InvariantCultureIgnoreCase) || + x.Name.EndsWith("angular/projects/dev-app/src/environments/environment.prod.ts", StringComparison.InvariantCultureIgnoreCase)) ) .ToList(); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs index 02ae0040bc..752adad54e 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs @@ -54,11 +54,12 @@ namespace Volo.Abp.Cli.ProjectModification _npmGlobalPackagesChecker.Check(); var packagesUpdated = new ConcurrentDictionary(); + async Task UpdateAsync(string file) { var updated = await UpdatePackagesInFile(file, includePreviews, switchToStable); packagesUpdated.TryAdd(file, updated); - }; + } Task.WaitAll(fileList.Select(UpdateAsync).ToArray()); @@ -66,23 +67,20 @@ namespace Volo.Abp.Cli.ProjectModification { var fileDirectory = Path.GetDirectoryName(file.Key).EnsureEndsWith(Path.DirectorySeparatorChar); - if (IsAngularProject(fileDirectory)) + if (includePreviews) { - if (includePreviews) - { - await CreateNpmrcFileAsync(Path.GetDirectoryName(file.Key)); - } - else if (switchToStable) - { - await DeleteNpmrcFileAsync(Path.GetDirectoryName(file.Key)); - } + await CreateNpmrcFileAsync(Path.GetDirectoryName(file.Key)); + RunNpmInstall(fileDirectory); + } + else if (switchToStable) + { + await DeleteNpmrcFileAsync(Path.GetDirectoryName(file.Key)); + RunYarn(fileDirectory); } - - RunYarn(fileDirectory); if (!IsAngularProject(fileDirectory)) { - Thread.Sleep(500); + Thread.Sleep(1000); RunGulp(fileDirectory); } } @@ -166,7 +164,8 @@ namespace Volo.Abp.Cli.ProjectModification return File.Exists(Path.Combine(fileDirectory, "angular.json")); } - protected virtual async Task UpdatePackagesInFile(string filePath, bool includePreviews = false, bool switchToStable = false) + protected virtual async Task UpdatePackagesInFile(string filePath, bool includePreviews = false, + bool switchToStable = false) { var packagesUpdated = false; var fileContent = File.ReadAllText(filePath); @@ -201,7 +200,7 @@ namespace Volo.Abp.Cli.ProjectModification bool includePreviews = false, bool switchToStable = false) { - var currentVersion = (string)package.Value; + var currentVersion = (string) package.Value; var version = await GetLatestVersion(package, currentVersion, includePreviews, switchToStable); @@ -214,7 +213,8 @@ namespace Volo.Abp.Cli.ProjectModification package.Value.Replace(versionWithPrefix); - Logger.LogInformation($"Updated {package.Name} to {version} in {filePath.Replace(Directory.GetCurrentDirectory(), "")}."); + Logger.LogInformation( + $"Updated {package.Name} to {version} in {filePath.Replace(Directory.GetCurrentDirectory(), "")}."); return true; } @@ -256,12 +256,12 @@ namespace Volo.Abp.Cli.ProjectModification protected virtual List GetAbpPackagesFromPackageJson(JObject fileObject) { - var dependencyList = new[] { "dependencies", "devDependencies", "peerDependencies" }; + var dependencyList = new[] {"dependencies", "devDependencies", "peerDependencies"}; var abpPackages = new List(); foreach (var dependencyListName in dependencyList) { - var dependencies = (JObject)fileObject[dependencyListName]; + var dependencies = (JObject) fileObject[dependencyListName]; if (dependencies == null) { @@ -269,7 +269,8 @@ namespace Volo.Abp.Cli.ProjectModification } var properties = dependencies.Properties().ToList(); - abpPackages.AddRange(properties.Where(p => p.Name.StartsWith("@abp/") || p.Name.StartsWith("@volo/")).ToList()); + abpPackages.AddRange(properties.Where(p => p.Name.StartsWith("@abp/") || p.Name.StartsWith("@volo/")) + .ToList()); } return abpPackages; @@ -286,5 +287,11 @@ namespace Volo.Abp.Cli.ProjectModification Logger.LogInformation($"Running Yarn on {fileDirectory}"); CmdHelper.RunCmd($"cd {fileDirectory} && yarn"); } + + protected virtual void RunNpmInstall(string fileDirectory) + { + Logger.LogInformation($"Running npm install on {fileDirectory}"); + CmdHelper.RunCmd($"cd {fileDirectory} && npm install"); + } } } diff --git a/framework/src/Volo.Abp.Cli/Properties/launchSettings.json b/framework/src/Volo.Abp.Cli/Properties/launchSettings.json deleted file mode 100644 index e7f99eea04..0000000000 --- a/framework/src/Volo.Abp.Cli/Properties/launchSettings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "profiles": { - "Volo.Abp.Cli": { - "commandName": "Project" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/System/Runtime/IOSPlatformProvider.cs b/framework/src/Volo.Abp.Core/System/Runtime/IOSPlatformProvider.cs new file mode 100644 index 0000000000..60240159f3 --- /dev/null +++ b/framework/src/Volo.Abp.Core/System/Runtime/IOSPlatformProvider.cs @@ -0,0 +1,9 @@ +using System.Runtime.InteropServices; + +namespace System.Runtime +{ + public interface IOSPlatformProvider + { + OSPlatform GetCurrentOSPlatform(); + } +} diff --git a/framework/src/Volo.Abp.Core/System/Runtime/OSPlatformProvider.cs b/framework/src/Volo.Abp.Core/System/Runtime/OSPlatformProvider.cs new file mode 100644 index 0000000000..89b57acaab --- /dev/null +++ b/framework/src/Volo.Abp.Core/System/Runtime/OSPlatformProvider.cs @@ -0,0 +1,23 @@ +using System.Runtime.InteropServices; +using Volo.Abp.DependencyInjection; + +namespace System.Runtime +{ + public class OSPlatformProvider : IOSPlatformProvider, ITransientDependency + { + public virtual OSPlatform GetCurrentOSPlatform() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return OSPlatform.OSX; //MAC + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return OSPlatform.Windows; + } + + return OSPlatform.Linux; + } + } +} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Collections/ITypeList.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Collections/ITypeList.cs index 6d53ddc954..8906859edf 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Collections/ITypeList.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Collections/ITypeList.cs @@ -27,7 +27,7 @@ namespace Volo.Abp.Collections /// Adds a type to list if it's not already in the list. /// /// Type - void TryAdd() where T : TBaseType; + bool TryAdd() where T : TBaseType; /// /// Checks if a type exists in the list. diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Collections/TypeList.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Collections/TypeList.cs index 24e66085f7..1f4b7ddc40 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Collections/TypeList.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Collections/TypeList.cs @@ -60,14 +60,15 @@ namespace Volo.Abp.Collections _typeList.Add(typeof(T)); } - public void TryAdd() where T : TBaseType + public bool TryAdd() where T : TBaseType { if (Contains()) { - return; + return false; } Add(); + return true; } /// diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicStringLengthAttribute.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicStringLengthAttribute.cs new file mode 100644 index 0000000000..d27a04b1ba --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicStringLengthAttribute.cs @@ -0,0 +1,58 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Reflection; +using JetBrains.Annotations; + +namespace Volo.Abp.Validation +{ + /// + /// Used to determine and + /// properties on the runtime. + /// + public class DynamicStringLengthAttribute : StringLengthAttribute + { + private static readonly FieldInfo MaximumLengthField; + + static DynamicStringLengthAttribute() + { + MaximumLengthField = typeof(StringLengthAttribute).GetField( + "k__BackingField", + BindingFlags.Instance | BindingFlags.NonPublic + ); + Debug.Assert(MaximumLengthField != null, nameof(MaximumLengthField) + " != null"); + } + + /// A type to get the values of the properties + /// The name of the public static property for the + /// The name of the public static property for the + public DynamicStringLengthAttribute( + [NotNull] Type sourceType, + [CanBeNull] string maximumLengthPropertyName, + [CanBeNull] string minimumLengthPropertyName = null) + : base(0) + { + Check.NotNull(sourceType, nameof(sourceType)); + + if (maximumLengthPropertyName != null) + { + var maximumLengthProperty = sourceType.GetProperty( + maximumLengthPropertyName, + BindingFlags.Static | BindingFlags.Public + ); + Debug.Assert(maximumLengthProperty != null, nameof(maximumLengthProperty) + " != null"); + MaximumLengthField.SetValue(this, (int) maximumLengthProperty.GetValue(null)); + } + + if (minimumLengthPropertyName != null) + { + var minimumLengthProperty = sourceType.GetProperty( + minimumLengthPropertyName, + BindingFlags.Static | BindingFlags.Public + ); + Debug.Assert(minimumLengthProperty != null, nameof(minimumLengthProperty) + " != null"); + MinimumLength = (int) minimumLengthProperty.GetValue(null); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs index 8db091eb30..796c6e20fb 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; @@ -241,6 +242,11 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory } } + if (IsBaseAuditProperty(propertyInfo, entityType)) + { + return false; + } + var isModified = !(propertyEntry.OriginalValue?.Equals(propertyEntry.CurrentValue) ?? propertyEntry.CurrentValue == null); if (isModified) { @@ -250,6 +256,59 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory return defaultValue; } + private bool IsBaseAuditProperty(PropertyInfo propertyInfo, Type entityType) + { + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IHasCreationTime.CreationTime)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IMayHaveCreator.CreatorId)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IMustHaveCreator.CreatorId)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IHasModificationTime.LastModificationTime)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IModificationAuditedObject.LastModifierId)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(ISoftDelete.IsDeleted)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IHasDeletionTime.DeletionTime)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IDeletionAuditedObject.DeleterId)) + { + return true; + } + + return false; + } + /// /// Updates change time, entity id and foreign keys after SaveChanges is called. /// @@ -313,4 +372,4 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory } } } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Http.Client.IdentityModel.Web/Properties/launchSettings.json b/framework/src/Volo.Abp.Http.Client.IdentityModel.Web/Properties/launchSettings.json deleted file mode 100644 index d56e5c65b7..0000000000 --- a/framework/src/Volo.Abp.Http.Client.IdentityModel.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:52306/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Http.Client.IdentityModel": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:52307/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Http.Client.IdentityModel/Properties/launchSettings.json b/framework/src/Volo.Abp.Http.Client.IdentityModel/Properties/launchSettings.json deleted file mode 100644 index d56e5c65b7..0000000000 --- a/framework/src/Volo.Abp.Http.Client.IdentityModel/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:52306/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Http.Client.IdentityModel": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:52307/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Http.Client/Properties/launchSettings.json b/framework/src/Volo.Abp.Http.Client/Properties/launchSettings.json deleted file mode 100644 index 3cef3cf668..0000000000 --- a/framework/src/Volo.Abp.Http.Client/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:52208/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Http.Client": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:52209/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs new file mode 100644 index 0000000000..1790c8af6b --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using MongoDB.Driver; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.Domain.Repositories.MongoDB +{ + public interface IMongoDbRepositoryFilterer where TEntity : class, IEntity + { + void AddGlobalFilters(List> filters); + } + + public interface IMongoDbRepositoryFilterer : IMongoDbRepositoryFilterer where TEntity : class, IEntity + { + FilterDefinition CreateEntityFilter(TKey id, bool applyFilters = false); + + FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index 3ac6ab5239..e8477f20e9 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -14,7 +14,6 @@ using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Local; using Volo.Abp.Guids; using Volo.Abp.MongoDB; -using Volo.Abp.MultiTenancy; namespace Volo.Abp.Domain.Repositories.MongoDB { @@ -331,6 +330,8 @@ namespace Volo.Abp.Domain.Repositories.MongoDB where TMongoDbContext : IAbpMongoDbContext where TEntity : class, IEntity { + public IMongoDbRepositoryFilterer RepositoryFilterer { get; set; } + public MongoDbRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) { @@ -358,7 +359,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB CancellationToken cancellationToken = default) { return await Collection - .Find(CreateEntityFilter(id, true)) + .Find(RepositoryFilterer.CreateEntityFilter(id, true)) .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } @@ -372,49 +373,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB protected override FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null) { - if (!withConcurrencyStamp || !(entity is IHasConcurrencyStamp entityWithConcurrencyStamp)) - { - return Builders.Filter.Eq(e => e.Id, entity.Id); - } - - if (concurrencyStamp == null) - { - concurrencyStamp = entityWithConcurrencyStamp.ConcurrencyStamp; - } - - return Builders.Filter.And( - Builders.Filter.Eq(e => e.Id, entity.Id), - Builders.Filter.Eq(e => ((IHasConcurrencyStamp)e).ConcurrencyStamp, concurrencyStamp) - ); - } - - protected virtual FilterDefinition CreateEntityFilter(TKey id, bool applyFilters = false) - { - var filters = new List> - { - Builders.Filter.Eq(e => e.Id, id) - }; - - if (applyFilters) - { - AddGlobalFilters(filters); - } - - return Builders.Filter.And(filters); - } - - protected virtual void AddGlobalFilters(List> filters) - { - if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && DataFilter.IsEnabled()) - { - filters.Add(Builders.Filter.Eq(e => ((ISoftDelete)e).IsDeleted, false)); - } - - if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))) - { - var tenantId = CurrentTenant.Id; - filters.Add(Builders.Filter.Eq(e => ((IMultiTenant)e).TenantId, tenantId)); - } + return RepositoryFilterer.CreateEntityFilter(entity, withConcurrencyStamp, concurrencyStamp); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepositoryFilterer.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepositoryFilterer.cs new file mode 100644 index 0000000000..4c07c747f3 --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepositoryFilterer.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.Domain.Repositories.MongoDB +{ + public class MongoDbRepositoryFilterer : IMongoDbRepositoryFilterer + where TEntity : class, IEntity + { + protected IDataFilter DataFilter { get; } + + protected ICurrentTenant CurrentTenant { get; } + + public MongoDbRepositoryFilterer(IDataFilter dataFilter, ICurrentTenant currentTenant) + { + DataFilter = dataFilter; + CurrentTenant = currentTenant; + } + + public virtual void AddGlobalFilters(List> filters) + { + if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && DataFilter.IsEnabled()) + { + filters.Add(Builders.Filter.Eq(e => ((ISoftDelete) e).IsDeleted, false)); + } + + if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))) + { + var tenantId = CurrentTenant.Id; + filters.Add(Builders.Filter.Eq(e => ((IMultiTenant) e).TenantId, tenantId)); + } + } + } + + public class MongoDbRepositoryFilterer : MongoDbRepositoryFilterer, + IMongoDbRepositoryFilterer + where TEntity : class, IEntity + { + public MongoDbRepositoryFilterer(IDataFilter dataFilter, ICurrentTenant currentTenant) + : base(dataFilter, currentTenant) + { + } + + public FilterDefinition CreateEntityFilter(TKey id, bool applyFilters = false) + { + var filters = new List> + { + Builders.Filter.Eq(e => e.Id, id) + }; + + if (applyFilters) + { + AddGlobalFilters(filters); + } + + return Builders.Filter.And(filters); + } + + public FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null) + { + if (!withConcurrencyStamp || !(entity is IHasConcurrencyStamp entityWithConcurrencyStamp)) + { + return Builders.Filter.Eq(e => e.Id, entity.Id); + } + + if (concurrencyStamp == null) + { + concurrencyStamp = entityWithConcurrencyStamp.ConcurrencyStamp; + } + + return Builders.Filter.And( + Builders.Filter.Eq(e => e.Id, entity.Id), + Builders.Filter.Eq(e => ((IHasConcurrencyStamp) e).ConcurrencyStamp, concurrencyStamp) + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs index d3e8055a22..e36e972954 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Domain; +using Volo.Abp.Domain.Repositories.MongoDB; using Volo.Abp.Modularity; using Volo.Abp.Uow.MongoDB; @@ -14,6 +15,16 @@ namespace Volo.Abp.MongoDB typeof(IMongoDbContextProvider<>), typeof(UnitOfWorkMongoDbContextProvider<>) ); + + context.Services.TryAddTransient( + typeof(IMongoDbRepositoryFilterer<>), + typeof(MongoDbRepositoryFilterer<>) + ); + + context.Services.TryAddTransient( + typeof(IMongoDbRepositoryFilterer<,>), + typeof(MongoDbRepositoryFilterer<,>) + ); } } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs index 57cb40219b..ae622acee2 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Shouldly; +using Volo.Abp.Validation; namespace Volo.Abp.AspNetCore.Mvc.Validation { @@ -31,6 +32,14 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation { return Content("ModelState.IsValid: " + ModelState.IsValid.ToString().ToLowerInvariant()); } + + [HttpGet] + [Route("object-result-action-dynamic-length")] + public Task ObjectResultActionDynamicLength(ValidationDynamicTestModel model) + { + ModelState.IsValid.ShouldBeTrue(); //AbpValidationFilter throws exception otherwise + return Task.FromResult(model.Value1); + } public class ValidationTest1Model { @@ -38,6 +47,18 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation [StringLength(5, MinimumLength = 2)] public string Value1 { get; set; } } + + public class ValidationDynamicTestModel + { + [DynamicStringLength(typeof(Consts), nameof(Consts.MaxValue2Length), nameof(Consts.MinValue2Length))] + public string Value1 { get; set; } + + public static class Consts + { + public static int MinValue2Length { get; set; } = 2; + public static int MaxValue2Length { get; set; } = 7; + } + } public class CustomValidateModel : IValidatableObject { @@ -51,6 +72,5 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation } } } - } -} +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs index 99e027f85a..767ff7aa3e 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs @@ -58,5 +58,21 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation result.Error.ValidationErrors.ShouldContain(x => x.Message == "Value1 should be hello"); } + [Fact] + public async Task Should_Validate_Dynamic_Length_Object_Result_Success() + { + var result = await GetResponseAsStringAsync("/api/validation-test/object-result-action-dynamic-length?value1=hello"); + result.ShouldBe("hello"); + } + + [Fact] + public async Task Should_Validate_Dynamic_Length_Object_Result_Failing() + { + var result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action-dynamic-length?value1=a", HttpStatusCode.BadRequest); //value1 has min length of 2 chars. + result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); + + result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action-dynamic-length?value1=12345678", HttpStatusCode.BadRequest); //value1 has max length of 7 chars. + result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); + } } } diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAuditedAndHasCustomAuditingProperties.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAuditedAndHasCustomAuditingProperties.cs new file mode 100644 index 0000000000..9ff57b286d --- /dev/null +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAuditedAndHasCustomAuditingProperties.cs @@ -0,0 +1,26 @@ +using System; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.Auditing.App.Entities +{ + [Audited] + public class AppEntityWithAuditedAndHasCustomAuditingProperties : AggregateRoot + { + protected AppEntityWithAuditedAndHasCustomAuditingProperties() + { + } + + public AppEntityWithAuditedAndHasCustomAuditingProperties(Guid id) + : base(id) + { + } + + public DateTime? CreationTime { get; set; } + public Guid? CreatorId { get; set; } + public DateTime? LastModificationTime { get; set; } + public Guid? LastModifierId { get; set; } + public bool IsDeleted { get; set; } + public DateTime? DeletionTime { get; set; } + public Guid? DeleterId { get; set; } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppFullAuditedEntityWithAudited.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppFullAuditedEntityWithAudited.cs new file mode 100644 index 0000000000..def7e0d44c --- /dev/null +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppFullAuditedEntityWithAudited.cs @@ -0,0 +1,21 @@ +using System; +using Volo.Abp.Domain.Entities.Auditing; + +namespace Volo.Abp.Auditing.App.Entities +{ + [Audited] + public class AppFullAuditedEntityWithAudited : FullAuditedAggregateRoot + { + protected AppFullAuditedEntityWithAudited() + { + } + + public AppFullAuditedEntityWithAudited(Guid id, string name) + : base(id) + { + Name = name; + } + + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs index 0968bf2cc2..d6b2d2b167 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs @@ -17,6 +17,10 @@ namespace Volo.Abp.Auditing.App.EntityFrameworkCore public DbSet AppEntityWithPropertyHasAudited { get; set; } public DbSet AppEntityWithSelector { get; set; } + + public DbSet AppFullAuditedEntityWithAudited { get; set; } + + public DbSet AppEntityWithAuditedAndHasCustomAuditingProperties { get; set; } public AbpAuditingTestDbContext(DbContextOptions options) : base(options) diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs index 2887eb8510..8c647c92c4 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -215,5 +216,63 @@ namespace Volo.Abp.Auditing #pragma warning restore 4014 } + private static List GetBaseAuditPropertyNames() + { + return new List + { + nameof(IHasCreationTime.CreationTime), + nameof(IMustHaveCreator.CreatorId), + nameof(IHasModificationTime.LastModificationTime), + nameof(IModificationAuditedObject.LastModifierId), + nameof(ISoftDelete.IsDeleted), + nameof(IHasDeletionTime.DeletionTime), + nameof(IDeletionAuditedObject.DeleterId) + }; + } + + [Fact] + public virtual async Task Should_Write_AuditLog_Ignoring_Base_Auditing_Properties_For_Entity_That_Has_Audited_Attribute() + { + using (var scope = _auditingManager.BeginScope()) + { + var repository = ServiceProvider.GetRequiredService>(); + await repository.InsertAsync(new AppFullAuditedEntityWithAudited(Guid.NewGuid(), "test name")); + await scope.SaveAsync(); + } + +#pragma warning disable 4014 + _auditingStore.Received().SaveAsync(Arg.Is(x => x.EntityChanges.Count == 1 + && x.EntityChanges[0].PropertyChanges.Any(y => + !GetBaseAuditPropertyNames().Contains(y.PropertyName)))); +#pragma warning restore 4014 + } + + [Fact] + public virtual async Task Should_Write_AuditLog_Including_Custom_Base_Auditing_Properties_For_Entity_That_Has_Audited_Attribute() + { + using (var scope = _auditingManager.BeginScope()) + { + var repository = ServiceProvider.GetRequiredService>(); + await repository.InsertAsync(new AppEntityWithAuditedAndHasCustomAuditingProperties(Guid.NewGuid()) + { + CreationTime = DateTime.Now, + CreatorId = Guid.NewGuid(), + LastModificationTime = DateTime.Now, + LastModifierId = Guid.NewGuid(), + IsDeleted = true, + DeletionTime = DateTime.Now, + DeleterId = Guid.NewGuid() + }); + await scope.SaveAsync(); + } + +#pragma warning disable 4014 + _auditingStore.Received().SaveAsync(Arg.Is(x => x.EntityChanges.Count == 1 + && x.EntityChanges[0].PropertyChanges + .Where(y => y.PropertyName != nameof(AppEntityWithAuditedAndHasCustomAuditingProperties + .ExtraProperties)) + .All(y => GetBaseAuditPropertyNames().Contains(y.PropertyName)))); +#pragma warning restore 4014 + } } } diff --git a/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNamingNormalizerProvider_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNamingNormalizerProvider_Tests.cs new file mode 100644 index 0000000000..23df740630 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNamingNormalizerProvider_Tests.cs @@ -0,0 +1,57 @@ +using Shouldly; +using Xunit; + +namespace Volo.Abp.BlobStoring.Azure +{ + public class DefaultAzureBlobNamingNormalizerProvider_Tests : AbpBlobStoringAzureTestCommonBase + { + private readonly IBlobNamingNormalizer _blobNamingNormalizer; + + public DefaultAzureBlobNamingNormalizerProvider_Tests() + { + _blobNamingNormalizer = GetRequiredService(); + } + + [Fact] + public void NormalizeContainerName_Lowercase() + { + var filename = "ThisIsMyContainerName"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("thisismycontainername"); + } + + [Fact] + public void NormalizeContainerName_Only_Letters_Numbers_Dash() + { + var filename = ",./this-i,./s-my-c,./ont,./ai+*/.=!@#$n^&*er-name.+/"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("this-is-my-container-name"); + } + + [Fact] + public void NormalizeContainerName_Dash() + { + var filename = "-this--is----my-container----name-"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("this-is-my-container-name"); + } + + + [Fact] + public void NormalizeContainerName_Min_Length() + { + var filename = "a"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.Length.ShouldBeGreaterThanOrEqualTo(3); + } + + + [Fact] + public void NormalizeContainerName_Max_Length() + { + var filename = "abpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabp"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.Length.ShouldBeLessThanOrEqualTo(63); + } + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/DefaultFileSystemBlobNamingNormalizerProvider_Tests.cs b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/DefaultFileSystemBlobNamingNormalizerProvider_Tests.cs new file mode 100644 index 0000000000..b3f9fcf5d1 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/DefaultFileSystemBlobNamingNormalizerProvider_Tests.cs @@ -0,0 +1,62 @@ +using System.Runtime; +using System.Runtime.InteropServices; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; +using Shouldly; +using Xunit; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + public class DefaultFileSystemBlobNamingNormalizerProvider_Tests : AbpBlobStoringFileSystemTestBase + { + private readonly IBlobNamingNormalizer _blobNamingNormalizer; + + public DefaultFileSystemBlobNamingNormalizerProvider_Tests() + { + _blobNamingNormalizer = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + var _iosPlatformProvider = Substitute.For(); + _iosPlatformProvider.GetCurrentOSPlatform().Returns(OSPlatform.Windows); + services.AddSingleton(_iosPlatformProvider); + } + + [Fact] + public void NormalizeContainerName() + { + var filename = "thisismy:*?\"<>|foldername"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("thisismyfoldername"); + } + + [Fact] + public void NormalizeBlobName() + { + var filename = "thisismy:*?\"<>|filename"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("thisismyfilename"); + } + + [Theory] + [InlineData("/")] + [InlineData("\\")] + public void NormalizeContainerName_With_Directory(string delimiter) + { + var filename = $"thisis{delimiter}my:*?\"<>|{delimiter}foldername"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe($"thisis{delimiter}my{delimiter}foldername"); + } + + [Theory] + [InlineData("/")] + [InlineData("\\")] + public void NormalizeBlobName_With_Directory(string delimiter) + { + var filename = $"thisis{delimiter}my:*?\"<>|{delimiter}filename"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe($"thisis{delimiter}my{delimiter}filename"); + } + } +} diff --git a/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo.Abp.Caching.StackExchangeRedis.Tests.csproj b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo.Abp.Caching.StackExchangeRedis.Tests.csproj new file mode 100644 index 0000000000..15f4bc8b9d --- /dev/null +++ b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo.Abp.Caching.StackExchangeRedis.Tests.csproj @@ -0,0 +1,17 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestBase.cs b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestBase.cs new file mode 100644 index 0000000000..354ce3cc2b --- /dev/null +++ b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestBase.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + public abstract class AbpCachingStackExchangeRedisTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestModule.cs b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestModule.cs new file mode 100644 index 0000000000..5d67ae743a --- /dev/null +++ b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestModule.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + [DependsOn( + typeof(AbpCachingStackExchangeRedisModule), + typeof(AbpTestBaseModule), + typeof(AbpAutofacModule) + )] + public class AbpCachingStackExchangeRedisTestModule : AbpModule + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache_Tests.cs b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache_Tests.cs new file mode 100644 index 0000000000..660c8dbdb7 --- /dev/null +++ b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache_Tests.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Caching.Distributed; +using Shouldly; +using Xunit; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + public class AbpRedisCache_Tests : AbpCachingStackExchangeRedisTestBase + { + private readonly IDistributedCache _distributedCache; + + public AbpRedisCache_Tests() + { + _distributedCache = GetRequiredService(); + } + + [Fact] + public void Should_Replace_RedisCache() + { + (_distributedCache is AbpRedisCache).ShouldBeTrue(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs index 14677a73bd..533362c851 100644 --- a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs +++ b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Testing; @@ -80,7 +81,6 @@ namespace Volo.Abp.Caching var personCache = GetRequiredService>(); var otherPersonCache = GetRequiredService>(); - var cacheKey = Guid.NewGuid().ToString(); const string personName = "john nash"; @@ -310,5 +310,34 @@ namespace Volo.Abp.Caching cacheItem2.ShouldBeNull(); } + [Fact] + public async Task Should_Set_And_Get_Multiple_Items_Async() + { + var personCache = GetRequiredService>(); + + await personCache.SetManyAsync(new[] + { + new KeyValuePair("john", new PersonCacheItem("John Nash")), + new KeyValuePair("thomas", new PersonCacheItem("Thomas Moore")) + }); + + var cacheItems = await personCache.GetManyAsync(new[] + { + "john", + "thomas", + "baris" //doesn't exist + }); + + cacheItems.Length.ShouldBe(3); + cacheItems[0].Key.ShouldBe("john"); + cacheItems[0].Value.Name.ShouldBe("John Nash"); + cacheItems[1].Key.ShouldBe("thomas"); + cacheItems[1].Value.Name.ShouldBe("Thomas Moore"); + cacheItems[2].Key.ShouldBe("baris"); + cacheItems[2].Value.ShouldBeNull(); + + (await personCache.GetAsync("john")).Name.ShouldBe("John Nash"); + (await personCache.GetAsync("baris")).ShouldBeNull(); + } } } \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json index 7cf2cf7be6..f0dbdaadb6 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json @@ -40,6 +40,9 @@ "DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Is self-registration enabled", "Description:Abp.Account.IsSelfRegistrationEnabled": "Whether a user can register the account by him or herself.", "DisplayName:Abp.Account.EnableLocalLogin": "Authenticate with a local account", - "Description:Abp.Account.EnableLocalLogin": "Indicates if the server will allow users to authenticate with a local account." + "Description:Abp.Account.EnableLocalLogin": "Indicates if the server will allow users to authenticate with a local account.", + "LoggedOutTitle": "Signed Out", + "LoggedOutText": "You have been signed out and you will be redirected soon.", + "ReturnToText": "Click here to redirect to {0}" } } diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json index 60d5f40634..3938a34984 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json @@ -40,6 +40,9 @@ "DisplayName:Abp.Account.IsSelfRegistrationEnabled": "self-registration etkin mi ?", "Description:Abp.Account.IsSelfRegistrationEnabled": "Bir kullanıcının hesabı kendisi tarafından kaydedip kaydedememesidir.", "DisplayName:Abp.Account.EnableLocalLogin": "Yerel bir hesapla kimlik doğrulaması", - "Description:Abp.Account.EnableLocalLogin": "Sunucunun, kullanıcıların yerel bir hesapla kimlik doğrulamasına izin verip vermeyeceğini belirtir." + "Description:Abp.Account.EnableLocalLogin": "Sunucunun, kullanıcıların yerel bir hesapla kimlik doğrulamasına izin verip vermeyeceğini belirtir.", + "LoggedOutTitle": "Çıkış Yaptınız", + "LoggedOutText": "Çıkış yaptınız ve birazdan yönlendirileceksiniz.", + "ReturnToText": "{0} uygulamasına dönmek için tıklayın." } -} \ No newline at end of file +} diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/RegisterDto.cs b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/RegisterDto.cs index 9a9f3caa4e..b69383d26f 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/RegisterDto.cs +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/RegisterDto.cs @@ -1,22 +1,23 @@ using System.ComponentModel.DataAnnotations; using Volo.Abp.Auditing; using Volo.Abp.Identity; +using Volo.Abp.Validation; namespace Volo.Abp.Account { public class RegisterDto { [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } [Required] [EmailAddress] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string EmailAddress { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [DataType(DataType.Password)] [DisableAuditing] public string Password { get; set; } diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs index ab403ea5a1..43857cc111 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs @@ -1,6 +1,8 @@ -using IdentityServer4.Services; +using System.Security.Claims; +using IdentityServer4.Services; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Volo.Abp.DependencyInjection; namespace Volo.Abp.Account.Web.Pages.Account @@ -24,13 +26,20 @@ namespace Volo.Abp.Account.Web.Pages.Account if (!string.IsNullOrEmpty(logoutId)) { var logoutContext = await Interaction.GetLogoutContextAsync(logoutId); + await SignInManager.SignOutAsync(); - var postLogoutUri = logoutContext.PostLogoutRedirectUri; + HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); - if (!string.IsNullOrEmpty(postLogoutUri)) + LoggedOutModel vm = new LoggedOutModel() { - return Redirect(postLogoutUri); - } + PostLogoutRedirectUri = logoutContext?.PostLogoutRedirectUri, + ClientName = logoutContext?.ClientName, + SignOutIframeUrl = logoutContext?.SignOutIFrameUrl + }; + + Logger.LogInformation($"Redirecting to LoggedOut Page..."); + + return RedirectToPage("./LoggedOut", vm); } if (ReturnUrl != null) @@ -38,6 +47,8 @@ namespace Volo.Abp.Account.Web.Pages.Account return LocalRedirect(ReturnUrl); } + Logger.LogInformation( + $"IdentityServerSupportedLogoutModel couldn't find postLogoutUri... Redirecting to:/Account/Login.."); return RedirectToPage("/Account/Login"); } } diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Properties/launchSettings.json b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Properties/launchSettings.json deleted file mode 100644 index aca3a5b165..0000000000 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:49583/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Account.Web.IdentityServer": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:49584/" - } - } -} \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj index 17a164e612..ca27f60695 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj @@ -21,8 +21,6 @@ - - diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml new file mode 100644 index 0000000000..0e7b14236a --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml @@ -0,0 +1,31 @@ +@page "/Account/LoggedOut" +@model Volo.Abp.Account.Web.Pages.Account.LoggedOutModel +@using Volo.Abp.Account.Localization +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.Account.Web.Pages.Account +@inject IHtmlLocalizer L + +@section scripts { + + + +} + +@section styles { + +} + + + + @L["LoggedOutTitle"] + @L["LoggedOutText"] + @if (Model.PostLogoutRedirectUri != null) + { + @L["ReturnToText", Model.ClientName] + } + @if (Model.SignOutIframeUrl != null) + { + + } + + diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml.cs new file mode 100644 index 0000000000..6023732975 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Volo.Abp.Account.Web.Pages.Account +{ + public class LoggedOutModel : AccountPageModel + { + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ClientName { get; set; } + + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string SignOutIframeUrl { get; set; } + + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string PostLogoutRedirectUri { get; set; } + + public virtual Task OnGetAsync() + { + return Task.FromResult(Page()); + } + + public virtual Task OnPostAsync() + { + return Task.FromResult(Page()); + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.css b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.css new file mode 100644 index 0000000000..75dcda385e --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.css @@ -0,0 +1,5 @@ +.logoutiframe { + display: none; + width:0; + height: 0; +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.js b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.js new file mode 100644 index 0000000000..950bf65719 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.js @@ -0,0 +1,5 @@ +document.addEventListener("DOMContentLoaded", function (event) { + setTimeout(function () { + window.location = document.getElementById("redirectButton").getAttribute("href"); + }, 3000) +}); diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs index 34a084bd3d..5251cb12f1 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs @@ -254,11 +254,11 @@ namespace Volo.Abp.Account.Web.Pages.Account public class LoginInputModel { [Required] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string UserNameOrEmailAddress { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [DataType(DataType.Password)] [DisableAuditing] public string Password { get; set; } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs index deed56b736..3c779dc870 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Volo.Abp.Identity; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Validation; namespace Volo.Abp.Account.Web.Pages.Account { @@ -36,44 +37,45 @@ namespace Volo.Abp.Account.Web.Pages.Account public class ChangePasswordInfoModel { [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [Display(Name = "DisplayName:CurrentPassword")] [DataType(DataType.Password)] public string CurrentPassword { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [Display(Name = "DisplayName:NewPassword")] [DataType(DataType.Password)] public string NewPassword { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [Display(Name = "DisplayName:NewPasswordConfirm")] [DataType(DataType.Password)] public string NewPasswordConfirm { get; set; } } + public class PersonalSettingsInfoModel { [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] [Display(Name = "DisplayName:UserName")] public string UserName { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] [Display(Name = "DisplayName:Email")] public string Email { get; set; } - [StringLength(IdentityUserConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxNameLength))] [Display(Name = "DisplayName:Name")] public string Name { get; set; } - [StringLength(IdentityUserConsts.MaxSurnameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxSurnameLength))] [Display(Name = "DisplayName:Surname")] public string Surname { get; set; } - [StringLength(IdentityUserConsts.MaxPhoneNumberLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] [Display(Name = "DisplayName:PhoneNumber")] public string PhoneNumber { get; set; } } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs index 87c7131305..f6d626dd63 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs @@ -9,6 +9,7 @@ using Volo.Abp.Application.Dtos; using Volo.Abp.Identity; using Volo.Abp.Settings; using Volo.Abp.Uow; +using Volo.Abp.Validation; using IdentityUser = Volo.Abp.Identity.IdentityUser; namespace Volo.Abp.Account.Web.Pages.Account @@ -74,16 +75,16 @@ namespace Volo.Abp.Account.Web.Pages.Account public class PostInput { [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } [Required] [EmailAddress] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string EmailAddress { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [DataType(DataType.Password)] [DisableAuditing] public string Password { get; set; } diff --git a/modules/account/src/Volo.Abp.Account.Web/Properties/launchSettings.json b/modules/account/src/Volo.Abp.Account.Web/Properties/launchSettings.json deleted file mode 100644 index 8dab775bff..0000000000 --- a/modules/account/src/Volo.Abp.Account.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:56009/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Account.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:56013/" - } - } -} \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj index a77cbad333..42918e34d4 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj +++ b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj @@ -25,8 +25,6 @@ - - diff --git a/modules/blogging/src/Volo.Blogging.Admin.Web/Properties/launchSettings.json b/modules/blogging/src/Volo.Blogging.Admin.Web/Properties/launchSettings.json deleted file mode 100644 index 64a5fd912c..0000000000 --- a/modules/blogging/src/Volo.Blogging.Admin.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:50000/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Blogging.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:50014/" - } - } -} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj b/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj index 9037420f3f..fdbe0dd150 100644 --- a/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj +++ b/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj @@ -28,7 +28,6 @@ - diff --git a/modules/blogging/src/Volo.Blogging.Web/Properties/launchSettings.json b/modules/blogging/src/Volo.Blogging.Web/Properties/launchSettings.json deleted file mode 100644 index 64a5fd912c..0000000000 --- a/modules/blogging/src/Volo.Blogging.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:50000/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Blogging.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:50014/" - } - } -} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj b/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj index e26b58d550..203c142de9 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj +++ b/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj @@ -28,9 +28,7 @@ - - diff --git a/modules/client-simulation/src/Volo.ClientSimulation.Web/Properties/launchSettings.json b/modules/client-simulation/src/Volo.ClientSimulation.Web/Properties/launchSettings.json deleted file mode 100644 index 2f043da627..0000000000 --- a/modules/client-simulation/src/Volo.ClientSimulation.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:51023/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.ClientSimulation.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:51024/" - } - } -} \ No newline at end of file diff --git a/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj b/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj index e8358805b7..aa3ecf2dac 100644 --- a/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj +++ b/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj @@ -27,9 +27,7 @@ - - diff --git a/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo.Docs.Admin.HttpApi.Client.csproj b/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo.Docs.Admin.HttpApi.Client.csproj index 076bc1d37a..00b0e26d75 100644 --- a/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo.Docs.Admin.HttpApi.Client.csproj +++ b/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo.Docs.Admin.HttpApi.Client.csproj @@ -4,7 +4,7 @@ - netcoreapp3.1 + netstandard2.0 Volo.Docs.Admin.HttpApi.Client Volo.Docs.Admin.HttpApi.Client diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Properties/launchSettings.json b/modules/docs/src/Volo.Docs.Admin.Web/Properties/launchSettings.json deleted file mode 100644 index b0ed7371d0..0000000000 --- a/modules/docs/src/Volo.Docs.Admin.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:51397", - "sslPort": 44346 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Docs.Admin.Web": { - "commandName": "Project", - "launchBrowser": true, - "applicationUrl": "https://localhost:5001;http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj b/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj index 164c28cdb2..5b75a24f41 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj +++ b/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj @@ -30,11 +30,9 @@ - - diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs index ef6f6e1ebb..1da1ab1f9d 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs @@ -38,7 +38,7 @@ namespace Volo.Docs.GitHub.Documents var rawDocumentUrl = rawRootUrl + documentName; var isNavigationDocument = documentName == project.NavigationDocumentName; var isParameterDocument = documentName == project.ParametersDocumentName; - var editLink = rootUrl.ReplaceFirst("/tree/", "/blob/") + languageCode + "/" + documentName; + var editLink = rootUrl.ReplaceFirst("/tree/", "/blob/").EnsureEndsWith('/') + languageCode + "/" + documentName; var localDirectory = ""; var fileName = documentName; diff --git a/modules/docs/src/Volo.Docs.Web/Properties/launchSettings.json b/modules/docs/src/Volo.Docs.Web/Properties/launchSettings.json deleted file mode 100644 index e32f163034..0000000000 --- a/modules/docs/src/Volo.Docs.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55007/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Docs.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:55020/" - } - } -} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj b/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj index 9afc79d796..4479ec40af 100644 --- a/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj +++ b/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj @@ -33,11 +33,9 @@ - - diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementRemoteServiceConsts.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementRemoteServiceConsts.cs index 749805da05..c941a1b7eb 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementRemoteServiceConsts.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementRemoteServiceConsts.cs @@ -2,6 +2,6 @@ { public class FeatureManagementRemoteServiceConsts { - public const string RemoteServiceName = "FeatureManagement"; + public const string RemoteServiceName = "AbpFeatureManagement"; } } \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs index 24b04b7013..6c9db9bade 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs @@ -16,7 +16,6 @@ namespace Volo.Abp.FeatureManagement public class FeatureAppService : FeatureManagementAppServiceBase, IFeatureAppService { protected FeatureManagementOptions Options { get; } - protected IFeatureManager FeatureManager { get; } protected IFeatureDefinitionManager FeatureDefinitionManager { get; } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs index b35225edb2..26388fdaf9 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs @@ -1,6 +1,9 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; using Volo.Abp.Guids; namespace Volo.Abp.FeatureManagement @@ -8,17 +11,20 @@ namespace Volo.Abp.FeatureManagement public class FeatureManagementStore : IFeatureManagementStore, ITransientDependency { protected IDistributedCache Cache { get; } + protected IFeatureDefinitionManager FeatureDefinitionManager { get; } protected IFeatureValueRepository FeatureValueRepository { get; } protected IGuidGenerator GuidGenerator { get; } public FeatureManagementStore( IFeatureValueRepository featureValueRepository, IGuidGenerator guidGenerator, - IDistributedCache cache) + IDistributedCache cache, + IFeatureDefinitionManager featureDefinitionManager) { FeatureValueRepository = featureValueRepository; GuidGenerator = guidGenerator; Cache = cache; + FeatureDefinitionManager = featureDefinitionManager; } public virtual async Task GetOrNullAsync(string name, string providerName, string providerKey) @@ -61,16 +67,43 @@ namespace Volo.Abp.FeatureManagement return cacheItem; } - var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey); + cacheItem = new FeatureValueCacheItem(null); + + await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); - cacheItem = new FeatureValueCacheItem(featureValue?.Value); + return cacheItem; + } + + private async Task SetCacheItemsAsync( + string providerName, + string providerKey, + string currentName, + FeatureValueCacheItem currentCacheItem) + { + var featureDefinitions = FeatureDefinitionManager.GetAll(); + var featuresDictionary = (await FeatureValueRepository.GetListAsync(providerName, providerKey)) + .ToDictionary(s => s.Name, s => s.Value); + + var cacheItems = new List>(); + + foreach (var featureDefinition in featureDefinitions) + { + var featureValue = featuresDictionary.GetOrDefault(featureDefinition.Name); + + cacheItems.Add( + new KeyValuePair( + CalculateCacheKey(featureDefinition.Name, providerName, providerKey), + new FeatureValueCacheItem(featureValue) + ) + ); - await Cache.SetAsync( - cacheKey, - cacheItem - ); + if (featureDefinition.Name == currentName) + { + currentCacheItem.Value = featureValue; + } + } - return cacheItem; + await Cache.SetManyAsync(cacheItems); } protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs index e3eda46033..0ae78ed03f 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs @@ -5,7 +5,8 @@ using Volo.Abp.AspNetCore.Mvc; namespace Volo.Abp.FeatureManagement { [RemoteService(Name = FeatureManagementRemoteServiceConsts.RemoteServiceName)] - [Area("abp")] + [Area("featureManagement")] + [Route("api/feature-management/features")] public class FeaturesController : AbpController, IFeatureAppService { protected IFeatureAppService FeatureAppService { get; } @@ -15,11 +16,13 @@ namespace Volo.Abp.FeatureManagement FeatureAppService = featureAppService; } + [HttpGet] public virtual Task GetAsync(string providerName, string providerKey) { return FeatureAppService.GetAsync(providerName, providerKey); } + [HttpPut] public virtual Task UpdateAsync(string providerName, string providerKey, UpdateFeaturesDto input) { return FeatureAppService.UpdateAsync(providerName, providerKey, input); diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Properties/launchSettings.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Properties/launchSettings.json deleted file mode 100644 index 23a4efbb61..0000000000 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:56993/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Abp.FeatureManagement.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:56994/" - } - } -} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs index 8e11d89b30..491e82d766 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs @@ -1,12 +1,13 @@ using System.ComponentModel.DataAnnotations; using Volo.Abp.ObjectExtending; +using Volo.Abp.Validation; namespace Volo.Abp.Identity { public class IdentityRoleCreateOrUpdateDtoBase : ExtensibleObject { [Required] - [StringLength(IdentityRoleConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityRoleConsts), nameof(IdentityRoleConsts.MaxNameLength))] public string Name { get; set; } public bool IsDefault { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateDto.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateDto.cs index a572c7db2c..a080a7e33e 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateDto.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateDto.cs @@ -1,13 +1,14 @@ using System.ComponentModel.DataAnnotations; using Volo.Abp.Auditing; +using Volo.Abp.Validation; namespace Volo.Abp.Identity { public class IdentityUserCreateDto : IdentityUserCreateOrUpdateDtoBase { - [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] [DisableAuditing] + [Required] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] public string Password { get; set; } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs index c18f2c16b5..e06ea8b987 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs @@ -2,27 +2,28 @@ using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; using Volo.Abp.ObjectExtending; +using Volo.Abp.Validation; namespace Volo.Abp.Identity { public abstract class IdentityUserCreateOrUpdateDtoBase : ExtensibleObject { [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } - [StringLength(IdentityUserConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(IdentityUserConsts.MaxSurnameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxSurnameLength))] public string Surname { get; set; } [Required] [EmailAddress] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string Email { get; set; } - [StringLength(IdentityUserConsts.MaxPhoneNumberLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] public string PhoneNumber { get; set; } public bool TwoFactorEnabled { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserUpdateDto.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserUpdateDto.cs index 216a55df9e..aa499bf2cd 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserUpdateDto.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserUpdateDto.cs @@ -1,13 +1,14 @@ using System.ComponentModel.DataAnnotations; using Volo.Abp.Auditing; using Volo.Abp.Domain.Entities; +using Volo.Abp.Validation; namespace Volo.Abp.Identity { public class IdentityUserUpdateDto : IdentityUserCreateOrUpdateDtoBase, IHasConcurrencyStamp { - [StringLength(IdentityUserConsts.MaxPasswordLength)] [DisableAuditing] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] public string Password { get; set; } public string ConcurrencyStamp { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UpdateProfileDto.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UpdateProfileDto.cs index 52919c7744..5e0397bdfa 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UpdateProfileDto.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UpdateProfileDto.cs @@ -1,23 +1,24 @@ using System.ComponentModel.DataAnnotations; using Volo.Abp.ObjectExtending; +using Volo.Abp.Validation; namespace Volo.Abp.Identity { public class UpdateProfileDto : ExtensibleObject { - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string Email { get; set; } - [StringLength(IdentityUserConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(IdentityUserConsts.MaxSurnameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxSurnameLength))] public string Surname { get; set; } - [StringLength(IdentityUserConsts.MaxPhoneNumberLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] public string PhoneNumber { get; set; } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Properties/launchSettings.json b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Properties/launchSettings.json deleted file mode 100644 index fc5b1a72f7..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53901/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Identity.AspNetCore": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53902/" - } - } -} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityClaimTypeConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityClaimTypeConsts.cs index 32e3197e12..6e15cb0cfc 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityClaimTypeConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityClaimTypeConsts.cs @@ -2,10 +2,10 @@ { public class IdentityClaimTypeConsts { - public const int MaxNameLength = 256; - public const int MaxRegexLength = 512; - public const int MaxRegexDescriptionLength = 128; - public const int MaxDescriptionLength = 256; - public const int MaxConcurrencyStampLength = 256; + public static int MaxNameLength { get; set; } = 256; + public static int MaxRegexLength { get; set; } = 512; + public static int MaxRegexDescriptionLength { get; set; } = 128; + public static int MaxDescriptionLength { get; set; } = 256; + public static int MaxConcurrencyStampLength { get; set; } = 256; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleClaimConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleClaimConsts.cs index f53717486f..0f20ac40ef 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleClaimConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleClaimConsts.cs @@ -2,8 +2,8 @@ { public static class IdentityRoleClaimConsts { - public const int MaxClaimTypeLength = IdentityUserClaimConsts.MaxClaimTypeLength; + public static int MaxClaimTypeLength { get; set; } = IdentityUserClaimConsts.MaxClaimTypeLength; - public const int MaxClaimValueLength = IdentityUserClaimConsts.MaxClaimValueLength; + public static int MaxClaimValueLength { get; set; } = IdentityUserClaimConsts.MaxClaimValueLength; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleConsts.cs index cac33aa53d..33e8e74977 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleConsts.cs @@ -2,8 +2,8 @@ { public static class IdentityRoleConsts { - public const int MaxNameLength = 256; - public const int MaxNormalizedNameLength = MaxNameLength; - public const int MaxConcurrencyStampLength = 256; + public static int MaxNameLength { get; set; } = 256; + public static int MaxNormalizedNameLength { get; set; } = 256; + public static int MaxConcurrencyStampLength { get; set; } = 256; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserClaimConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserClaimConsts.cs index 025b255f11..a850d6cff2 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserClaimConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserClaimConsts.cs @@ -2,8 +2,8 @@ { public static class IdentityUserClaimConsts { - public const int MaxClaimTypeLength = 256; + public static int MaxClaimTypeLength { get; set; } = 256; - public const int MaxClaimValueLength = 1024; + public static int MaxClaimValueLength { get; set; } = 1024; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs index ef2cc57ef4..7b1a90dacc 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs @@ -4,26 +4,26 @@ namespace Volo.Abp.Identity { public static class IdentityUserConsts { - public const int MaxUserNameLength = AbpUserConsts.MaxUserNameLength; + public static int MaxUserNameLength { get; set; } = AbpUserConsts.MaxUserNameLength; - public const int MaxNameLength = AbpUserConsts.MaxNameLength; + public static int MaxNameLength { get; set; } = AbpUserConsts.MaxNameLength; - public const int MaxSurnameLength = AbpUserConsts.MaxSurnameLength; + public static int MaxSurnameLength { get; set; } = AbpUserConsts.MaxSurnameLength; - public const int MaxNormalizedUserNameLength = MaxUserNameLength; + public static int MaxNormalizedUserNameLength { get; set; } = MaxUserNameLength; - public const int MaxEmailLength = AbpUserConsts.MaxEmailLength; + public static int MaxEmailLength { get; set; } = AbpUserConsts.MaxEmailLength; - public const int MaxNormalizedEmailLength = MaxEmailLength; + public static int MaxNormalizedEmailLength { get; set; } = MaxEmailLength; - public const int MaxPhoneNumberLength = AbpUserConsts.MaxPhoneNumberLength; + public static int MaxPhoneNumberLength { get; set; } = AbpUserConsts.MaxPhoneNumberLength; - public const int MaxPasswordLength = 128; + public static int MaxPasswordLength { get; set; } = 128; - public const int MaxPasswordHashLength = 256; + public static int MaxPasswordHashLength { get; set; } = 256; - public const int MaxSecurityStampLength = 256; + public static int MaxSecurityStampLength { get; set; } = 256; - public const int MaxConcurrencyStampLength = 256; + public static int MaxConcurrencyStampLength { get; set; } = 256; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserLoginConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserLoginConsts.cs index 0ef43bb1b1..3f95dfd634 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserLoginConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserLoginConsts.cs @@ -2,8 +2,8 @@ { public static class IdentityUserLoginConsts { - public const int MaxLoginProviderLength = 64; - public const int MaxProviderKeyLength = 196; - public const int MaxProviderDisplayNameLength = 128; + public static int MaxLoginProviderLength { get; set; } = 64; + public static int MaxProviderKeyLength { get; set; } = 196; + public static int MaxProviderDisplayNameLength { get; set; } = 128; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserTokenConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserTokenConsts.cs index 92305851fb..1e4eb4794b 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserTokenConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserTokenConsts.cs @@ -2,8 +2,8 @@ { public static class IdentityUserTokenConsts { - public const int MaxLoginProviderLength = 64; + public static int MaxLoginProviderLength { get; set; } = 64; - public const int MaxNameLength = 128; + public static int MaxNameLength { get; set; } = 128; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitConsts.cs index ad6091a44e..7338fc8a28 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitConsts.cs @@ -5,7 +5,7 @@ /// /// Maximum length of the property. /// - public const int MaxDisplayNameLength = 128; + public static int MaxDisplayNameLength { get; set; } = 128; /// /// Maximum depth of an OU hierarchy. diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitEto.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitEto.cs new file mode 100644 index 0000000000..09013aa1ce --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitEto.cs @@ -0,0 +1,16 @@ +using System; + +namespace Volo.Abp.Identity +{ + [Serializable] + public class OrganizationUnitEto + { + public Guid Id { get; set; } + + public Guid? TenantId { get; set; } + + public string Code { get; set; } + + public string DisplayName { get; set; } + } +} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConfiguration.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConfiguration.cs index 3892fb00f6..a8e488871a 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConfiguration.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConfiguration.cs @@ -31,5 +31,14 @@ namespace Volo.Abp.ObjectExtending configureAction ); } + + public IdentityModuleExtensionConfiguration ConfigureOrganizationUnit( + Action configureAction) + { + return this.ConfigureEntity( + IdentityModuleExtensionConsts.EntityNames.OrganizationUnit, + configureAction + ); + } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConsts.cs index 6d2b4c0653..45c9db6462 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConsts.cs @@ -11,6 +11,8 @@ public const string Role = "Role"; public const string ClaimType = "ClaimType"; + + public const string OrganizationUnit = "OrganizationUnit"; } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs index 07c4d2e971..d21e945f60 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs @@ -34,6 +34,7 @@ namespace Volo.Abp.Identity options.EtoMappings.Add(typeof(AbpIdentityDomainModule)); options.EtoMappings.Add(typeof(AbpIdentityDomainModule)); options.EtoMappings.Add(typeof(AbpIdentityDomainModule)); + options.EtoMappings.Add(typeof(AbpIdentityDomainModule)); }); var identityBuilder = context.Services.AddAbpIdentity(options => @@ -66,6 +67,12 @@ namespace Volo.Abp.Identity IdentityModuleExtensionConsts.EntityNames.ClaimType, typeof(IdentityClaimType) ); + + ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToEntity( + IdentityModuleExtensionConsts.ModuleName, + IdentityModuleExtensionConsts.EntityNames.OrganizationUnit, + typeof(OrganizationUnit) + ); } private static void AddAbpIdentityOptionsFactory(IServiceCollection services) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappingProfile.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappingProfile.cs index 547525b46e..9a5557401d 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappingProfile.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappingProfile.cs @@ -10,6 +10,7 @@ namespace Volo.Abp.Identity CreateMap(); CreateMap(); CreateMap(); + CreateMap(); } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Properties/launchSettings.json b/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Properties/launchSettings.json deleted file mode 100644 index 6ba85345ab..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53978/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Identity.HttpApi.Client": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53980/" - } - } -} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi/Properties/launchSettings.json b/modules/identity/src/Volo.Abp.Identity.HttpApi/Properties/launchSettings.json deleted file mode 100644 index 2323768a18..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53977/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Identity.HttpApi": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53979/" - } - } -} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs index e7244c80f1..df86acd94d 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Roles { @@ -34,7 +35,7 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Roles public class RoleInfoModel { [Required] - [StringLength(IdentityRoleConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityRoleConsts), nameof(IdentityRoleConsts.MaxNameLength))] [Display(Name = "DisplayName:RoleName")] public string Name { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs index 69305d243c..8a004e95a5 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Domain.Entities; +using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Roles { @@ -44,7 +45,7 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Roles public string ConcurrencyStamp { get; set; } [Required] - [StringLength(IdentityRoleConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityRoleConsts), nameof(IdentityRoleConsts.MaxNameLength))] [Display(Name = "DisplayName:RoleName")] public string Name { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs index e7a1f4a26d..6149c676b8 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Auditing; using Volo.Abp.Application.Dtos; +using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Users { @@ -54,27 +55,27 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users public class UserInfoViewModel { [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } - [StringLength(IdentityUserConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(IdentityUserConsts.MaxSurnameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxSurnameLength))] public string Surname { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] [DataType(DataType.Password)] [DisableAuditing] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] public string Password { get; set; } [Required] [EmailAddress] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string Email { get; set; } - [StringLength(IdentityUserConsts.MaxPhoneNumberLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] public string PhoneNumber { get; set; } public bool TwoFactorEnabled { get; set; } = true; diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs index f14ec21fe6..08c5ae0dd9 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc; using Volo.Abp.Auditing; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Entities; +using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Users { @@ -63,26 +64,26 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users public string ConcurrencyStamp { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } - [StringLength(IdentityUserConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(IdentityUserConsts.MaxSurnameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxSurnameLength))] public string Surname { get; set; } - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [DataType(DataType.Password)] [DisableAuditing] public string Password { get; set; } [Required] [EmailAddress] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string Email { get; set; } - [StringLength(IdentityUserConsts.MaxPhoneNumberLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] public string PhoneNumber { get; set; } public bool TwoFactorEnabled { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Properties/launchSettings.json b/modules/identity/src/Volo.Abp.Identity.Web/Properties/launchSettings.json deleted file mode 100644 index 9a8b023b55..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55588/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Identity.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:55590/" - } - } -} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj index f5c9ffc3d4..5a8367dc5f 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj +++ b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj @@ -25,9 +25,7 @@ - - diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrantCacheItem.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrantCacheItem.cs index e4d3f1648e..49013954a2 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrantCacheItem.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrantCacheItem.cs @@ -5,8 +5,6 @@ namespace Volo.Abp.PermissionManagement [Serializable] public class PermissionGrantCacheItem { - public string Name { get; set; } //TODO: Consider to remove this - public bool IsGranted { get; set; } public PermissionGrantCacheItem() @@ -14,9 +12,8 @@ namespace Volo.Abp.PermissionManagement } - public PermissionGrantCacheItem(string name, bool isGranted) + public PermissionGrantCacheItem(bool isGranted) { - Name = name; IsGranted = isGranted; } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs index 2ffd6f80e1..bdfdc38881 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Authorization.Permissions; @@ -10,17 +12,21 @@ namespace Volo.Abp.PermissionManagement public class PermissionStore : IPermissionStore, ITransientDependency { public ILogger Logger { get; set; } - + protected IPermissionGrantRepository PermissionGrantRepository { get; } + protected IPermissionDefinitionManager PermissionDefinitionManager { get; } + protected IDistributedCache Cache { get; } - + public PermissionStore( IPermissionGrantRepository permissionGrantRepository, - IDistributedCache cache) + IDistributedCache cache, + IPermissionDefinitionManager permissionDefinitionManager) { PermissionGrantRepository = permissionGrantRepository; Cache = cache; + PermissionDefinitionManager = permissionDefinitionManager; Logger = NullLogger.Instance; } @@ -29,7 +35,10 @@ namespace Volo.Abp.PermissionManagement return (await GetCacheItemAsync(name, providerName, providerKey)).IsGranted; } - protected virtual async Task GetCacheItemAsync(string name, string providerName, string providerKey) + protected virtual async Task GetCacheItemAsync( + string name, + string providerName, + string providerKey) { var cacheKey = CalculateCacheKey(name, providerName, providerKey); @@ -43,23 +52,51 @@ namespace Volo.Abp.PermissionManagement return cacheItem; } - Logger.LogDebug($"Not found in the cache, getting from the repository: {cacheKey}"); + Logger.LogDebug($"Not found in the cache: {cacheKey}"); + + cacheItem = new PermissionGrantCacheItem(false); + + await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); - cacheItem = new PermissionGrantCacheItem( - name, - (await PermissionGrantRepository.FindAsync(name, providerName, providerKey)) != null - ); + return cacheItem; + } - Logger.LogDebug($"Setting the cache item: {cacheKey}"); + protected virtual async Task SetCacheItemsAsync( + string providerName, + string providerKey, + string currentName, + PermissionGrantCacheItem currentCacheItem) + { + var permissions = PermissionDefinitionManager.GetPermissions(); + + Logger.LogDebug($"Getting all granted permissions from the repository for this provider name,key: {providerName},{providerKey}"); - await Cache.SetAsync( - cacheKey, - cacheItem + var grantedPermissionsHashSet = new HashSet( + (await PermissionGrantRepository.GetListAsync(providerName, providerKey)).Select(p => p.Name) ); - Logger.LogDebug($"Finished setting the cache item: {cacheKey}"); + Logger.LogDebug($"Setting the cache items. Count: {permissions.Count}"); - return cacheItem; + var cacheItems = new List>(); + + foreach (var permission in permissions) + { + var isGranted = grantedPermissionsHashSet.Contains(permission.Name); + + cacheItems.Add(new KeyValuePair( + CalculateCacheKey(permission.Name, providerName, providerKey), + new PermissionGrantCacheItem(isGranted)) + ); + + if (permission.Name == currentName) + { + currentCacheItem.IsGranted = isGranted; + } + } + + await Cache.SetManyAsync(cacheItems); + + Logger.LogDebug($"Finished setting the cache items. Count: {permissions.Count}"); } protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) @@ -67,4 +104,4 @@ namespace Volo.Abp.PermissionManagement return PermissionGrantCacheItem.CalculateCacheKey(name, providerName, providerKey); } } -} +} \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs index 2ed9a4d044..bb9db51f98 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs @@ -5,7 +5,8 @@ using Volo.Abp.AspNetCore.Mvc; namespace Volo.Abp.PermissionManagement { [RemoteService(Name = PermissionManagementRemoteServiceConsts.RemoteServiceName)] - [Area("abp")] + [Area("permissionManagement")] + [Route("api/permission-management/permissions")] public class PermissionsController : AbpController, IPermissionAppService { protected IPermissionAppService PermissionAppService { get; } @@ -15,11 +16,13 @@ namespace Volo.Abp.PermissionManagement PermissionAppService = permissionAppService; } + [HttpGet] public virtual Task GetAsync(string providerName, string providerKey) { return PermissionAppService.GetAsync(providerName, providerKey); } + [HttpPut] public virtual Task UpdateAsync(string providerName, string providerKey, UpdatePermissionsDto input) { return PermissionAppService.UpdateAsync(providerName, providerKey, input); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Properties/launchSettings.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Properties/launchSettings.json deleted file mode 100644 index 6cb3381b6c..0000000000 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55926/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.PermissionManagement.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:55927/" - } - } -} \ No newline at end of file diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs index d7a8b0ccb1..617d360898 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs @@ -11,23 +11,25 @@ namespace Volo.Abp.SettingManagement public class SettingManagementStore : ISettingManagementStore, ITransientDependency { protected IDistributedCache Cache { get; } + protected ISettingDefinitionManager SettingDefinitionManager { get; } protected ISettingRepository SettingRepository { get; } protected IGuidGenerator GuidGenerator { get; } public SettingManagementStore( ISettingRepository settingRepository, IGuidGenerator guidGenerator, - IDistributedCache cache) + IDistributedCache cache, + ISettingDefinitionManager settingDefinitionManager) { SettingRepository = settingRepository; GuidGenerator = guidGenerator; Cache = cache; + SettingDefinitionManager = settingDefinitionManager; } public virtual async Task GetOrNullAsync(string name, string providerName, string providerKey) { - var cacheItem = await GetCacheItemAsync(name, providerName, providerKey); - return cacheItem.Value; + return (await GetCacheItemAsync(name, providerName, providerKey)).Value; } public virtual async Task SetAsync(string name, string value, string providerName, string providerKey) @@ -70,16 +72,43 @@ namespace Volo.Abp.SettingManagement return cacheItem; } - var setting = await SettingRepository.FindAsync(name, providerName, providerKey); + cacheItem = new SettingCacheItem(null); + + await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); + + return cacheItem; + } - cacheItem = new SettingCacheItem(setting?.Value); + private async Task SetCacheItemsAsync( + string providerName, + string providerKey, + string currentName, + SettingCacheItem currentCacheItem) + { + var settingDefinitions = SettingDefinitionManager.GetAll(); + var settingsDictionary = (await SettingRepository.GetListAsync(providerName, providerKey)) + .ToDictionary(s => s.Name, s => s.Value); + + var cacheItems = new List>(); + + foreach (var settingDefinition in settingDefinitions) + { + var settingValue = settingsDictionary.GetOrDefault(settingDefinition.Name); + + cacheItems.Add( + new KeyValuePair( + CalculateCacheKey(settingDefinition.Name, providerName, providerKey), + new SettingCacheItem(settingValue) + ) + ); - await Cache.SetAsync( - cacheKey, - cacheItem - ); + if (settingDefinition.Name == currentName) + { + currentCacheItem.Value = settingValue; + } + } - return cacheItem; + await Cache.SetManyAsync(cacheItems); } protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Properties/launchSettings.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Properties/launchSettings.json deleted file mode 100644 index 844334b642..0000000000 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:62272/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.SettingManagement.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:62273/" - } - } -} \ No newline at end of file diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj index b37ef515df..a0197c0686 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj @@ -24,8 +24,6 @@ - - diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj index 28541197c4..dc168af444 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj @@ -21,7 +21,6 @@ - diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs index 5bf2aeb0b9..1f512c8807 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs @@ -5,7 +5,6 @@ using Volo.Abp.Domain; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; -using Volo.Abp.UI; namespace Volo.Abp.TenantManagement { @@ -14,7 +13,6 @@ namespace Volo.Abp.TenantManagement [DependsOn(typeof(AbpDataModule))] [DependsOn(typeof(AbpDddDomainModule))] [DependsOn(typeof(AbpAutoMapperModule))] - [DependsOn(typeof(AbpUiModule))] //TODO: It's not good to depend on the UI module. However, UserFriendlyException is inside it! public class AbpTenantManagementDomainModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs index d52734a667..a03c96142f 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs @@ -1,7 +1,6 @@ using System; using System.Threading.Tasks; using Volo.Abp.Domain.Services; -using Volo.Abp.UI; namespace Volo.Abp.TenantManagement { diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Properties/launchSettings.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Properties/launchSettings.json deleted file mode 100644 index dab1d2b972..0000000000 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55673/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.TenantManagement.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:55676/" - } - } -} \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj index c558ea9637..71ec4cc04a 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj @@ -19,9 +19,7 @@ - - diff --git a/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs b/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs index acfce7f37b..ee37c6c9f4 100644 --- a/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs +++ b/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs @@ -2,14 +2,14 @@ namespace Volo.Abp.Users { public class AbpUserConsts { - public const int MaxUserNameLength = 256; + public static int MaxUserNameLength { get; set; } = 256; - public const int MaxNameLength = 64; + public static int MaxNameLength { get; set; } = 64; - public const int MaxSurnameLength = 64; + public static int MaxSurnameLength { get; set; } = 64; - public const int MaxEmailLength = 256; + public static int MaxEmailLength { get; set; } = 256; - public const int MaxPhoneNumberLength = 16; + public static int MaxPhoneNumberLength { get; set; } = 16; } } \ No newline at end of file diff --git a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj index 6cbf49a69c..b2f5d2a4a8 100644 --- a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj +++ b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj @@ -30,9 +30,4 @@ - - - - - diff --git a/npm/ng-packs/apps/dev-app/browserslist b/npm/ng-packs/apps/dev-app/.browserslistrc similarity index 100% rename from npm/ng-packs/apps/dev-app/browserslist rename to npm/ng-packs/apps/dev-app/.browserslistrc diff --git a/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts b/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts index be5089316a..8f6973079d 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts @@ -1,36 +1,37 @@ -import { ABP } from '@abp/ng.core'; +import { ABP, DynamicLayoutComponent } from '@abp/ng.core'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; const routes: Routes = [ { path: '', - loadChildren: () => import('./home/home.module').then(m => m.HomeModule), - data: { - routes: { - name: '::Menu:Home', - order: 1, - } as ABP.Route, - }, - }, - { - path: 'account', - loadChildren: () => - import('@abp/ng.account').then(m => m.AccountModule.forLazy({ redirectUrl: '/' })), - }, - { - path: 'identity', - loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()), - }, - { - path: 'tenant-management', - loadChildren: () => - import('@abp/ng.tenant-management').then(m => m.TenantManagementModule.forLazy()), - }, - { - path: 'setting-management', - loadChildren: () => - import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()), + component: DynamicLayoutComponent, + children: [ + { + path: '', + pathMatch: 'full', + loadChildren: () => import('./home/home.module').then(m => m.HomeModule), + }, + { + path: 'account', + loadChildren: () => + import('@abp/ng.account').then(m => m.AccountModule.forLazy({ redirectUrl: '/' })), + }, + { + path: 'identity', + loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()), + }, + { + path: 'tenant-management', + loadChildren: () => + import('@abp/ng.tenant-management').then(m => m.TenantManagementModule.forLazy()), + }, + { + path: 'setting-management', + loadChildren: () => + import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()), + }, + ], }, ]; diff --git a/npm/ng-packs/apps/dev-app/src/app/app.module.ts b/npm/ng-packs/apps/dev-app/src/app/app.module.ts index 71fedf421b..4e69d73cd0 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app.module.ts @@ -13,6 +13,7 @@ import { NgxsModule } from '@ngxs/store'; import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { APP_ROUTE_PROVIDER } from './route.provider'; const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; @@ -35,6 +36,7 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; ThemeBasicModule.forRoot(), ...(environment.production ? [] : LOGGERS), ], + providers: [APP_ROUTE_PROVIDER], declarations: [AppComponent], bootstrap: [AppComponent], }) diff --git a/npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts b/npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts index 367affb583..634706a36c 100644 --- a/npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts @@ -1,12 +1,10 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { HomeComponent } from './home.component'; -import { ApplicationLayoutComponent } from '@abp/ng.theme.basic'; const routes: Routes = [ { path: '', - component: ApplicationLayoutComponent, children: [{ path: '', component: HomeComponent }], }, ]; diff --git a/npm/ng-packs/apps/dev-app/src/app/route.provider.ts b/npm/ng-packs/apps/dev-app/src/app/route.provider.ts new file mode 100644 index 0000000000..5a14ad7ba0 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/route.provider.ts @@ -0,0 +1,20 @@ +import { RoutesService, eLayoutType } from '@abp/ng.core'; +import { APP_INITIALIZER } from '@angular/core'; + +export const APP_ROUTE_PROVIDER = [ + { provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true }, +]; + +function configureRoutes(routes: RoutesService) { + return () => { + routes.add([ + { + path: '/', + name: '::Menu:Home', + iconClass: 'fas fa-home', + order: 1, + layout: eLayoutType.application, + }, + ]); + }; +} diff --git a/npm/ng-packs/apps/dev-app/tsconfig.dev.json b/npm/ng-packs/apps/dev-app/tsconfig.dev.json index cfbd94952d..ac0a5c3d24 100644 --- a/npm/ng-packs/apps/dev-app/tsconfig.dev.json +++ b/npm/ng-packs/apps/dev-app/tsconfig.dev.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "../../out-tsc/app", "types": [] diff --git a/npm/ng-packs/apps/dev-app/tsconfig.spec.json b/npm/ng-packs/apps/dev-app/tsconfig.spec.json index e487d98612..f21e414d94 100644 --- a/npm/ng-packs/apps/dev-app/tsconfig.spec.json +++ b/npm/ng-packs/apps/dev-app/tsconfig.spec.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "../../out-tsc/spec", "types": ["node", "jest"] diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index 37476c4fd8..6ea5faeeae 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -19,7 +19,7 @@ "ci:test": "ng test --coverage=false", "ci:build": "cd scripts && yarn build:prod", "lerna": "lerna", - "compile:ivy": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points --tsconfig './tsconfig.prod.json'", + "compile:ivy": "yarn ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points --tsconfig './tsconfig.prod.json' --source node_modules --async false", "postinstall": "npm run compile:ivy" }, "devDependencies": { @@ -32,35 +32,35 @@ "@abp/ng.tenant-management": "~2.9.0", "@abp/ng.theme.basic": "~2.9.0", "@abp/ng.theme.shared": "~2.9.0", - "@abp/utils": "^2.9.0", + "@abp/utils": "^3.0.0-alpha.1", "@angular-builders/jest": "^9.0.1", - "@angular-devkit/build-angular": "~0.901.7", - "@angular-devkit/build-ng-packagr": "~0.901.7", - "@angular/animations": "~9.1.9", - "@angular/cli": "~9.1.7", - "@angular/common": "~9.1.9", - "@angular/compiler": "~9.1.9", - "@angular/compiler-cli": "~9.1.9", - "@angular/core": "~9.1.9", - "@angular/forms": "~9.1.9", - "@angular/language-service": "~9.1.9", - "@angular/localize": "~9.1.9", - "@angular/platform-browser": "~9.1.9", - "@angular/platform-browser-dynamic": "~9.1.9", - "@angular/router": "~9.1.9", + "@angular-devkit/build-angular": "~0.1000.0", + "@angular-devkit/build-ng-packagr": "~0.1000.0", + "@angular/animations": "~10.0.0", + "@angular/cli": "~10.0.0", + "@angular/common": "~10.0.0", + "@angular/compiler": "~10.0.0", + "@angular/compiler-cli": "~10.0.0", + "@angular/core": "~10.0.0", + "@angular/forms": "~10.0.0", + "@angular/language-service": "~10.0.0", + "@angular/localize": "~10.0.0", + "@angular/platform-browser": "~10.0.0", + "@angular/platform-browser-dynamic": "~10.0.0", + "@angular/router": "~10.0.0", "@fortawesome/fontawesome-free": "^5.13.0", "@ng-bootstrap/ng-bootstrap": "^6.1.0", - "@ngneat/spectator": "^4.5.0", - "@ngx-validate/core": "^0.0.8", + "@ngneat/spectator": "^5.11.0", + "@ngx-validate/core": "^0.0.9", "@ngxs/devtools-plugin": "^3.6.2", "@ngxs/logger-plugin": "^3.6.2", "@ngxs/router-plugin": "^3.6.2", "@ngxs/storage-plugin": "^3.6.2", "@ngxs/store": "^3.6.2", - "@swimlane/ngx-datatable": "^16.0.0", + "@swimlane/ngx-datatable": "^17.0.0", "@types/jest": "^25.2.3", "@types/node": "^12.11.1", - "angular-oauth2-oidc": "^8.0.4", + "angular-oauth2-oidc": "^9.2.2", "bootstrap": "^4.5.0", "chart.js": "^2.9.3", "codelyzer": "^5.1.2", @@ -73,23 +73,23 @@ "just-clone": "^3.1.0", "just-compare": "^1.3.0", "lerna": "^3.19.0", - "ng-packagr": "^9.1.5", + "ng-packagr": "^10.0.0", "ngxs-reset-plugin": "^1.3.0", "ngxs-schematic": "^1.1.9", "prettier": "^1.18.2", - "protractor": "~5.4.0", + "protractor": "~7.0.0", "rxjs": "~6.5.4", "snq": "^1.0.3", "symlink-manager": "^1.5.0", "ts-node": "~7.0.0", - "ts-toolbelt": "^6.3.6", - "tsickle": "^0.37.0", - "tslint": "~5.20.0", - "typescript": "~3.8.0", + "ts-toolbelt": "^6.9.9", + "tsickle": "^0.38.1", + "tslint": "~6.1.0", + "typescript": "~3.9.5", "zone.js": "~0.10.2" }, "dependencies": { - "tslib": "^1.10.0" + "tslib": "^2.0.0" }, "config": { "commitizen": { @@ -99,4 +99,4 @@ "resolutions": { "@ngx-validate/core": "^0.0.8" } -} +} \ No newline at end of file diff --git a/npm/ng-packs/packages/account/ngcc.config.js b/npm/ng-packs/packages/account/ngcc.config.js new file mode 100644 index 0000000000..93f34d5655 --- /dev/null +++ b/npm/ng-packs/packages/account/ngcc.config.js @@ -0,0 +1,7 @@ +module.exports = { + entryPoints: { + '.': {}, + './config': {}, + './dist': { ignore: true }, + }, +}; diff --git a/npm/ng-packs/packages/account/package.json b/npm/ng-packs/packages/account/package.json index 254b6031a1..9f4c7942d2 100644 --- a/npm/ng-packs/packages/account/package.json +++ b/npm/ng-packs/packages/account/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "@abp/ng.theme.shared": "~2.9.0" - }, + , "tslib": "^2.0.0" +}, "publishConfig": { "access": "public" } diff --git a/npm/ng-packs/packages/core/package.json b/npm/ng-packs/packages/core/package.json index fd326aae58..5566fb9750 100644 --- a/npm/ng-packs/packages/core/package.json +++ b/npm/ng-packs/packages/core/package.json @@ -8,15 +8,16 @@ }, "dependencies": { "@abp/utils": "^2.9.0", - "@angular/localize": "~9.1.9", + "@angular/localize": "~9.1.11", "@ngxs/router-plugin": "^3.6.2", "@ngxs/storage-plugin": "^3.6.2", "@ngxs/store": "^3.6.2", - "angular-oauth2-oidc": "^8.0.4", + "angular-oauth2-oidc": "^9.2.2", "just-clone": "^3.1.0", "just-compare": "^1.3.0", "snq": "^1.0.3", - "ts-toolbelt": "^6.3.6" + "ts-toolbelt": "^6.9.9", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index 25d49fb169..49a0868967 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -1,4 +1,4 @@ -import { Component, Injector, OnDestroy, Type } from '@angular/core'; +import { Component, Injector, OnDestroy, Type, Optional, SkipSelf } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { Store } from '@ngxs/store'; import { eLayoutType } from '../enums/common'; @@ -23,7 +23,12 @@ import { TreeNode } from '../utils/tree-utils'; export class DynamicLayoutComponent implements OnDestroy { layout: Type; - constructor(injector: Injector, private store: Store) { + constructor( + injector: Injector, + private store: Store, + @Optional() @SkipSelf() dynamicLayoutComponent: DynamicLayoutComponent, + ) { + if (dynamicLayoutComponent) return; const route = injector.get(ActivatedRoute); const router = injector.get(Router); const routes = injector.get(RoutesService); diff --git a/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts index 2dffa1532d..f5f4d22f5e 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts @@ -2,6 +2,10 @@ import { Directive, Input, Optional, ElementRef, Renderer2, AfterViewInit } from import { Subject } from 'rxjs'; import snq from 'snq'; +/** + * + * @deprecated To be deleted in v3.3 + */ @Directive({ selector: '[abpVisibility]', }) @@ -40,7 +44,8 @@ export class VisibilityDirective implements AfterViewInit { setTimeout(() => { const htmlNodes = snq( - () => Array.from(this.focusedElement.childNodes).filter(node => node instanceof HTMLElement), + () => + Array.from(this.focusedElement.childNodes).filter(node => node instanceof HTMLElement), [], ); diff --git a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts index 0c38b22acf..35f667bf20 100644 --- a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts @@ -20,14 +20,14 @@ export class AuthService { private oAuthService: OAuthService, private store: Store, @Optional() @Inject('ACCOUNT_OPTIONS') private options: any, - ) {} - - login(username: string, password: string): Observable { - const tenant = this.store.selectSnapshot(SessionState.getTenant); - + ) { this.oAuthService.configure( this.store.selectSnapshot(ConfigState.getOne('environment')).oAuthConfig, ); + } + + login(username: string, password: string): Observable { + const tenant = this.store.selectSnapshot(SessionState.getTenant); return from(this.oAuthService.loadDiscoveryDocument()).pipe( switchMap(() => @@ -63,7 +63,7 @@ export class AuthService { ) .pipe( switchMap(() => { - this.oAuthService.logOut(); + this.oAuthService.logOut(true); return this.store.dispatch(new GetAppConfiguration()); }), ); diff --git a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts index 7257a50ff3..4a3426dd51 100644 --- a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts @@ -126,12 +126,18 @@ export abstract class AbstractTreeService { } } +@Injectable() export abstract class AbstractNavTreeService extends AbstractTreeService implements OnDestroy { readonly id = 'name'; readonly parentId = 'parentName'; readonly hide = (item: T) => item.invisible || !this.isGranted(item); - readonly sort = (a: T, b: T) => a.order - b.order; + readonly sort = (a: T, b: T) => { + if (!a.order) return 1; + if (!b.order) return -1; + + return a.order - b.order; + }; constructor(protected actions: Actions, protected store: Store) { super(); diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index 4bbf25ade2..d9793529aa 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -190,7 +190,7 @@ export class ConfigState { @Action(GetAppConfiguration) addData({ patchState, dispatch }: StateContext) { - const apiName = this.store.selectSnapshot(ConfigState.getDeep('environment.application.name')); + const apiName = 'default'; const api = this.store.selectSnapshot(ConfigState.getApiUrl(apiName)); return this.http .get(`${api}/api/abp/application-configuration`) diff --git a/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts index 93214442ea..c1413d73ab 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts @@ -33,10 +33,10 @@ describe('Routes Service', () => { const visible = await service.visible$.pipe(take(1)).toPromise(); expect(flat.length).toBe(4); - expect(flat[0].name).toBe('foo'); - expect(flat[1].name).toBe('baz'); - expect(flat[2].name).toBe('x'); - expect(flat[3].name).toBe('bar'); + expect(flat[3].name).toBe('foo'); + expect(flat[0].name).toBe('baz'); + expect(flat[1].name).toBe('x'); + expect(flat[2].name).toBe('bar'); expect(tree.length).toBe(1); expect(tree[0].name).toBe('foo'); @@ -88,8 +88,8 @@ describe('Routes Service', () => { const visible = service.visible; expect(flat.length).toBe(2); - expect(flat[0].name).toBe('foo'); - expect(flat[1].name).toBe('x'); + expect(flat[1].name).toBe('foo'); + expect(flat[0].name).toBe('x'); expect(tree.length).toBe(1); expect(tree[0].name).toBe('foo'); @@ -114,10 +114,10 @@ describe('Routes Service', () => { const visible = service.visible; expect(flat.length).toBe(4); - expect(flat[0].name).toBe('foo'); - expect(flat[1].name).toBe('baz'); - expect(flat[2].name).toBe('x'); - expect(flat[3].name).toBe('bar'); + expect(flat[3].name).toBe('foo'); + expect(flat[0].name).toBe('baz'); + expect(flat[1].name).toBe('x'); + expect(flat[2].name).toBe('bar'); expect(tree.length).toBe(1); expect(tree[0].name).toBe('foo'); diff --git a/npm/ng-packs/packages/feature-management/package.json b/npm/ng-packs/packages/feature-management/package.json index fd1816df45..e2550bf15a 100644 --- a/npm/ng-packs/packages/feature-management/package.json +++ b/npm/ng-packs/packages/feature-management/package.json @@ -7,7 +7,8 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~2.9.0" + "@abp/ng.theme.shared": "~2.9.0", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts b/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts index 1020e60d4e..4aabf7bf80 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts @@ -15,7 +15,7 @@ export class FeatureManagementService { getFeatures(params: FeatureManagement.Provider): Observable { const request: Rest.Request = { method: 'GET', - url: '/api/abp/features', + url: '/api/feature-management/features', params, }; return this.rest.request(request, { @@ -30,7 +30,7 @@ export class FeatureManagementService { }: FeatureManagement.Provider & FeatureManagement.Features): Observable { const request: Rest.Request = { method: 'PUT', - url: '/api/abp/features', + url: '/api/feature-management/features', body: { features }, params: { providerKey, providerName }, }; diff --git a/npm/ng-packs/packages/identity/ngcc.config.js b/npm/ng-packs/packages/identity/ngcc.config.js new file mode 100644 index 0000000000..93f34d5655 --- /dev/null +++ b/npm/ng-packs/packages/identity/ngcc.config.js @@ -0,0 +1,7 @@ +module.exports = { + entryPoints: { + '.': {}, + './config': {}, + './dist': { ignore: true }, + }, +}; diff --git a/npm/ng-packs/packages/identity/package.json b/npm/ng-packs/packages/identity/package.json index 38ec9cd9d9..553b5518e9 100644 --- a/npm/ng-packs/packages/identity/package.json +++ b/npm/ng-packs/packages/identity/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "@abp/ng.permission-management": "~2.9.0", - "@abp/ng.theme.shared": "~2.9.0" + "@abp/ng.theme.shared": "~2.9.0", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html index 648b1024d0..bf49550f12 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html +++ b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html @@ -102,109 +102,110 @@ >
- - - -
-
- * - -
+ + +
diff --git a/npm/ng-packs/packages/identity/src/lib/identity.module.ts b/npm/ng-packs/packages/identity/src/lib/identity.module.ts index 9aeadac6a2..036c7e3607 100644 --- a/npm/ng-packs/packages/identity/src/lib/identity.module.ts +++ b/npm/ng-packs/packages/identity/src/lib/identity.module.ts @@ -2,7 +2,7 @@ import { CoreModule, LazyModuleFactory } from '@abp/ng.core'; import { PermissionManagementModule } from '@abp/ng.permission-management'; import { ThemeSharedModule } from '@abp/ng.theme.shared'; import { ModuleWithProviders, NgModule, NgModuleFactory } from '@angular/core'; -import { NgbDropdownModule, NgbTabsetModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgbDropdownModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; import { NgxValidateCoreModule } from '@ngx-validate/core'; import { NgxsModule } from '@ngxs/store'; import { RolesComponent } from './components/roles/roles.component'; @@ -17,7 +17,7 @@ import { IdentityState } from './states/identity.state'; NgxsModule.forFeature([IdentityState]), CoreModule, IdentityRoutingModule, - NgbTabsetModule, + NgbNavModule, ThemeSharedModule, NgbDropdownModule, PermissionManagementModule, diff --git a/npm/ng-packs/packages/permission-management/package.json b/npm/ng-packs/packages/permission-management/package.json index a09d4c7f8e..1bc7859bcf 100644 --- a/npm/ng-packs/packages/permission-management/package.json +++ b/npm/ng-packs/packages/permission-management/package.json @@ -7,7 +7,8 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~2.9.0" + "@abp/ng.theme.shared": "~2.9.0", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts b/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts index fa6433e952..4825036f81 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts +++ b/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts @@ -16,7 +16,7 @@ export class PermissionManagementService { ): Observable { const request: Rest.Request = { method: 'GET', - url: '/api/abp/permissions', + url: '/api/permission-management/permissions', params, }; @@ -33,7 +33,7 @@ export class PermissionManagementService { }: PermissionManagement.GrantedProvider & PermissionManagement.UpdateRequest): Observable { const request: Rest.Request = { method: 'PUT', - url: '/api/abp/permissions', + url: '/api/permission-management/permissions', body: { permissions }, params: { providerKey, providerName }, }; diff --git a/npm/ng-packs/packages/setting-management/config/src/enums/index.ts b/npm/ng-packs/packages/setting-management/config/src/enums/index.ts index 4a7a6a0e23..3bda94b078 100644 --- a/npm/ng-packs/packages/setting-management/config/src/enums/index.ts +++ b/npm/ng-packs/packages/setting-management/config/src/enums/index.ts @@ -1,2 +1 @@ -export * from './policy-names'; export * from './route-names'; diff --git a/npm/ng-packs/packages/setting-management/config/src/enums/policy-names.ts b/npm/ng-packs/packages/setting-management/config/src/enums/policy-names.ts deleted file mode 100644 index df66e95e3b..0000000000 --- a/npm/ng-packs/packages/setting-management/config/src/enums/policy-names.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const enum eSettingManagementPolicyNames { - Settings = 'AbpAccount.SettingManagement', -} diff --git a/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts b/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts index badc46ae9a..28c0fb7388 100644 --- a/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts +++ b/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts @@ -2,7 +2,6 @@ import { eLayoutType, RoutesService, SettingTabsService } from '@abp/ng.core'; import { eThemeSharedRouteNames } from '@abp/ng.theme.shared'; import { APP_INITIALIZER } from '@angular/core'; import { debounceTime, map } from 'rxjs/operators'; -import { eSettingManagementPolicyNames } from '../enums/policy-names'; import { eSettingManagementRouteNames } from '../enums/route-names'; export const SETTING_MANAGEMENT_ROUTE_PROVIDERS = [ @@ -22,9 +21,8 @@ export function configureRoutes(routes: RoutesService) { name: eSettingManagementRouteNames.Settings, path: '/setting-management', parentName: eThemeSharedRouteNames.Administration, - requiredPolicy: eSettingManagementPolicyNames.Settings, layout: eLayoutType.application, - order: 6, + order: 100, iconClass: 'fa fa-cog', }, ]); diff --git a/npm/ng-packs/packages/setting-management/ngcc.config.js b/npm/ng-packs/packages/setting-management/ngcc.config.js new file mode 100644 index 0000000000..93f34d5655 --- /dev/null +++ b/npm/ng-packs/packages/setting-management/ngcc.config.js @@ -0,0 +1,7 @@ +module.exports = { + entryPoints: { + '.': {}, + './config': {}, + './dist': { ignore: true }, + }, +}; diff --git a/npm/ng-packs/packages/setting-management/package.json b/npm/ng-packs/packages/setting-management/package.json index 969510305e..a6fb57ce18 100644 --- a/npm/ng-packs/packages/setting-management/package.json +++ b/npm/ng-packs/packages/setting-management/package.json @@ -7,7 +7,8 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~2.9.0" + "@abp/ng.theme.shared": "~2.9.0", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/tenant-management/ngcc.config.js b/npm/ng-packs/packages/tenant-management/ngcc.config.js new file mode 100644 index 0000000000..93f34d5655 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/ngcc.config.js @@ -0,0 +1,7 @@ +module.exports = { + entryPoints: { + '.': {}, + './config': {}, + './dist': { ignore: true }, + }, +}; diff --git a/npm/ng-packs/packages/tenant-management/package.json b/npm/ng-packs/packages/tenant-management/package.json index 0921e0dd9e..e8fd10094e 100644 --- a/npm/ng-packs/packages/tenant-management/package.json +++ b/npm/ng-packs/packages/tenant-management/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "@abp/ng.feature-management": "~2.9.0", - "@abp/ng.theme.shared": "~2.9.0" + "@abp/ng.theme.shared": "~2.9.0", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/theme-basic/package.json b/npm/ng-packs/packages/theme-basic/package.json index c7e3275e91..9307b1e21e 100644 --- a/npm/ng-packs/packages/theme-basic/package.json +++ b/npm/ng-packs/packages/theme-basic/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "@abp/ng.theme.shared": "~2.9.0" - }, + , "tslib": "^2.0.0" +}, "publishConfig": { "access": "public" } diff --git a/npm/ng-packs/packages/theme-basic/src/lib/actions/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/actions/index.ts deleted file mode 100644 index 2e29080de8..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/actions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './layout.actions'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/actions/layout.actions.ts b/npm/ng-packs/packages/theme-basic/src/lib/actions/layout.actions.ts deleted file mode 100644 index 3786ea82b7..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/actions/layout.actions.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Layout } from '../models/layout'; - -export class AddNavigationElement { - static readonly type = '[Layout] Add Navigation Element'; - constructor(public payload: Layout.NavigationElement | Layout.NavigationElement[]) {} -} - -export class RemoveNavigationElementByName { - static readonly type = '[Layout] Remove Navigation ElementByName'; - constructor(public name: string) {} -} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html index c4acbec880..eb68dc7df0 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html @@ -34,21 +34,15 @@
-
+ +
diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts index 06b6d5339c..6c4de6cf21 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts @@ -2,6 +2,8 @@ export * from './account-layout/account-layout.component'; export * from './application-layout/application-layout.component'; export * from './empty-layout/empty-layout.component'; export * from './logo/logo.component'; +export * from './nav-items/current-user.component'; +export * from './nav-items/languages.component'; export * from './nav-items/nav-items.component'; export * from './routes/routes.component'; export * from './validation-error/validation-error.component'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts new file mode 100644 index 0000000000..70238d3538 --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts @@ -0,0 +1,67 @@ +import { ApplicationConfiguration, AuthService, ConfigState } from '@abp/ng.core'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { Select } from '@ngxs/store'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'abp-current-user', + // tslint:disable-next-line: component-max-inline-declarations + template: ` + + {{ + 'AbpAccount::Login' | abpLocalization + }} + + + `, +}) +export class CurrentUserComponent implements OnInit { + @Select(ConfigState.getOne('currentUser')) + currentUser$: Observable; + + get smallScreen(): boolean { + return window.innerWidth < 992; + } + + constructor(private authService: AuthService, private router: Router) {} + + ngOnInit() {} + + logout() { + this.authService.logout().subscribe(() => { + this.router.navigate(['/'], { state: { redirectUrl: this.router.url } }); + }); + } +} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts new file mode 100644 index 0000000000..115e9e563e --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts @@ -0,0 +1,88 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Store, Select } from '@ngxs/store'; +import { SetLanguage, ConfigState, ApplicationConfiguration, SessionState } from '@abp/ng.core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import snq from 'snq'; + +@Component({ + selector: 'abp-languages', + // tslint:disable-next-line: component-max-inline-declarations + template: ` + + `, +}) +export class LanguagesComponent implements OnInit { + get smallScreen(): boolean { + return window.innerWidth < 992; + } + + @Select(ConfigState.getDeep('localization.languages')) + languages$: Observable; + + get defaultLanguage$(): Observable { + return this.languages$.pipe( + map( + languages => + snq( + () => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName, + ), + '', + ), + ); + } + + get dropdownLanguages$(): Observable { + return this.languages$.pipe( + map( + languages => + snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), + [], + ), + ); + } + + get selectedLangCulture(): string { + return this.store.selectSnapshot(SessionState.getLanguage); + } + + constructor(private store: Store) {} + + ngOnInit() {} + + onChangeLang(cultureName: string) { + this.store.dispatch(new SetLanguage(cultureName)); + } +} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html index cb86773db5..955ccb8878 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html @@ -1,81 +1,16 @@ - - - - + - + diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts index 6819807cd7..7a5ee9b37a 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts @@ -1,125 +1,12 @@ -import { - ABP, - ApplicationConfiguration, - AuthService, - ConfigState, - SessionState, - SetLanguage, - takeUntilDestroy, -} from '@abp/ng.core'; -import { - AfterViewInit, - Component, - Input, - OnDestroy, - TemplateRef, - TrackByFunction, - ViewChild, -} from '@angular/core'; -import { Navigate, RouterState } from '@ngxs/router-plugin'; -import { Select, Store } from '@ngxs/store'; -import compare from 'just-compare'; -import { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import snq from 'snq'; -import { AddNavigationElement } from '../../actions/layout.actions'; -import { eNavigationElementNames } from '../../enums/navigation-element-names'; -import { Layout } from '../../models/layout'; -import { LayoutState } from '../../states/layout.state'; +import { NavItem, NavItemsService } from '@abp/ng.theme.shared'; +import { Component, Input, TrackByFunction } from '@angular/core'; @Component({ selector: 'abp-nav-items', templateUrl: 'nav-items.component.html', }) -export class NavItemsComponent implements AfterViewInit, OnDestroy { - @Select(LayoutState.getNavigationElements) - navElements$: Observable; +export class NavItemsComponent { + trackByFn: TrackByFunction = (_, element) => element.id; - @Select(ConfigState.getOne('currentUser')) - currentUser$: Observable; - - @Select(ConfigState.getDeep('localization.languages')) - languages$: Observable; - - @ViewChild('currentUser', { static: false, read: TemplateRef }) - currentUserRef: TemplateRef; - - @ViewChild('language', { static: false, read: TemplateRef }) - languageRef: TemplateRef; - - @Input() - smallScreen: boolean; - - rightPartElements: TemplateRef[] = []; - - trackByFn: TrackByFunction = (_, element) => element.name; - - get defaultLanguage$(): Observable { - return this.languages$.pipe( - map( - languages => - snq( - () => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName, - ), - '', - ), - ); - } - - get dropdownLanguages$(): Observable { - return this.languages$.pipe( - map( - languages => - snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), - [], - ), - ); - } - - get selectedLangCulture(): string { - return this.store.selectSnapshot(SessionState.getLanguage); - } - - constructor(private store: Store, private authService: AuthService) {} - - ngAfterViewInit() { - const navigations = this.store - .selectSnapshot(LayoutState.getNavigationElements) - .map(({ name }) => name); - - if (navigations.indexOf(eNavigationElementNames.Language) < 0) { - this.store.dispatch( - new AddNavigationElement([ - { element: this.languageRef, order: 4, name: eNavigationElementNames.Language }, - { element: this.currentUserRef, order: 5, name: eNavigationElementNames.User }, - ]), - ); - } - - this.navElements$ - .pipe( - map(elements => elements.map(({ element }) => element)), - filter(elements => !compare(elements, this.rightPartElements)), - takeUntilDestroy(this), - ) - .subscribe(elements => { - setTimeout(() => (this.rightPartElements = elements), 0); - }); - } - - ngOnDestroy() {} - - onChangeLang(cultureName: string) { - this.store.dispatch(new SetLanguage(cultureName)); - } - - logout() { - this.authService.logout().subscribe(() => { - this.store.dispatch( - new Navigate(['/'], null, { - state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url }, - }), - ); - }); - } + constructor(public readonly navItems: NavItemsService) {} } diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html index d987f22860..42bdd7376a 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html @@ -18,10 +18,10 @@