From 78664f1d491a2bff36a151b7b7c3dd31d60371f3 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 12 May 2020 16:25:21 +0800 Subject: [PATCH 01/58] Make ICurrentPrincipalAccessor changeable. Resolve #3913 --- .../SignalR/AbpSignalRUserIdProvider.cs | 17 +++++-- .../HttpContextCurrentPrincipalAccessor.cs | 9 +--- .../Claims/ICurrentPrincipalAccessor.cs | 5 +- .../Claims/ThreadCurrentPrincipalAccessor.cs | 39 +++++++++++++-- .../FakeAuthenticationMiddleware.cs | 9 +++- .../Claims/CurrentPrincipalAccessor_Test.cs | 50 +++++++++++++++++++ 6 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/CurrentPrincipalAccessor_Test.cs diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRUserIdProvider.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRUserIdProvider.cs index 10d986933d..71abc1cc54 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRUserIdProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRUserIdProvider.cs @@ -1,21 +1,28 @@ using Microsoft.AspNetCore.SignalR; using Volo.Abp.DependencyInjection; +using Volo.Abp.Security.Claims; using Volo.Abp.Users; namespace Volo.Abp.AspNetCore.SignalR { public class AbpSignalRUserIdProvider : IUserIdProvider, ITransientDependency { - public ICurrentUser CurrentUser { get; } - - public AbpSignalRUserIdProvider(ICurrentUser currentUser) + private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor; + + private readonly ICurrentUser _currentUser; + + public AbpSignalRUserIdProvider(ICurrentPrincipalAccessor currentPrincipalAccessor, ICurrentUser currentUser) { - CurrentUser = currentUser; + _currentPrincipalAccessor = currentPrincipalAccessor; + _currentUser = currentUser; } public virtual string GetUserId(HubConnectionContext connection) { - return CurrentUser.Id?.ToString(); + using (_currentPrincipalAccessor.Change(connection.User)) + { + return _currentUser.Id?.ToString(); + } } } } diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs index d1318e2c3e..601ea32021 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs @@ -1,18 +1,13 @@ -using System.Security.Claims; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Volo.Abp.Security.Claims; namespace Volo.Abp.AspNetCore.Security.Claims { public class HttpContextCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor { - public override ClaimsPrincipal Principal => _httpContextAccessor.HttpContext?.User ?? base.Principal; - - private readonly IHttpContextAccessor _httpContextAccessor; - public HttpContextCurrentPrincipalAccessor(IHttpContextAccessor httpContextAccessor) { - _httpContextAccessor = httpContextAccessor; + CurrentScope.Value = () => httpContextAccessor.HttpContext?.User ?? GetThreadClaimsPrincipal(); } } } diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ICurrentPrincipalAccessor.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ICurrentPrincipalAccessor.cs index d49e61ddd6..c510765006 100644 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ICurrentPrincipalAccessor.cs +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ICurrentPrincipalAccessor.cs @@ -1,9 +1,12 @@ -using System.Security.Claims; +using System; +using System.Security.Claims; namespace Volo.Abp.Security.Claims { public interface ICurrentPrincipalAccessor { ClaimsPrincipal Principal { get; } + + IDisposable Change(ClaimsPrincipal principal); } } diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs index de0032c51d..f5d763705c 100644 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs @@ -1,11 +1,44 @@ -using System.Security.Claims; +using System; +using System.Security.Claims; using System.Threading; +using System.Xml.Schema; using Volo.Abp.DependencyInjection; namespace Volo.Abp.Security.Claims { public class ThreadCurrentPrincipalAccessor : ICurrentPrincipalAccessor, ISingletonDependency { - public virtual ClaimsPrincipal Principal => Thread.CurrentPrincipal as ClaimsPrincipal; + public Guid Id = Guid.NewGuid(); + + public virtual ClaimsPrincipal Principal + { + get => CurrentScope.Value?.Invoke(); + set => CurrentScope.Value = () => value; + } + + protected readonly AsyncLocal> CurrentScope; + + public ThreadCurrentPrincipalAccessor() + { + CurrentScope = new AsyncLocal> + { + Value = GetThreadClaimsPrincipal + }; + } + + protected ClaimsPrincipal GetThreadClaimsPrincipal() + { + return Thread.CurrentPrincipal as ClaimsPrincipal; + } + + public virtual IDisposable Change(ClaimsPrincipal principal) + { + var parentScope = Principal; + Principal = principal; + return new DisposeAction(() => + { + Principal = parentScope; + }); + } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs index b9caa1d7cb..cb26230b93 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs @@ -4,16 +4,19 @@ using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Volo.Abp.DependencyInjection; +using Volo.Abp.Security.Claims; namespace Volo.Abp.AspNetCore.Mvc.Authorization { public class FakeAuthenticationMiddleware : IMiddleware, ITransientDependency { private readonly FakeUserClaims _fakeUserClaims; + private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor; - public FakeAuthenticationMiddleware(FakeUserClaims fakeUserClaims) + public FakeAuthenticationMiddleware(FakeUserClaims fakeUserClaims, ICurrentPrincipalAccessor currentPrincipalAccessor) { _fakeUserClaims = fakeUserClaims; + _currentPrincipalAccessor = currentPrincipalAccessor; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) @@ -24,9 +27,11 @@ namespace Volo.Abp.AspNetCore.Mvc.Authorization { new ClaimsIdentity(_fakeUserClaims.Claims, "FakeSchema") }); + + //_currentPrincipalAccessor.Change(context.User); } await next(context); } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/CurrentPrincipalAccessor_Test.cs b/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/CurrentPrincipalAccessor_Test.cs new file mode 100644 index 0000000000..7a7671215d --- /dev/null +++ b/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/CurrentPrincipalAccessor_Test.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Security.Claims; +using Shouldly; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.Security.Claims +{ + public class CurrentPrincipalAccessor_Test : AbpIntegratedTest + { + private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor; + + public CurrentPrincipalAccessor_Test() + { + _currentPrincipalAccessor = GetRequiredService(); + } + + [Fact] + public void Should_Get_Changed_Principal_If() + { + var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List + { + new Claim(ClaimTypes.Name,"bob"), + new Claim(ClaimTypes.NameIdentifier,"123456") + })); + + var claimsPrincipal2 = new ClaimsPrincipal(new ClaimsIdentity(new List + { + new Claim(ClaimTypes.Name,"lee"), + new Claim(ClaimTypes.NameIdentifier,"654321") + })); + + + _currentPrincipalAccessor.Principal.ShouldBe(null); + + using (_currentPrincipalAccessor.Change(claimsPrincipal)) + { + _currentPrincipalAccessor.Principal.ShouldBe(claimsPrincipal); + + using (_currentPrincipalAccessor.Change(claimsPrincipal2)) + { + _currentPrincipalAccessor.Principal.ShouldBe(claimsPrincipal2); + } + + _currentPrincipalAccessor.Principal.ShouldBe(claimsPrincipal); + } + _currentPrincipalAccessor.Principal.ShouldBeNull(); + } + } +} From 441e18e2f4f8cd81c4c5771993b95ed27f9c8c64 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 13 May 2020 08:43:34 +0800 Subject: [PATCH 02/58] Refactoring ICurrentPrincipalAccessor. --- .../HttpContextCurrentPrincipalAccessor.cs | 12 ++++++-- .../Claims/ThreadCurrentPrincipalAccessor.cs | 30 +++++++------------ .../Security/FakeCurrentPrincipalAccessor.cs | 10 +++++-- .../Security/FakeCurrentPrincipalAccessor.cs | 8 +++-- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs index 601ea32021..717c578d33 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs @@ -1,13 +1,21 @@ -using Microsoft.AspNetCore.Http; +using System.Security.Claims; +using Microsoft.AspNetCore.Http; using Volo.Abp.Security.Claims; namespace Volo.Abp.AspNetCore.Security.Claims { public class HttpContextCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor { + private readonly IHttpContextAccessor _httpContextAccessor; + public HttpContextCurrentPrincipalAccessor(IHttpContextAccessor httpContextAccessor) { - CurrentScope.Value = () => httpContextAccessor.HttpContext?.User ?? GetThreadClaimsPrincipal(); + _httpContextAccessor = httpContextAccessor; + } + + protected override ClaimsPrincipal GetClaimsPrincipal() + { + return _httpContextAccessor.HttpContext?.User ?? base.GetClaimsPrincipal(); } } } diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs index f5d763705c..ad496ba916 100644 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs @@ -1,43 +1,33 @@ using System; using System.Security.Claims; using System.Threading; -using System.Xml.Schema; using Volo.Abp.DependencyInjection; namespace Volo.Abp.Security.Claims { public class ThreadCurrentPrincipalAccessor : ICurrentPrincipalAccessor, ISingletonDependency { - public Guid Id = Guid.NewGuid(); + public ClaimsPrincipal Principal => _currentPrincipal.Value ?? GetClaimsPrincipal(); - public virtual ClaimsPrincipal Principal - { - get => CurrentScope.Value?.Invoke(); - set => CurrentScope.Value = () => value; - } + private readonly AsyncLocal _currentPrincipal = new AsyncLocal(); - protected readonly AsyncLocal> CurrentScope; - - public ThreadCurrentPrincipalAccessor() + protected virtual ClaimsPrincipal GetClaimsPrincipal() { - CurrentScope = new AsyncLocal> - { - Value = GetThreadClaimsPrincipal - }; + return Thread.CurrentPrincipal as ClaimsPrincipal; } - protected ClaimsPrincipal GetThreadClaimsPrincipal() + public virtual IDisposable Change(ClaimsPrincipal principal) { - return Thread.CurrentPrincipal as ClaimsPrincipal; + return SetCurrent(principal); } - public virtual IDisposable Change(ClaimsPrincipal principal) + private IDisposable SetCurrent(ClaimsPrincipal principal) { - var parentScope = Principal; - Principal = principal; + var parent = Principal; + _currentPrincipal.Value = principal; return new DisposeAction(() => { - Principal = parentScope; + _currentPrincipal.Value = parent; }); } } diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs index ef00c20a24..5e6b064875 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs @@ -6,9 +6,13 @@ using Volo.Abp.Security.Claims; namespace MyCompanyName.MyProjectName.Security { [Dependency(ReplaceServices = true)] - public class FakeCurrentPrincipalAccessor : ICurrentPrincipalAccessor, ISingletonDependency + public class FakeCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor { - public ClaimsPrincipal Principal => GetPrincipal(); + protected override ClaimsPrincipal GetClaimsPrincipal() + { + return GetPrincipal(); + } + private ClaimsPrincipal _principal; private ClaimsPrincipal GetPrincipal() @@ -36,4 +40,4 @@ namespace MyCompanyName.MyProjectName.Security return _principal; } } -} \ No newline at end of file +} diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs index 4196dd09e1..5e6b064875 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs @@ -6,9 +6,13 @@ using Volo.Abp.Security.Claims; namespace MyCompanyName.MyProjectName.Security { [Dependency(ReplaceServices = true)] - public class FakeCurrentPrincipalAccessor : ICurrentPrincipalAccessor, ISingletonDependency + public class FakeCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor { - public ClaimsPrincipal Principal => GetPrincipal(); + protected override ClaimsPrincipal GetClaimsPrincipal() + { + return GetPrincipal(); + } + private ClaimsPrincipal _principal; private ClaimsPrincipal GetPrincipal() From 5a2b36eeb4b132d23df74ab2c9b9172c7410b54c Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 13 May 2020 09:57:02 +0800 Subject: [PATCH 03/58] Make GetClaimsPrincipal mehtod public. --- .../Security/Claims/HttpContextCurrentPrincipalAccessor.cs | 2 +- .../Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs | 2 +- .../Mvc/Authorization/FakeAuthenticationMiddleware.cs | 2 -- .../Security/FakeCurrentPrincipalAccessor.cs | 2 +- .../Security/FakeCurrentPrincipalAccessor.cs | 2 +- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs index 717c578d33..3e21c04538 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs @@ -13,7 +13,7 @@ namespace Volo.Abp.AspNetCore.Security.Claims _httpContextAccessor = httpContextAccessor; } - protected override ClaimsPrincipal GetClaimsPrincipal() + public override ClaimsPrincipal GetClaimsPrincipal() { return _httpContextAccessor.HttpContext?.User ?? base.GetClaimsPrincipal(); } diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs index ad496ba916..a4809631fd 100644 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs @@ -11,7 +11,7 @@ namespace Volo.Abp.Security.Claims private readonly AsyncLocal _currentPrincipal = new AsyncLocal(); - protected virtual ClaimsPrincipal GetClaimsPrincipal() + public virtual ClaimsPrincipal GetClaimsPrincipal() { return Thread.CurrentPrincipal as ClaimsPrincipal; } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs index cb26230b93..a1f3e473ea 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs @@ -27,8 +27,6 @@ namespace Volo.Abp.AspNetCore.Mvc.Authorization { new ClaimsIdentity(_fakeUserClaims.Claims, "FakeSchema") }); - - //_currentPrincipalAccessor.Change(context.User); } await next(context); diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs index 5e6b064875..a6d9aae833 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs @@ -8,7 +8,7 @@ namespace MyCompanyName.MyProjectName.Security [Dependency(ReplaceServices = true)] public class FakeCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor { - protected override ClaimsPrincipal GetClaimsPrincipal() + public override ClaimsPrincipal GetClaimsPrincipal() { return GetPrincipal(); } diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs index 5e6b064875..a6d9aae833 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs @@ -8,7 +8,7 @@ namespace MyCompanyName.MyProjectName.Security [Dependency(ReplaceServices = true)] public class FakeCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor { - protected override ClaimsPrincipal GetClaimsPrincipal() + public override ClaimsPrincipal GetClaimsPrincipal() { return GetPrincipal(); } From 88f2d83846a032fd92cbd5195a63b04fd22a88a0 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 13 May 2020 11:31:14 +0800 Subject: [PATCH 04/58] Update FakeAuthenticationMiddleware.cs --- .../Mvc/Authorization/FakeAuthenticationMiddleware.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs index a1f3e473ea..5ee6ec5c1f 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs @@ -4,19 +4,16 @@ using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Volo.Abp.DependencyInjection; -using Volo.Abp.Security.Claims; namespace Volo.Abp.AspNetCore.Mvc.Authorization { public class FakeAuthenticationMiddleware : IMiddleware, ITransientDependency { private readonly FakeUserClaims _fakeUserClaims; - private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor; - public FakeAuthenticationMiddleware(FakeUserClaims fakeUserClaims, ICurrentPrincipalAccessor currentPrincipalAccessor) + public FakeAuthenticationMiddleware(FakeUserClaims fakeUserClaims) { _fakeUserClaims = fakeUserClaims; - _currentPrincipalAccessor = currentPrincipalAccessor; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) From 95bd402a51deb957a5260b7e08f704ac0367fef3 Mon Sep 17 00:00:00 2001 From: 51mono <46481080+zhishile@users.noreply.github.com> Date: Thu, 14 May 2020 07:52:47 +0800 Subject: [PATCH 05/58] Add docs module client proxies --- .../Volo.Docs.Admin.HttpApi.Client.csproj | 2 ++ .../Volo/Docs/Admin/DocsHttpApiClientModule.cs | 12 +++++++++--- .../Volo.Docs.HttpApi.Client.csproj | 1 + .../Volo/Docs/DocsHttpApiClientModule.cs | 12 ++++++++++-- 4 files changed, 22 insertions(+), 5 deletions(-) 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 50e135a237..076bc1d37a 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 @@ -12,6 +12,8 @@ + + diff --git a/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo/Docs/Admin/DocsHttpApiClientModule.cs b/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo/Docs/Admin/DocsHttpApiClientModule.cs index bcab405f63..e6aa9c260d 100644 --- a/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo/Docs/Admin/DocsHttpApiClientModule.cs +++ b/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo/Docs/Admin/DocsHttpApiClientModule.cs @@ -1,11 +1,17 @@ -using Volo.Abp.Modularity; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Http.Client; +using Volo.Abp.Modularity; namespace Volo.Docs.Admin { [DependsOn( - typeof(DocsAdminApplicationContractsModule))] + typeof(DocsAdminApplicationContractsModule), + typeof(AbpHttpClientModule))] public class DocsAdminHttpApiClientModule : AbpModule { - //TODO: Create client proxies! + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddHttpClientProxies(typeof(DocsAdminApplicationContractsModule).Assembly); + } } } diff --git a/modules/docs/src/Volo.Docs.HttpApi.Client/Volo.Docs.HttpApi.Client.csproj b/modules/docs/src/Volo.Docs.HttpApi.Client/Volo.Docs.HttpApi.Client.csproj index 5df5515e32..ed9acfaf20 100644 --- a/modules/docs/src/Volo.Docs.HttpApi.Client/Volo.Docs.HttpApi.Client.csproj +++ b/modules/docs/src/Volo.Docs.HttpApi.Client/Volo.Docs.HttpApi.Client.csproj @@ -12,6 +12,7 @@ + diff --git a/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs b/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs index 1c208c6195..97389a9fa3 100644 --- a/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs +++ b/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs @@ -1,11 +1,19 @@ -using Volo.Abp.Modularity; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Http.Client; +using Volo.Abp.Modularity; namespace Volo.Docs { [DependsOn( - typeof(DocsApplicationContractsModule))] + typeof(DocsApplicationContractsModule), + typeof(AbpHttpClientModule) + )] public class DocsHttpApiClientModule : AbpModule { //TODO: Create client proxies + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddHttpClientProxies(typeof(DocsApplicationContractsModule).Assembly); + } } } From f5e5edae820ac0e3b35feb4f0714530fd0844cc3 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Thu, 14 May 2020 09:49:49 +0800 Subject: [PATCH 06/58] Search phone number --- .../MongoDB/MongoIdentityUserRepository.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index 9eb1664bd5..52a345b0c4 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -15,13 +15,13 @@ namespace Volo.Abp.Identity.MongoDB { public class MongoIdentityUserRepository : MongoDbRepository, IIdentityUserRepository { - public MongoIdentityUserRepository(IMongoDbContextProvider dbContextProvider) + public MongoIdentityUserRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) { } public virtual async Task FindByNormalizedUserNameAsync( - string normalizedUserName, + string normalizedUserName, bool includeDetails = true, CancellationToken cancellationToken = default) { @@ -33,7 +33,7 @@ namespace Volo.Abp.Identity.MongoDB } public virtual async Task> GetRoleNamesAsync( - Guid id, + Guid id, CancellationToken cancellationToken = default) { var user = await GetAsync(id, cancellationToken: GetCancellationToken(cancellationToken)); @@ -42,8 +42,8 @@ namespace Volo.Abp.Identity.MongoDB } public virtual async Task FindByLoginAsync( - string loginProvider, - string providerKey, + string loginProvider, + string providerKey, bool includeDetails = true, CancellationToken cancellationToken = default) { @@ -71,7 +71,7 @@ namespace Volo.Abp.Identity.MongoDB } public virtual async Task> GetListByNormalizedRoleNameAsync( - string normalizedRoleName, + string normalizedRoleName, bool includeDetails = false, CancellationToken cancellationToken = default) { @@ -89,9 +89,9 @@ namespace Volo.Abp.Identity.MongoDB public virtual async Task> GetListAsync( string sorting = null, - int maxResultCount = int.MaxValue, - int skipCount = 0, - string filter = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + string filter = null, bool includeDetails = false, CancellationToken cancellationToken = default) { @@ -100,7 +100,8 @@ namespace Volo.Abp.Identity.MongoDB !filter.IsNullOrWhiteSpace(), u => u.UserName.Contains(filter) || - u.Email.Contains(filter) + u.Email.Contains(filter) || + (u.PhoneNumber != null && u.PhoneNumber.Contains(filter)) ) .OrderBy(sorting ?? nameof(IdentityUser.UserName)) .As>() @@ -132,4 +133,4 @@ namespace Volo.Abp.Identity.MongoDB .LongCountAsync(GetCancellationToken(cancellationToken)); } } -} \ No newline at end of file +} From db5f2ca9ca994a3cd548f63b3db2c64801486c61 Mon Sep 17 00:00:00 2001 From: 51mono <46481080+zhishile@users.noreply.github.com> Date: Thu, 14 May 2020 10:47:08 +0800 Subject: [PATCH 07/58] Remove the Todo comment --- .../Volo/Docs/DocsHttpApiClientModule.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs b/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs index 97389a9fa3..5f0d86bc98 100644 --- a/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs +++ b/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs @@ -10,7 +10,6 @@ namespace Volo.Docs )] public class DocsHttpApiClientModule : AbpModule { - //TODO: Create client proxies public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddHttpClientProxies(typeof(DocsApplicationContractsModule).Assembly); From 54c685f80170d5689b2fdcd43e8fe306d7b6fe5f Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Sat, 16 May 2020 17:31:05 +0800 Subject: [PATCH 08/58] Change HttpApiClient project target to netstandard2.0 --- .../MyCompanyName.MyProjectName.HttpApi.Client.csproj | 2 +- .../MyCompanyName.MyProjectName.HttpApi.Client.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj index 4c70886aaa..fa83d6c25e 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj @@ -3,7 +3,7 @@ - netcoreapp3.1 + netstandard2.0 MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj index 1466849524..4fb9f73c33 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj @@ -3,7 +3,7 @@ - netcoreapp3.1 + netstandard2.0 MyCompanyName.MyProjectName From d73281680f1eeba38479d31f56fa43ee5a6065e1 Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Sat, 16 May 2020 19:33:09 +0800 Subject: [PATCH 09/58] Razor page's exception filter ignores void and task return types. Resolve #3969 --- .../Abp/AspNetCore/Mvc/ActionResultHelper.cs | 11 +++- .../AbpExceptionPageFilter.cs | 18 +++--- .../AspNetCore/Mvc/Uow/AbpUowPageFilter.cs | 6 +- .../ExceptionTestPage.cshtml.cs | 28 ++++++-- .../ExceptionTestPage_Tests.cs | 64 ++++++++++++++++--- .../Mvc/Features/FeatureTestPage.cshtml.cs | 6 +- .../Mvc/Uow/UnitOfWorkTestPage.cshtml.cs | 8 +-- 7 files changed, 104 insertions(+), 37 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionResultHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionResultHelper.cs index 577c8d0786..f2d7195e08 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionResultHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionResultHelper.cs @@ -8,7 +8,7 @@ namespace Volo.Abp.AspNetCore.Mvc { public static class ActionResultHelper { - public static List ObjectResultTypes { get; } + public static List ObjectResultTypes { get; } static ActionResultHelper() { @@ -20,10 +20,15 @@ namespace Volo.Abp.AspNetCore.Mvc }; } - public static bool IsObjectResult(Type returnType) + public static bool IsObjectResult(Type returnType, params Type[] excludeTypes) { returnType = AsyncHelper.UnwrapTask(returnType); + if (!excludeTypes.IsNullOrEmpty() && excludeTypes.Any(t => t.IsAssignableFrom(returnType))) + { + return false; + } + if (!typeof(IActionResult).IsAssignableFrom(returnType)) { return true; @@ -32,4 +37,4 @@ namespace Volo.Abp.AspNetCore.Mvc return ObjectResultTypes.Any(t => t.IsAssignableFrom(returnType)); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs index fb0a7c5a1f..9e449f0474 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -25,7 +25,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling public AbpExceptionPageFilter( IExceptionToErrorInfoConverter errorInfoConverter, - IHttpExceptionStatusCodeFinder statusCodeFinder, + IHttpExceptionStatusCodeFinder statusCodeFinder, IJsonSerializer jsonSerializer) { _errorInfoConverter = errorInfoConverter; @@ -34,13 +34,12 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling Logger = NullLogger.Instance; } - public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) { return Task.CompletedTask; } - + public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) { if (context.HandlerMethod == null || !ShouldHandleException(context)) @@ -54,20 +53,20 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling { return;; } - + await HandleAndWrapException(pageHandlerExecutedContext); } - + protected virtual bool ShouldHandleException(PageHandlerExecutingContext context) { //TODO: Create DontWrap attribute to control wrapping..? if (context.ActionDescriptor.IsPageAction() && - ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType)) + ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType, typeof(void))) { return true; } - + if (context.HttpContext.Request.CanAccept(MimeTypes.Application.Json)) { return true; @@ -107,6 +106,5 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling context.Exception = null; //Handled! } - } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs index 993a12b0e7..bcef10ecd4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs @@ -38,7 +38,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow context.HttpContext.Items["_AbpActionInfo"] = new AbpActionInfoInHttpContext { - IsObjectResult = ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType) + IsObjectResult = ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType, typeof(void)) }; if (unitOfWorkAttr?.IsDisabled == true) @@ -71,7 +71,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow } } } - + private AbpUnitOfWorkOptions CreateOptions(PageHandlerExecutingContext context, UnitOfWorkAttribute unitOfWorkAttribute) { var options = new AbpUnitOfWorkOptions(); @@ -102,4 +102,4 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow return result.Exception == null || result.ExceptionHandled; } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs index 717d31effe..410f9d124d 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs @@ -1,18 +1,36 @@ -using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling { public class ExceptionTestPage : AbpPageModel { - public void OnGetUserFriendlyException1() + public void OnGetUserFriendlyException_void() { throw new UserFriendlyException("This is a sample exception!"); } - - public IActionResult OnGetUserFriendlyException2() + + public Task OnGetUserFriendlyException_Task() + { + throw new UserFriendlyException("This is a sample exception!"); + } + + + public IActionResult OnGetUserFriendlyException_ActionResult() { throw new UserFriendlyException("This is a sample exception!"); } + + public JsonResult OnGetUserFriendlyException_JsonResult() + { + throw new UserFriendlyException("This is a sample exception!"); + } + + public Task OnGetUserFriendlyException_Task_JsonResult() + { + throw new UserFriendlyException("This is a sample exception!"); + } + } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs index 7013c0c676..d7c609aa59 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -24,15 +24,33 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling } [Fact] - public async Task Should_Return_RemoteServiceErrorResponse_For_UserFriendlyException_For_Void_Return_Value() + public async Task Should_Not_Handle_Exceptions_For_Void_Return_Values() { - var result = await GetResponseAsObjectAsync("/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException1", HttpStatusCode.Forbidden); - result.Error.ShouldNotBeNull(); - result.Error.Message.ShouldBe("This is a sample exception!"); + await Assert.ThrowsAsync( + async () => await GetResponseAsStringAsync( + "/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_Void" + ) + ); #pragma warning disable 4014 _fakeExceptionSubscriber - .Received() + .DidNotReceive() + .HandleAsync(Arg.Any()); +#pragma warning restore 4014 + } + + [Fact] + public async Task Should_Not_Handle_Exceptions_For_Task_Return_Values() + { + await Assert.ThrowsAsync( + async () => await GetResponseAsStringAsync( + "/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_Task" + ) + ); + +#pragma warning disable 4014 + _fakeExceptionSubscriber + .DidNotReceive() .HandleAsync(Arg.Any()); #pragma warning restore 4014 } @@ -41,8 +59,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling public async Task Should_Not_Handle_Exceptions_For_ActionResult_Return_Values() { await Assert.ThrowsAsync( - async () => await GetResponseAsObjectAsync( - "/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException2" + async () => await GetResponseAsStringAsync( + "/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_ActionResult" ) ); @@ -50,7 +68,35 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling _fakeExceptionSubscriber .DidNotReceive() .HandleAsync(Arg.Any()); +#pragma warning restore 4014 + } + + [Fact] + public async Task Should_Return_RemoteServiceErrorResponse_For_UserFriendlyException_For_Object_Return_Value() + { + var result = await GetResponseAsObjectAsync("/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_JsonResult", HttpStatusCode.Forbidden); + result.Error.ShouldNotBeNull(); + result.Error.Message.ShouldBe("This is a sample exception!"); + +#pragma warning disable 4014 + _fakeExceptionSubscriber + .Received() + .HandleAsync(Arg.Any()); +#pragma warning restore 4014 + } + + [Fact] + public async Task Should_Return_RemoteServiceErrorResponse_For_UserFriendlyException_For_Task_Object_Return_Value() + { + var result = await GetResponseAsObjectAsync("/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_Task_JsonResult", HttpStatusCode.Forbidden); + result.Error.ShouldNotBeNull(); + result.Error.Message.ShouldBe("This is a sample exception!"); + +#pragma warning disable 4014 + _fakeExceptionSubscriber + .Received() + .HandleAsync(Arg.Any()); #pragma warning restore 4014 } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs index 6a051a29ee..d9f600d9ed 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs @@ -14,9 +14,9 @@ namespace Volo.Abp.AspNetCore.Mvc.Features } [RequiresFeature("NotAllowedFeature")] - public void OnGetNotAllowedFeature() + public ObjectResult OnGetNotAllowedFeature() { - + return new ObjectResult(42); } public ObjectResult OnGetNoFeature() @@ -24,4 +24,4 @@ namespace Volo.Abp.AspNetCore.Mvc.Features return new ObjectResult(42); } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs index ce54e8d34f..1428f2c88c 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs @@ -32,23 +32,23 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow } [UnitOfWork(isTransactional: true)] - public void OnGetHandledException() + public ObjectResult OnGetHandledException() { CurrentUnitOfWork.ShouldNotBeNull(); CurrentUnitOfWork.Options.IsTransactional.ShouldBeTrue(); throw new UserFriendlyException("This is a sample exception!"); } - + public ObjectResult OnGetExceptionOnComplete() { CurrentUnitOfWork.ShouldNotBeNull(); CurrentUnitOfWork.Options.IsTransactional.ShouldBeFalse(); _testUnitOfWorkConfig.ThrowExceptionOnComplete = true; - + //Prevent rendering of pages. return new ObjectResult(""); } } -} \ No newline at end of file +} From b72ba88752f31e3ebafd7f31b36ce45f3747257f Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Sat, 16 May 2020 19:34:51 +0800 Subject: [PATCH 10/58] Update ExceptionTestPage.cshtml.cs --- .../AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs index 410f9d124d..dd4edebec1 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs @@ -16,7 +16,6 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling throw new UserFriendlyException("This is a sample exception!"); } - public IActionResult OnGetUserFriendlyException_ActionResult() { throw new UserFriendlyException("This is a sample exception!"); From 5e84b6ad15dac16184cd08daf1185301bf14badf Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Sun, 17 May 2020 02:09:42 +0300 Subject: [PATCH 11/58] add TotalQuestionCount --- .../AbpIoLocalization/Admin/Localization/Resources/en.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index 46cd1cb470..b5e5d8d5b2 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -148,6 +148,7 @@ "SuccessfullySent": "Successfully Sent", "SuccessfullyDeleted": "Successfully Deleted", "DiscountRequestDeletionWarningMessage": "Discount request will be deleted" , - "BusinessType": "Business Type" + "BusinessType": "Business Type", + "TotalQuestionCount": "Total question count" } } \ No newline at end of file From 9a19517d174b5c597b2d479144bb661c22b2a657 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Sun, 17 May 2020 02:10:43 +0300 Subject: [PATCH 12/58] add RemainingQuestionCount --- .../AbpIoLocalization/Admin/Localization/Resources/en.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index b5e5d8d5b2..6ce9c8c1bf 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -149,6 +149,7 @@ "SuccessfullyDeleted": "Successfully Deleted", "DiscountRequestDeletionWarningMessage": "Discount request will be deleted" , "BusinessType": "Business Type", - "TotalQuestionCount": "Total question count" + "TotalQuestionCount": "Total question count", + "RemainingQuestionCount": "Remaining question count" } } \ No newline at end of file From 2f0a7af7b3b4b849a392ad21572565789055bc22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 17 May 2020 22:56:15 +0300 Subject: [PATCH 13/58] Resolved #3984 Expose enum type & members to client side for extension properties. --- .../ObjectExtending/EntityExtensionDto.cs | 1 - .../ObjectExtending/ExtensionEnumDto.cs | 13 +++ .../ObjectExtending/ExtensionEnumFieldDto.cs | 12 +++ .../ObjectExtending/ObjectExtensionsDto.cs | 2 + .../Form/AbpSelectTagHelperService.cs | 95 ++++++------------- .../ui-extensions.js | 88 +++++++++++++++-- .../CachedObjectExtensionsDtoService.cs | 45 ++++++++- .../Volo/Abp/AbpInitializationException.cs | 46 +++++++++ .../Volo/Abp/Modularity/ModuleLoader.cs | 27 +++++- .../Volo/Abp/Modularity/ModuleManager.cs | 22 ++++- .../Volo/Abp/Reflection/TypeHelper.cs | 2 +- .../ExtraPropertiesValueConverter.cs | 6 +- .../Abp/Localization/LocalizableString.cs | 0 .../LocalizationResourceNameAttribute.cs | 0 .../Abp/Localization/StringLocalizerHelper.cs | 50 ++++++++++ ...xtensionPropertyConfigurationExtensions.cs | 32 +++++++ .../Volo/Abp/Reflection/TypeHelper_Tests.cs | 7 ++ .../ObjectExtensionManager_Tests.cs | 20 ++-- 18 files changed, 380 insertions(+), 88 deletions(-) create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/AbpInitializationException.cs rename framework/src/{Volo.Abp.Localization => Volo.Abp.Localization.Abstractions}/Volo/Abp/Localization/LocalizableString.cs (100%) rename framework/src/{Volo.Abp.Localization => Volo.Abp.Localization.Abstractions}/Volo/Abp/Localization/LocalizationResourceNameAttribute.cs (100%) create mode 100644 framework/src/Volo.Abp.Localization/Volo/Abp/Localization/StringLocalizerHelper.cs create mode 100644 framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs index 83093e0370..d15870e7c1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs @@ -9,6 +9,5 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending public Dictionary Properties { get; set; } public Dictionary Configuration { get; set; } - } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs new file mode 100644 index 0000000000..4f2dc526eb --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +{ + [Serializable] + public class ExtensionEnumDto + { + public List Fields { get; set; } + + public string LocalizationResource { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs new file mode 100644 index 0000000000..ddc7c349d4 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs @@ -0,0 +1,12 @@ +using System; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +{ + [Serializable] + public class ExtensionEnumFieldDto + { + public string Name { get; set; } + + public object Value { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs index fd9665eab1..0338bbd4dc 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs @@ -7,5 +7,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending public class ObjectExtensionsDto { public Dictionary Modules { get; set; } + + public Dictionary Enums { get; set; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs index bb79740a41..21f6c498a9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Linq.Dynamic.Core; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Rendering; @@ -12,6 +13,7 @@ using Microsoft.Extensions.Localization; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; using Volo.Abp.DynamicProxy; +using Volo.Abp.Localization; using Volo.Abp.Reflection; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form @@ -21,12 +23,18 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form private readonly IHtmlGenerator _generator; private readonly HtmlEncoder _encoder; private readonly IAbpTagHelperLocalizer _tagHelperLocalizer; + private readonly IStringLocalizerFactory _stringLocalizerFactory; - public AbpSelectTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IAbpTagHelperLocalizer tagHelperLocalizer) + public AbpSelectTagHelperService( + IHtmlGenerator generator, + HtmlEncoder encoder, + IAbpTagHelperLocalizer tagHelperLocalizer, + IStringLocalizerFactory stringLocalizerFactory) { _generator = generator; _encoder = encoder; _tagHelperLocalizer = tagHelperLocalizer; + _stringLocalizerFactory = stringLocalizerFactory; } public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) @@ -209,74 +217,33 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form } var containerLocalizer = _tagHelperLocalizer.GetLocalizerOrNull(explorer.Container.ModelType.Assembly); - IStringLocalizer modelObjectLocalizer = null; - if (explorer.Model != null) - { - modelObjectLocalizer = _tagHelperLocalizer.GetLocalizerOrNull(ProxyHelper.UnProxy(explorer.Model).GetType().Assembly); - } - - selectItems.AddRange(enumType.GetEnumNames() - .Select(enumName => new SelectListItem - { - Value = Convert.ToUInt64(Enum.Parse(enumType, enumName)).ToString(), - Text = GetLocalizedEnumFieldName(containerLocalizer, modelObjectLocalizer, enumType, enumName) - })); - - return selectItems; - } - - protected virtual string GetLocalizedEnumFieldName( - IStringLocalizer containerLocalizer, - IStringLocalizer modelObjectLocalizer, - Type enumType, - string fieldName) - { - LocalizedString localizedString; - - //Look for the enum name + enum field name - - var localizationKey = enumType.Name + "." + fieldName; - if (containerLocalizer != null) - { - localizedString = containerLocalizer[localizationKey]; - if (!localizedString.ResourceNotFound) - { - return localizedString.Value; - } - } - if (modelObjectLocalizer != null) + foreach (var enumValue in enumType.GetEnumValues()) { - localizedString = modelObjectLocalizer[localizationKey]; - if (!localizedString.ResourceNotFound) + var memberName = enumType.GetEnumName(enumValue); + var localizedMemberName = AbpInternalLocalizationHelper.LocalizeWithFallback( + new[] + { + containerLocalizer, + _stringLocalizerFactory.CreateDefaultOrNull() + }, + new[] + { + $"Enum:{enumType.Name}.{memberName}", + $"{enumType.Name}.{memberName}", + memberName + }, + memberName + ); + + selectItems.Add(new SelectListItem { - return localizedString.Value; - } - } - - //Look for the enum field name - - localizationKey = fieldName; - - if (containerLocalizer != null) - { - localizedString = containerLocalizer[localizationKey]; - if (!localizedString.ResourceNotFound) - { - return localizedString.Value; - } - } - - if (modelObjectLocalizer != null) - { - localizedString = modelObjectLocalizer[localizationKey]; - if (!localizedString.ResourceNotFound) - { - return localizedString.Value; - } + Value = enumValue.ToString(), + Text = localizedMemberName + }); } - return fieldName; + return selectItems; } protected virtual List GetSelectItemsFromAttribute( diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js index 9ee545615c..dbf73618ad 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js @@ -99,6 +99,26 @@ function initializeObjectExtensions() { + var getShortEnumTypeName = function (enumType) { + var lastDotIndex = enumType.lastIndexOf('.'); + if (lastDotIndex < 0) { + return enumType; + } + + return enumType.substr(lastDotIndex + 1); + }; + + var getEnumMemberName = function (enumInfo, enumMemberValue) { + for (var i = 0; i < enumInfo.fields.length; i++) { + var enumField = enumInfo.fields[i]; + if (enumField.value == enumMemberValue) { + return enumField.name; + } + } + + return null; + }; + function localizeDisplayName(propertyName, displayName) { if (displayName && displayName.name) { return abp.localization.localize(displayName.name, displayName.resource); @@ -111,6 +131,48 @@ return abp.localization.localize(propertyName); } + function localizeWithFallback(localizationResources, keys, defaultValue) { + for (var i = 0; i < localizationResources.length; i++) { + var localizationResource = localizationResources[i]; + if (!localizationResource) { + continue; + } + + for (var j = 0; j < keys.length; j++) { + var key = keys[j]; + + if (abp.localization.isLocalized(key, localizationResource)) { + return abp.localization.localize(key, localizationResource); + } + } + } + + return defaultValue; + } + + function localizeEnumMember(property, row) { + var enumType = property.config.type; + var enumInfo = abp.objectExtensions.enums[enumType]; + var enumMemberValue = row.extraProperties[property.name]; + var enumMemberName = getEnumMemberName(enumInfo, enumMemberValue); + + if (!enumMemberName) { + return enumMemberValue; + } + + var shortEnumType = getShortEnumTypeName(enumType); + + return localizeWithFallback( + [enumInfo.localizationResource, abp.localization.defaultResourceName], + [ + 'Enum:' + shortEnumType + '.' + enumMemberName, + shortEnumType + '.' + enumMemberName, + enumMemberName + ], + enumMemberName + ); + } + function configureTableColumns(tableName, columnConfigs) { abp.ui.extensions.tableColumns.get(tableName) .addContributor( @@ -137,16 +199,30 @@ return tableProperties; } + function convertPropertyToColumnConfig(property) { + var columnConfig = { + title: localizeDisplayName(property.name, property.config.displayName), + data: "extraProperties." + property.name, + orderable: false + }; + + if (property.config.typeSimple === 'enum') { + columnConfig.render = function (data, type, row) { + return localizeEnumMember( + property, + row + ); + } + } + + return columnConfig; + } + function convertPropertiesToColumnConfigs(properties) { var columnConfigs = []; for (var i = 0; i < properties.length; i++) { - var tableProperty = properties[i]; - columnConfigs.push({ - title: localizeDisplayName(tableProperty.name, tableProperty.config.displayName), - data: "extraProperties." + tableProperty.name, - orderable: false - }); + columnConfigs.push(convertPropertyToColumnConfig(properties[i])); } return columnConfigs; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs index aab28b3cb9..870ff0368e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs @@ -39,7 +39,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending { var objectExtensionsDto = new ObjectExtensionsDto { - Modules = new Dictionary() + Modules = new Dictionary(), + Enums = new Dictionary() }; foreach (var moduleConfig in ObjectExtensionManager.Instance.Modules()) @@ -47,6 +48,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending objectExtensionsDto.Modules[moduleConfig.Key] = CreateModuleExtensionDto(moduleConfig.Value); } + FillEnums(objectExtensionsDto); + return objectExtensionsDto; } @@ -183,5 +186,45 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending return null; } + + protected virtual void FillEnums(ObjectExtensionsDto objectExtensionsDto) + { + var enumProperties = ObjectExtensionManager.Instance.Modules().Values + .SelectMany( + m => m.Entities.Values.SelectMany( + e => e.GetProperties() + ) + ) + .Where(p => p.Type.IsEnum) + .ToList(); + + foreach (var enumProperty in enumProperties) + { + // ReSharper disable once AssignNullToNotNullAttribute (enumProperty.Type.FullName can not be null for this case) + objectExtensionsDto.Enums[enumProperty.Type.FullName] = CreateExtensionEnumDto(enumProperty); + } + } + + protected virtual ExtensionEnumDto CreateExtensionEnumDto(ExtensionPropertyConfiguration enumProperty) + { + var extensionEnumDto = new ExtensionEnumDto + { + Fields = new List(), + LocalizationResource = enumProperty.GetLocalizationResourceNameOrNull() + }; + + foreach (var enumValue in enumProperty.Type.GetEnumValues()) + { + extensionEnumDto.Fields.Add( + new ExtensionEnumFieldDto + { + Name = enumProperty.Type.GetEnumName(enumValue), + Value = enumValue + } + ); + } + + return extensionEnumDto; + } } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpInitializationException.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpInitializationException.cs new file mode 100644 index 0000000000..1bcd513168 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpInitializationException.cs @@ -0,0 +1,46 @@ +using System; +using System.Runtime.Serialization; + +namespace Volo.Abp +{ + public class AbpInitializationException : AbpException + { + /// + /// Creates a new object. + /// + public AbpInitializationException() + { + + } + + /// + /// Creates a new object. + /// + /// Exception message + public AbpInitializationException(string message) + : base(message) + { + + } + + /// + /// Creates a new object. + /// + /// Exception message + /// Inner exception + public AbpInitializationException(string message, Exception innerException) + : base(message, innerException) + { + + } + + /// + /// Constructor for serializing. + /// + public AbpInitializationException(SerializationInfo serializationInfo, StreamingContext context) + : base(serializationInfo, context) + { + + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs index 30bf523c1d..374e1adb78 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs @@ -105,7 +105,14 @@ namespace Volo.Abp.Modularity //PreConfigureServices foreach (var module in modules.Where(m => m.Instance is IPreConfigureServices)) { - ((IPreConfigureServices)module.Instance).PreConfigureServices(context); + try + { + ((IPreConfigureServices)module.Instance).PreConfigureServices(context); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during {nameof(IPreConfigureServices.PreConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + } } //ConfigureServices @@ -119,13 +126,27 @@ namespace Volo.Abp.Modularity } } - module.Instance.ConfigureServices(context); + try + { + module.Instance.ConfigureServices(context); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + } } //PostConfigureServices foreach (var module in modules.Where(m => m.Instance is IPostConfigureServices)) { - ((IPostConfigureServices)module.Instance).PostConfigureServices(context); + try + { + ((IPostConfigureServices)module.Instance).PostConfigureServices(context); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during {nameof(IPostConfigureServices.PostConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + } } foreach (var module in modules) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs index 8f25389b63..e594a24603 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs @@ -34,11 +34,18 @@ namespace Volo.Abp.Modularity { LogListOfModules(); - foreach (var Contributor in _lifecycleContributors) + foreach (var contributor in _lifecycleContributors) { foreach (var module in _moduleContainer.Modules) { - Contributor.Initialize(context, module.Instance); + try + { + contributor.Initialize(context, module.Instance); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during the initialize {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + } } } @@ -59,11 +66,18 @@ namespace Volo.Abp.Modularity { var modules = _moduleContainer.Modules.Reverse().ToList(); - foreach (var Contributor in _lifecycleContributors) + foreach (var contributor in _lifecycleContributors) { foreach (var module in modules) { - Contributor.Shutdown(context, module.Instance); + try + { + contributor.Shutdown(context, module.Instance); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during the shutdown {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + } } } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs index b388ef791d..192043a800 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs @@ -169,7 +169,7 @@ namespace Volo.Abp.Reflection { return Activator.CreateInstance(type); } - + return null; } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs index c1c8491dd6..2fa00e3caf 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs @@ -58,12 +58,14 @@ namespace Volo.Abp.EntityFrameworkCore.ValueConverters return dictionary; } - private static object GetNormalizedValue(Dictionary dictionary, ObjectExtensionPropertyInfo property) + private static object GetNormalizedValue( + Dictionary dictionary, + ObjectExtensionPropertyInfo property) { var value = dictionary.GetOrDefault(property.Name); if (value == null) { - return null; + return property.GetDefaultValue(); } try diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableString.cs b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs similarity index 100% rename from framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableString.cs rename to framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceNameAttribute.cs b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizationResourceNameAttribute.cs similarity index 100% rename from framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceNameAttribute.cs rename to framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizationResourceNameAttribute.cs diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/StringLocalizerHelper.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/StringLocalizerHelper.cs new file mode 100644 index 0000000000..b6daf4a44d --- /dev/null +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/StringLocalizerHelper.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.Localization; + +namespace Volo.Abp.Localization +{ + /// + /// This class is designed to be used internal by the framework. + /// + public static class AbpInternalLocalizationHelper + { + /// + /// Searches an array of keys in an array of localizers. + /// + /// + /// An array of localizers. Search the keys on the localizers. + /// Can contain null items in the array. + /// + /// + /// An array of keys. Search the keys on the localizers. + /// Should not contain null items in the array. + /// + /// + /// Return value if none of the localizers has none of the keys. + /// + /// + public static string LocalizeWithFallback( + IStringLocalizer[] localizers, + string[] keys, + string defaultValue) + { + foreach (var key in keys) + { + foreach (var localizer in localizers) + { + if (localizer == null) + { + continue; + } + + var localizedString = localizer[key]; + if (!localizedString.ResourceNotFound) + { + return localizedString.Value; + } + } + } + + return defaultValue; + } + } +} diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs new file mode 100644 index 0000000000..946fc04274 --- /dev/null +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs @@ -0,0 +1,32 @@ +using System; +using Volo.Abp.Localization; + +namespace Volo.Abp.ObjectExtending.Modularity +{ + public static class ExtensionPropertyConfigurationExtensions + { + public static string GetLocalizationResourceNameOrNull( + this ExtensionPropertyConfiguration property) + { + var resourceType = property.GetLocalizationResourceTypeOrNull(); + if (resourceType == null) + { + return null; + } + + return LocalizationResourceNameAttribute.GetName(resourceType); + } + + public static Type GetLocalizationResourceTypeOrNull( + this ExtensionPropertyConfiguration property) + { + if (property.DisplayName != null && + property.DisplayName is LocalizableString localizableString) + { + return localizableString.ResourceType; + } + + return null; + } + } +} diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/TypeHelper_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/TypeHelper_Tests.cs index 978bf9de54..b12a52e935 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/TypeHelper_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/TypeHelper_Tests.cs @@ -80,6 +80,7 @@ namespace Volo.Abp.Reflection TypeHelper.GetDefaultValue(typeof(byte)).ShouldBe(0); TypeHelper.GetDefaultValue(typeof(int)).ShouldBe(0); TypeHelper.GetDefaultValue(typeof(string)).ShouldBeNull(); + TypeHelper.GetDefaultValue(typeof(MyEnum)).ShouldBe(MyEnum.EnumValue0); } [Fact] @@ -94,5 +95,11 @@ namespace Volo.Abp.Reflection { } + + public enum MyEnum + { + EnumValue0, + EnumValue1, + } } } diff --git a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ObjectExtensionManager_Tests.cs b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ObjectExtensionManager_Tests.cs index 6386dc41ff..a751fdf20f 100644 --- a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ObjectExtensionManager_Tests.cs +++ b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ObjectExtensionManager_Tests.cs @@ -138,23 +138,27 @@ namespace Volo.Abp.ObjectExtending .AddOrUpdateProperty("StringPropWithCustomDefaultValue", property => { property.DefaultValue = "custom-value"; + }) + .AddOrUpdateProperty("EnumProp", property => + { + property.DefaultValue = MyTestEnum.EnumValue2; }); _objectExtensionManager .GetPropertyOrNull("IntProp") - .DefaultValue.ShouldBe(0); + .GetDefaultValue().ShouldBe(0); _objectExtensionManager .GetPropertyOrNull("IntPropWithCustomDefaultValue") - .DefaultValue.ShouldBe(42); + .GetDefaultValue().ShouldBe(42); _objectExtensionManager .GetPropertyOrNull("BoolProp") - .DefaultValue.ShouldBe(false); + .GetDefaultValue().ShouldBe(false); _objectExtensionManager .GetPropertyOrNull("NullableIntProp") - .DefaultValue.ShouldBeNull(); + .GetDefaultValue().ShouldBeNull(); var propWithDefaultValueFactory = _objectExtensionManager .GetPropertyOrNull("NullableIntPropWithCustomDefaultValueFactory"); @@ -162,11 +166,15 @@ namespace Volo.Abp.ObjectExtending _objectExtensionManager .GetPropertyOrNull("StringProp") - .DefaultValue.ShouldBeNull(); + .GetDefaultValue().ShouldBeNull(); _objectExtensionManager .GetPropertyOrNull("StringPropWithCustomDefaultValue") - .DefaultValue.ShouldBe("custom-value"); + .GetDefaultValue().ShouldBe("custom-value"); + + _objectExtensionManager + .GetPropertyOrNull("EnumProp") + .GetDefaultValue().ShouldBe(MyTestEnum.EnumValue2); } private class MyExtensibleObject : ExtensibleObject From 9071221dcf45a1f782826d9fbf6c464cca4c8068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 18 May 2020 01:20:38 +0300 Subject: [PATCH 14/58] Update TagHelperExtensions.cs --- .../TagHelpers/Extensions/TagHelperExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs index d925fd11e8..af961a2bf6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.TagHelpers; using System.Text.Encodings.Web; -using Volo.Abp.Threading; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions { From 4aa07db3d27f4b4bea6e291415f3e244af4e472f Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 18 May 2020 01:44:15 +0300 Subject: [PATCH 15/58] fix sentences... --- docs/en/Dapper.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/en/Dapper.md b/docs/en/Dapper.md index d8b17838eb..25bba118ec 100644 --- a/docs/en/Dapper.md +++ b/docs/en/Dapper.md @@ -1,22 +1,23 @@ # Dapper Integration -Because Dapper's idea is that the sql statement takes precedence, and mainly provides some extension methods for the `IDbConnection` interface. +Dapper is a light-weight and simple database provider. The major benefit of using Dapper is writing T-SQL queries. It provides some extension methods for `IDbConnection` interface. -Abp does not encapsulate too many functions for Dapper. Abp Dapper provides a `DapperRepository` base class based on Abp EntityFrameworkCore, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper. +ABP does not encapsulate many functions for Dapper. ABP Dapper library provides a `DapperRepository` base class based on ABP EntityFrameworkCore module, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper. -These two properties can work well with [Unit-Of-Work](Unit-Of-Work.md). +`IDbConnection` and `IDbTransaction` works well with the [ABP Unit-Of-Work](Unit-Of-Work.md). ## Installation -Please install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md). +Install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md). -`Volo.Abp.Dapper` is the main nuget package for the Dapper integration. Install it to your project (for a layered application, to your data/infrastructure layer): +`Volo.Abp.Dapper` is the main nuget package for the Dapper integration. +Install it to your project (for a layered application, to your data/infrastructure layer): ```shell Install-Package Volo.Abp.Dapper ``` -Then add `AbpDapperModule` module dependency (`DependsOn` attribute) to your [module](Module-Development-Basics.md): +Then add `AbpDapperModule` module dependency (with `DependsOn` attribute) to your [module](Module-Development-Basics.md): ````C# using Volo.Abp.Dapper; @@ -34,9 +35,10 @@ namespace MyCompany.MyProject ## Implement Dapper Repository -The following code implements the `Person` repository, which requires EF Core's `DbContext` (MyAppDbContext). You can inject `PersonDapperRepository` to call its methods. +The following code creates the `PersonRepository`, which requires EF Core's `DbContext` (MyAppDbContext). +You can inject `PersonDapperRepository` to your services for your database operations. -`DbConnection` and `DbTransaction` are from the `DapperRepository` base class. +`DbConnection` and `DbTransaction` comes from the `DapperRepository` base class. ```C# public class PersonDapperRepository : DapperRepository, ITransientDependency From cc9fd339f9517af66b7a101083bf468c8c526ac2 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 18 May 2020 01:45:11 +0300 Subject: [PATCH 16/58] Update Dapper.md --- docs/en/Dapper.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/Dapper.md b/docs/en/Dapper.md index 25bba118ec..12660f21aa 100644 --- a/docs/en/Dapper.md +++ b/docs/en/Dapper.md @@ -11,6 +11,7 @@ ABP does not encapsulate many functions for Dapper. ABP Dapper library provides Install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md). `Volo.Abp.Dapper` is the main nuget package for the Dapper integration. +You can find it on NuGet Gallery: https://www.nuget.org/packages/Volo.Abp.Dapper Install it to your project (for a layered application, to your data/infrastructure layer): ```shell From fbfdbb1c5311cb59de70ee460ef0aa3b11922cd6 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 18 May 2020 01:45:32 +0300 Subject: [PATCH 17/58] Update Dapper.md --- docs/en/Dapper.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/en/Dapper.md b/docs/en/Dapper.md index 12660f21aa..0cb1a645d7 100644 --- a/docs/en/Dapper.md +++ b/docs/en/Dapper.md @@ -11,7 +11,9 @@ ABP does not encapsulate many functions for Dapper. ABP Dapper library provides Install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md). `Volo.Abp.Dapper` is the main nuget package for the Dapper integration. + You can find it on NuGet Gallery: https://www.nuget.org/packages/Volo.Abp.Dapper + Install it to your project (for a layered application, to your data/infrastructure layer): ```shell From 1c713d42013414599f4496dc529d294058759f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 18 May 2020 01:46:36 +0300 Subject: [PATCH 18/58] Change the example. --- docs/en/Object-Extensions.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/Object-Extensions.md b/docs/en/Object-Extensions.md index 40a7ddd5ea..8c7cca4037 100644 --- a/docs/en/Object-Extensions.md +++ b/docs/en/Object-Extensions.md @@ -200,17 +200,17 @@ ObjectExtensionManager.Instance ````csharp ObjectExtensionManager.Instance - .AddOrUpdateProperty( - "MyIntProperty", + .AddOrUpdateProperty( + "MyDateTimeProperty", options => { - options.DefaultValueFactory = () => 42; + options.DefaultValueFactory = () => DateTime.Now; }); ```` `options.DefaultValueFactory` has a higher priority than the `options.DefaultValue` . -> Tip: Use `DefaultValueFactory` option only if the default value may change over the time. If it is a constant value, then use the `DefaultValue` option. +> Tip: Use `DefaultValueFactory` option only if the default value may change over the time (like `DateTime.Now` in this example). If it is a constant value, then use the `DefaultValue` option. #### CheckPairDefinitionOnMapping From 7d8be4f62c106c8399f3a6d54808a9f207aa27dd Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 18 May 2020 01:47:01 +0300 Subject: [PATCH 19/58] Update Dapper.md --- docs/en/Dapper.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/en/Dapper.md b/docs/en/Dapper.md index 0cb1a645d7..850847ebbb 100644 --- a/docs/en/Dapper.md +++ b/docs/en/Dapper.md @@ -2,9 +2,7 @@ Dapper is a light-weight and simple database provider. The major benefit of using Dapper is writing T-SQL queries. It provides some extension methods for `IDbConnection` interface. -ABP does not encapsulate many functions for Dapper. ABP Dapper library provides a `DapperRepository` base class based on ABP EntityFrameworkCore module, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper. - -`IDbConnection` and `IDbTransaction` works well with the [ABP Unit-Of-Work](Unit-Of-Work.md). +ABP does not encapsulate many functions for Dapper. ABP Dapper library provides a `DapperRepository` base class based on ABP EntityFrameworkCore module, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper. `IDbConnection` and `IDbTransaction` works well with the [ABP Unit-Of-Work](Unit-Of-Work.md). ## Installation From 27009ed76ebbfea0e080b526f56fdbadab33aab0 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 18 May 2020 01:48:02 +0300 Subject: [PATCH 20/58] Update Dapper.md --- docs/en/Dapper.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/en/Dapper.md b/docs/en/Dapper.md index 850847ebbb..daf8ceb13a 100644 --- a/docs/en/Dapper.md +++ b/docs/en/Dapper.md @@ -8,9 +8,7 @@ ABP does not encapsulate many functions for Dapper. ABP Dapper library provides Install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md). -`Volo.Abp.Dapper` is the main nuget package for the Dapper integration. - -You can find it on NuGet Gallery: https://www.nuget.org/packages/Volo.Abp.Dapper +`Volo.Abp.Dapper` is the library for the Dapper integration. You can find it on NuGet Gallery: https://www.nuget.org/packages/Volo.Abp.Dapper Install it to your project (for a layered application, to your data/infrastructure layer): From b36b948b698dfee2b192c2b30edda710cd40c04b Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 18 May 2020 01:48:18 +0300 Subject: [PATCH 21/58] Update Dapper.md --- docs/en/Dapper.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/en/Dapper.md b/docs/en/Dapper.md index daf8ceb13a..26fbf97e66 100644 --- a/docs/en/Dapper.md +++ b/docs/en/Dapper.md @@ -8,7 +8,9 @@ ABP does not encapsulate many functions for Dapper. ABP Dapper library provides Install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md). -`Volo.Abp.Dapper` is the library for the Dapper integration. You can find it on NuGet Gallery: https://www.nuget.org/packages/Volo.Abp.Dapper +`Volo.Abp.Dapper` is the library for the Dapper integration. + +You can find it on NuGet Gallery: https://www.nuget.org/packages/Volo.Abp.Dapper Install it to your project (for a layered application, to your data/infrastructure layer): From a4b79212dbe72c05f4ff260bc5c0bb4e04fef02b Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Mon, 18 May 2020 10:50:31 +0800 Subject: [PATCH 22/58] Add docs module RemoteServiceName. --- .../Volo/Docs/Admin/DocsAdminRemoteServiceConsts.cs | 7 +++++++ .../Volo/Docs/Admin/DocsHttpApiClientModule.cs | 2 +- .../Volo/Docs/DocsRemoteServiceConsts.cs | 7 +++++++ .../Volo/Docs/DocsHttpApiClientModule.cs | 2 +- 4 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminRemoteServiceConsts.cs create mode 100644 modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/DocsRemoteServiceConsts.cs diff --git a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminRemoteServiceConsts.cs b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminRemoteServiceConsts.cs new file mode 100644 index 0000000000..8f29237bb0 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminRemoteServiceConsts.cs @@ -0,0 +1,7 @@ +namespace Volo.Docs.Admin +{ + public static class DocsAdminRemoteServiceConsts + { + public const string RemoteServiceName = "AbpDocsAdmin"; + } +} diff --git a/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo/Docs/Admin/DocsHttpApiClientModule.cs b/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo/Docs/Admin/DocsHttpApiClientModule.cs index e6aa9c260d..284d5b4fbf 100644 --- a/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo/Docs/Admin/DocsHttpApiClientModule.cs +++ b/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo/Docs/Admin/DocsHttpApiClientModule.cs @@ -11,7 +11,7 @@ namespace Volo.Docs.Admin { public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddHttpClientProxies(typeof(DocsAdminApplicationContractsModule).Assembly); + context.Services.AddHttpClientProxies(typeof(DocsAdminApplicationContractsModule).Assembly, DocsAdminRemoteServiceConsts.RemoteServiceName); } } } diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/DocsRemoteServiceConsts.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/DocsRemoteServiceConsts.cs new file mode 100644 index 0000000000..5c84ad5c46 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/DocsRemoteServiceConsts.cs @@ -0,0 +1,7 @@ +namespace Volo.Docs +{ + public static class DocsRemoteServiceConsts + { + public const string RemoteServiceName = "AbpDocs"; + } +} diff --git a/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs b/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs index 5f0d86bc98..174b4570c2 100644 --- a/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs +++ b/modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs @@ -12,7 +12,7 @@ namespace Volo.Docs { public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddHttpClientProxies(typeof(DocsApplicationContractsModule).Assembly); + context.Services.AddHttpClientProxies(typeof(DocsApplicationContractsModule).Assembly, DocsRemoteServiceConsts.RemoteServiceName); } } } From 46d0d4eba34cd2170d981530bd46f2c85ea41e5d Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Mon, 18 May 2020 14:23:17 +0800 Subject: [PATCH 23/58] "L" function in the text template rendering system should support parametric text. Resolve #3977 --- .../Abp/TextTemplating/TemplateLocalizer.cs | 50 +++++++++++++++++++ .../Abp/TextTemplating/TemplateRenderer.cs | 9 +--- .../Abp/TextTemplating/Localization/en.json | 7 +-- .../Abp/TextTemplating/Localization/tr.json | 7 +-- .../SampleTemplates/ForgotPasswordEmail.tpl | 2 +- .../TextTemplating/TemplateRenderer_Tests.cs | 21 +++++++- 6 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateLocalizer.cs diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateLocalizer.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateLocalizer.cs new file mode 100644 index 0000000000..c3c67dd194 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateLocalizer.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Localization; +using Scriban; +using Scriban.Runtime; +using Scriban.Syntax; + +namespace Volo.Abp.TextTemplating +{ + public class TemplateLocalizer : IScriptCustomFunction + { + private readonly IStringLocalizer _localizer; + + public TemplateLocalizer(IStringLocalizer localizer) + { + _localizer = localizer; + } + + public object Invoke(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, + ScriptBlockStatement blockStatement) + { + return GetString(arguments); + } + + public ValueTask InvokeAsync(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, + ScriptBlockStatement blockStatement) + { + return new ValueTask(GetString(arguments)); + } + + private string GetString(ScriptArray arguments) + { + if (arguments.IsNullOrEmpty()) + { + return string.Empty; + } + + var name = arguments[0]; + if (name == null || name.ToString().IsNullOrWhiteSpace()) + { + return string.Empty; + } + + var args = arguments.Skip(1).Where(x => x != null && !x.ToString().IsNullOrWhiteSpace()).ToArray(); + return args.Any() ? _localizer[name.ToString(), args] : _localizer[name.ToString()]; + } + } +} diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs index 86eb684f52..c7d7b9d016 100644 --- a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs @@ -140,12 +140,7 @@ namespace Volo.Abp.TextTemplating var localizer = GetLocalizerOrNull(templateDefinition); if (localizer != null) { - scriptObject.Import( - "L", - new Func( - name => localizer[name] - ) - ); + scriptObject.SetValue("L", new TemplateLocalizer(localizer), true); } context.PushGlobal(scriptObject); @@ -163,4 +158,4 @@ namespace Volo.Abp.TextTemplating return _stringLocalizerFactory.CreateDefaultOrNull(); } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json index a0dff7e930..441661716c 100644 --- a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json @@ -1,6 +1,7 @@ { "culture": "en", "texts": { - "HelloText": "Hello" - } -} \ No newline at end of file + "HelloText": "Hello {0}", + "HowAreYou": "how are you?" + } +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json index d09f02cd8f..60e5da8fa0 100644 --- a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json @@ -1,6 +1,7 @@ { "culture": "tr", "texts": { - "HelloText": "Merhaba" - } -} \ No newline at end of file + "HelloText": "Merhaba {0}", + "HowAreYou": "nasılsın?" + } +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl index 8bcab86d0f..6e5fcbe6e1 100644 --- a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl @@ -1 +1 @@ -{{L "HelloText"}}. Please click to the following link to get an email to reset your password! \ No newline at end of file +{{L "HelloText" model.name}}, {{L "HowAreYou" }}. Please click to the following link to get an email to reset your password! \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs index 14b3df3486..fef287a2b1 100644 --- a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs @@ -81,13 +81,15 @@ namespace Volo.Abp.TextTemplating { (await _templateRenderer.RenderAsync( TestTemplates.ForgotPasswordEmail, + new ForgotPasswordEmailModel("John"), cultureName: "en" - )).ShouldBe("*BEGIN*Hello. Please click to the following link to get an email to reset your password!*END*"); + )).ShouldBe("*BEGIN*Hello John, how are you?. Please click to the following link to get an email to reset your password!*END*"); (await _templateRenderer.RenderAsync( TestTemplates.ForgotPasswordEmail, + model: new Dictionary() { { "name", "John" } }, cultureName: "tr" - )).ShouldBe("*BEGIN*Merhaba. Please click to the following link to get an email to reset your password!*END*"); + )).ShouldBe("*BEGIN*Merhaba John, nasılsın?. Please click to the following link to get an email to reset your password!*END*"); } private class WelcomeEmailModel @@ -104,5 +106,20 @@ namespace Volo.Abp.TextTemplating Name = name; } } + + private class ForgotPasswordEmailModel + { + public string Name { get; set; } + + public ForgotPasswordEmailModel() + { + + } + + public ForgotPasswordEmailModel(string name) + { + Name = name; + } + } } } From 31ccb5d05cf96ad3c952a816182b28222e17bc98 Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Mon, 18 May 2020 14:40:53 +0800 Subject: [PATCH 24/58] Update Text-Templating.md. --- docs/en/Text-Templating.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 18a360c5c8..c71787f598 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -204,13 +204,14 @@ Inline localization uses the [localization system](Localization.md) to localize Assuming you need to send an email to a user to reset her/his password. Here, the template content: ```` -{%{{{L "ResetMyPassword"}}}%} +{%{{{L "ResetMyPassword" model.name}}}%} ```` `L` function is used to localize the given key based on the current user culture. You need to define the `ResetMyPassword` key inside your localization file: ````json -"ResetMyPassword": "Click here to reset your password" +"ResetMyPasswordTitle": "Reset my password", +"ResetMyPassword": "Hi {0}, Click here to reset your password" ```` You also need to declare the localization resource to be used with this template, inside your template definition provider class: @@ -234,6 +235,7 @@ var result = await _templateRenderer.RenderAsync( "PasswordReset", //the template name new PasswordResetModel { + Name = "john", Link = "https://abp.io/example-link?userId=123&token=ABC" } ); @@ -242,7 +244,7 @@ var result = await _templateRenderer.RenderAsync( You will see the localized result: ````csharp -Click here to reset your password +Hi john, Click here to reset your password ```` > If you define the [default localization resource](Localization.md) for your application, then no need to declare the resource type for the template definition. From dade0f28170be9d5efe1a710e7c8a1979b641fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0smail=20=C3=87A=C4=9EDA=C5=9E?= Date: Mon, 18 May 2020 13:49:10 +0300 Subject: [PATCH 25/58] added en localizaiton file for Volo.Abp.Emailing.Tests project --- .../Volo/Abp/Emailing/Localization/en.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json new file mode 100644 index 0000000000..70d0b2b52e --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json @@ -0,0 +1,6 @@ +{ + "culture": "de", + "texts": { + "hello": "Hello" + } +} \ No newline at end of file From ce6dc02ade5140796d79a9833b610be99c856505 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 18 May 2020 16:29:57 +0300 Subject: [PATCH 26/58] bloging linkedin meta tags --- .../src/Volo.Blogging.Web/BloggingTwitterOptions.cs | 12 ------------ .../Pages/Blogs/Posts/Detail.cshtml | 6 ++++++ .../SocialMedia/BloggingTwitterOptions.cs | 7 +++++++ 3 files changed, 13 insertions(+), 12 deletions(-) delete mode 100644 modules/blogging/src/Volo.Blogging.Web/BloggingTwitterOptions.cs create mode 100644 modules/blogging/src/Volo.Blogging.Web/SocialMedia/BloggingTwitterOptions.cs diff --git a/modules/blogging/src/Volo.Blogging.Web/BloggingTwitterOptions.cs b/modules/blogging/src/Volo.Blogging.Web/BloggingTwitterOptions.cs deleted file mode 100644 index 8c1b1fbf7d..0000000000 --- a/modules/blogging/src/Volo.Blogging.Web/BloggingTwitterOptions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Volo.Blogging -{ - public class BloggingTwitterOptions - { - public string Site { get; set; } - } -} diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml index 63cfbf8a8e..db6427e2a7 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml @@ -8,6 +8,7 @@ @using Volo.Blogging.Pages.Blog.Posts @using Volo.Blogging.Areas.Blog.Helpers.TagHelpers @using Volo.Abp.AspNetCore.Mvc.UI.Packages.Prismjs +@using Volo.Blogging.SocialMedia @inject IAuthorizationService Authorization @inject IOptionsSnapshot twitterOptions @model DetailModel @@ -21,6 +22,11 @@ ViewBag.TwitterDescription = Model.Post.Description; ViewBag.TwitterImage = $"{Request.Scheme}://{Request.Host}{Request.PathBase}{Model.Post.CoverImage}"; + ViewBag.LinkedInUrl = Request.GetEncodedUrl(); + ViewBag.LinkedInTitle = Model.Post.Title; + ViewBag.LinkedInDescription = Model.Post.Description; + ViewBag.LinkedInImage = $"{Request.Scheme}://{Request.Host}{Request.PathBase}{Model.Post.CoverImage}"; + var hasCommentingPermission = CurrentUser.IsAuthenticated; //TODO: Apply real policy! } @section scripts { diff --git a/modules/blogging/src/Volo.Blogging.Web/SocialMedia/BloggingTwitterOptions.cs b/modules/blogging/src/Volo.Blogging.Web/SocialMedia/BloggingTwitterOptions.cs new file mode 100644 index 0000000000..5960be21b9 --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.Web/SocialMedia/BloggingTwitterOptions.cs @@ -0,0 +1,7 @@ +namespace Volo.Blogging.SocialMedia +{ + public class BloggingTwitterOptions + { + public string Site { get; set; } + } +} From eebaf449cec68af98f98418b44e0489919f41e3c Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Mon, 18 May 2020 22:19:18 +0800 Subject: [PATCH 27/58] Add console template --- .../Volo/Abp/Cli/Commands/NewCommand.cs | 3 +- .../ProjectBuilding/AbpIoSourceCodeStore.cs | 9 +- .../ProjectBuilding/TemplateInfoProvider.cs | 5 +- .../Templates/Console/ConsoleTemplate.cs | 16 ++ .../Templates/Console/ConsoleTemplateBase.cs | 13 + templates/console/.gitattributes | 1 + templates/console/.gitignore | 255 ++++++++++++++++++ .../console/MyCompanyName.MyProjectName.sln | 21 ++ templates/console/common.props | 12 + .../HelloWorldService.cs | 13 + ...ompanyName.MyProjectName.ConsoleApp.csproj | 22 ++ .../MyProjectNameConsoleAppHostedService.cs | 34 +++ .../MyProjectNameConsoleAppModule.cs | 14 + .../Program.cs | 51 ++++ 14 files changed, 463 insertions(+), 6 deletions(-) create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplate.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplateBase.cs create mode 100644 templates/console/.gitattributes create mode 100644 templates/console/.gitignore create mode 100644 templates/console/MyCompanyName.MyProjectName.sln create mode 100644 templates/console/common.props create mode 100644 templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/HelloWorldService.cs create mode 100644 templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyCompanyName.MyProjectName.ConsoleApp.csproj create mode 100644 templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyProjectNameConsoleAppHostedService.cs create mode 100644 templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyProjectNameConsoleAppModule.cs create mode 100644 templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/Program.cs diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index 928764faf1..fc63ef4430 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -266,6 +266,7 @@ namespace Volo.Abp.Cli.Commands protected virtual MobileApp GetMobilePreference(CommandLineArgs commandLineArgs) { var optionValue = commandLineArgs.Options.GetOrNull(Options.Mobile.Short, Options.Mobile.Long); + var template = commandLineArgs.Options.GetOrNull(Options.Template.Short, Options.Template.Long); switch (optionValue) { case "none": @@ -273,7 +274,7 @@ namespace Volo.Abp.Cli.Commands case "react-native": return MobileApp.ReactNative; default: - return MobileApp.ReactNative; + return "console" == template ? MobileApp.None : MobileApp.ReactNative; } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs index 66843bea4f..0992ced415 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs @@ -12,6 +12,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Volo.Abp.Cli.Http; using Volo.Abp.Cli.ProjectBuilding.Templates.App; +using Volo.Abp.Cli.ProjectBuilding.Templates.Console; using Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule; using Volo.Abp.DependencyInjection; using Volo.Abp.Http; @@ -63,12 +64,12 @@ namespace Volo.Abp.Cli.ProjectBuilding Logger.LogWarning("The remote service is currently unavailable, please specify the version."); Logger.LogWarning(string.Empty); Logger.LogWarning("Find the following template in your cache directory: "); - Logger.LogWarning("\t Template Name\tVersion"); + Logger.LogWarning("\tTemplate Name\tVersion"); var templateList = GetLocalTemplates(); foreach (var cacheFile in templateList) { - Logger.LogWarning($"\t {cacheFile.TemplateName}\t\t{cacheFile.Version}"); + Logger.LogWarning($"\t{cacheFile.TemplateName}\t\t{cacheFile.Version}"); } Logger.LogWarning(string.Empty); @@ -241,7 +242,7 @@ namespace Volo.Abp.Cli.ProjectBuilding stringBuilder.AppendLine(cacheFile); } - var matches = Regex.Matches(stringBuilder.ToString(), $"({AppTemplate.TemplateName}|{AppProTemplate.TemplateName}|{ModuleTemplate.TemplateName}|{ModuleProTemplate.TemplateName})-(.+).zip"); + var matches = Regex.Matches(stringBuilder.ToString(), $"({AppTemplate.TemplateName}|{AppProTemplate.TemplateName}|{ModuleTemplate.TemplateName}|{ModuleProTemplate.TemplateName}|{ConsoleTemplate.TemplateName})-(.+).zip"); foreach (Match match in matches) { templateList.Add((match.Groups[1].Value, match.Groups[2].Value)); @@ -278,4 +279,4 @@ namespace Volo.Abp.Cli.ProjectBuilding public string Version { get; set; } } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs index 5a287d11bf..d1c2e4e2ba 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs @@ -1,6 +1,7 @@ using System; using Volo.Abp.Cli.ProjectBuilding.Building; using Volo.Abp.Cli.ProjectBuilding.Templates.App; +using Volo.Abp.Cli.ProjectBuilding.Templates.Console; using Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule; using Volo.Abp.DependencyInjection; @@ -25,9 +26,11 @@ namespace Volo.Abp.Cli.ProjectBuilding return new ModuleTemplate(); case ModuleProTemplate.TemplateName: return new ModuleProTemplate(); + case ConsoleTemplate.TemplateName: + return new ConsoleTemplate(); default: throw new Exception("There is no template found with given name: " + name); } } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplate.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplate.cs new file mode 100644 index 0000000000..e2b49c00f7 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplate.cs @@ -0,0 +1,16 @@ +namespace Volo.Abp.Cli.ProjectBuilding.Templates.Console +{ + public class ConsoleTemplate : ConsoleTemplateBase + { + /// + /// "console". + /// + public const string TemplateName = "console"; + + public ConsoleTemplate() + : base(TemplateName) + { + DocumentUrl = CliConsts.DocsLink + "/en/abp/latest/Getting-Started-Console-Application"; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplateBase.cs new file mode 100644 index 0000000000..33e383e9f2 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplateBase.cs @@ -0,0 +1,13 @@ +using JetBrains.Annotations; +using Volo.Abp.Cli.ProjectBuilding.Building; + +namespace Volo.Abp.Cli.ProjectBuilding.Templates.Console +{ + public abstract class ConsoleTemplateBase : TemplateInfo + { + protected ConsoleTemplateBase([NotNull] string name) : + base(name) + { + } + } +} diff --git a/templates/console/.gitattributes b/templates/console/.gitattributes new file mode 100644 index 0000000000..c941e52669 --- /dev/null +++ b/templates/console/.gitattributes @@ -0,0 +1 @@ +**/wwwroot/libs/** linguist-vendored diff --git a/templates/console/.gitignore b/templates/console/.gitignore new file mode 100644 index 0000000000..95d81cb113 --- /dev/null +++ b/templates/console/.gitignore @@ -0,0 +1,255 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# MyProjectName +src/MyCompanyName.MyProjectName.ConsoleApp/Logs/logs.txt diff --git a/templates/console/MyCompanyName.MyProjectName.sln b/templates/console/MyCompanyName.MyProjectName.sln new file mode 100644 index 0000000000..1843fa99b2 --- /dev/null +++ b/templates/console/MyCompanyName.MyProjectName.sln @@ -0,0 +1,21 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyCompanyName.MyProjectName.ConsoleApp", "src\MyCompanyName.MyProjectName.ConsoleApp\MyCompanyName.MyProjectName.ConsoleApp.csproj", "{00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E8067AED-2B6E-4134-AAF8-9101457D709A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2} = {E8067AED-2B6E-4134-AAF8-9101457D709A} + EndGlobalSection +EndGlobal diff --git a/templates/console/common.props b/templates/console/common.props new file mode 100644 index 0000000000..4adb69f0fe --- /dev/null +++ b/templates/console/common.props @@ -0,0 +1,12 @@ + + + latest + 0.1.0 + $(NoWarn);CS1591 + + + + + + + \ No newline at end of file diff --git a/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/HelloWorldService.cs b/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/HelloWorldService.cs new file mode 100644 index 0000000000..d72a1512d5 --- /dev/null +++ b/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/HelloWorldService.cs @@ -0,0 +1,13 @@ +using System; +using Volo.Abp.DependencyInjection; + +namespace MyCompanyName.MyProjectName.ConsoleApp +{ + public class HelloWorldService : ITransientDependency + { + public void SayHello() + { + Console.WriteLine("Hello World!"); + } + } +} diff --git a/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyCompanyName.MyProjectName.ConsoleApp.csproj b/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyCompanyName.MyProjectName.ConsoleApp.csproj new file mode 100644 index 0000000000..96e4f7c547 --- /dev/null +++ b/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyCompanyName.MyProjectName.ConsoleApp.csproj @@ -0,0 +1,22 @@ + + + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + + + diff --git a/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyProjectNameConsoleAppHostedService.cs b/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyProjectNameConsoleAppHostedService.cs new file mode 100644 index 0000000000..15c46c91be --- /dev/null +++ b/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyProjectNameConsoleAppHostedService.cs @@ -0,0 +1,34 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; +using Volo.Abp; + +namespace MyCompanyName.MyProjectName.ConsoleApp +{ + public class MyProjectNameConsoleAppHostedService : IHostedService + { + public Task StartAsync(CancellationToken cancellationToken) + { + using (var application = AbpApplicationFactory.Create(options => + { + options.UseAutofac(); //Autofac integration + options.Services.AddLogging(c => c.AddSerilog()); + })) + { + application.Initialize(); + + //Resolve a service and use it + var helloWorldService = application.ServiceProvider.GetService(); + helloWorldService.SayHello(); + + application.Shutdown(); + } + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + } +} diff --git a/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyProjectNameConsoleAppModule.cs b/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyProjectNameConsoleAppModule.cs new file mode 100644 index 0000000000..2e68bd8de3 --- /dev/null +++ b/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/MyProjectNameConsoleAppModule.cs @@ -0,0 +1,14 @@ +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace MyCompanyName.MyProjectName.ConsoleApp +{ + + [DependsOn( + typeof(AbpAutofacModule) + )] + public class MyProjectNameConsoleAppModule : AbpModule + { + + } +} diff --git a/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/Program.cs b/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/Program.cs new file mode 100644 index 0000000000..c20255fb0c --- /dev/null +++ b/templates/console/src/MyCompanyName.MyProjectName.ConsoleApp/Program.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; +using Serilog.Events; + +namespace MyCompanyName.MyProjectName.ConsoleApp +{ + public class Program + { + public static async Task Main(string[] args) + { + Log.Logger = new LoggerConfiguration() +#if DEBUG + .MinimumLevel.Debug() +#else + .MinimumLevel.Information() +#endif + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.Async(c => c.File("Logs/logs.txt")) + .WriteTo.Console() + .CreateLogger(); + + try + { + Log.Information("Starting console host."); + await CreateHostBuilder(args).RunConsoleAsync(); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly!"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + + } + + internal static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureServices((hostContext, services) => + { + services.AddHostedService(); + }); + } +} From 33bd7ffdca93a4cda70a099d2f556943f2d267b1 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Mon, 18 May 2020 22:33:43 +0800 Subject: [PATCH 28/58] Improve code --- .../src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index fc63ef4430..5605f357bf 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Cli.Args; using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.Cli.ProjectBuilding.Templates.Console; using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; @@ -274,7 +275,7 @@ namespace Volo.Abp.Cli.Commands case "react-native": return MobileApp.ReactNative; default: - return "console" == template ? MobileApp.None : MobileApp.ReactNative; + return ConsoleTemplate.TemplateName == template ? MobileApp.None : MobileApp.ReactNative; } } From 832cd5d47754bdba36a7188026ce9254773755ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ak=C4=B1n=20Sabri=20=C3=87am?= Date: Mon, 18 May 2020 18:22:33 +0300 Subject: [PATCH 29/58] added localization keys fororganizations in abpio admin --- .../AbpIoLocalization/Admin/Localization/Resources/en.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index 14a4eb345c..c172c1b302 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -147,6 +147,8 @@ "EmailSent": "Email Sent", "SuccessfullySent": "Successfully Sent", "SuccessfullyDeleted": "Successfully Deleted", - "DiscountRequestDeletionWarningMessage": "Discount request will be deleted" + "DiscountRequestDeletionWarningMessage": "Discount request will be deleted", + "TotalQuestionMustBeGreaterWarningMessage": "TotalQuestionCount must be greater than RemainingQuestionCount !", + "QuestionCountsMustBeGreaterThanZero": "TotalQuestionCount and RemainingQuestionCount must be zero or greater than zero !" } } \ No newline at end of file From 91af69ffec3b267c6d5a68529672340d7788ec95 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Mon, 18 May 2020 21:03:42 +0300 Subject: [PATCH 30/58] feat: add localization methods with fallbacks --- .../src/lib/services/localization.service.ts | 52 +++--- .../lib/tests/localization.service.spec.ts | 172 +++++++++++++++++- .../core/src/lib/utils/localization-utils.ts | 34 ++++ 3 files changed, 233 insertions(+), 25 deletions(-) create mode 100644 npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts diff --git a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts index 8adef08f46..5fea751b14 100644 --- a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts @@ -2,11 +2,12 @@ import { Injectable, NgZone, Optional, SkipSelf } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; import { noop, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; import { SetLanguage } from '../actions/session.actions'; -import { ApplicationConfiguration } from '../models/application-configuration'; import { Config } from '../models/config'; import { ConfigState } from '../states/config.state'; import { registerLocale } from '../utils/initial-utils'; +import { localize, localizeWithFallback } from '../utils/localization-utils'; type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean; @@ -77,30 +78,35 @@ export class LocalizationService { return this.store.selectSnapshot(ConfigState.getLocalization(key, ...interpolateParams)); } - isLocalized(key, sourceName) { - if (sourceName === '_') { - // A convention to suppress the localization - return true; - } - - const localization = this.store.selectSnapshot( - ConfigState.getOne('localization'), - ) as ApplicationConfiguration.Localization; - sourceName = sourceName || localization.defaultResourceName; - if (!sourceName) { - return false; - } + localize(resourceName: string, key: string, defaultValue: string): Observable { + return this.store + .select(ConfigState.getOne('localization')) + .pipe(map(localize(resourceName, key, defaultValue))); + } - const source = localization.values[sourceName]; - if (!source) { - return false; - } + localizeSync(resourceName: string, key: string, defaultValue: string): string { + return localize( + resourceName, + key, + defaultValue, + )(this.store.selectSnapshot(ConfigState.getOne('localization'))); + } - const value = source[key]; - if (value === undefined) { - return false; - } + localizeWithFallback( + resourceNames: string[], + keys: string[], + defaultValue: string, + ): Observable { + return this.store + .select(ConfigState.getOne('localization')) + .pipe(map(localizeWithFallback(resourceNames, keys, defaultValue))); + } - return true; + localizeWithFallbackSync(resourceNames: string[], keys: string[], defaultValue: string): string { + return localizeWithFallback( + resourceNames, + keys, + defaultValue, + )(this.store.selectSnapshot(ConfigState.getOne('localization'))); } } diff --git a/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts index 8c5b859f9f..e17d53e463 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts @@ -1,7 +1,7 @@ import { Router } from '@angular/router'; import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest'; -import { Store, Actions } from '@ngxs/store'; -import { Observable, of, Subject } from 'rxjs'; +import { Actions, Store } from '@ngxs/store'; +import { of, Subject } from 'rxjs'; import { LocalizationService } from '../services/localization.service'; describe('LocalizationService', () => { @@ -75,4 +75,172 @@ describe('LocalizationService', () => { } }); }); + + describe('#localize', () => { + test.each` + resource | key | defaultValue | expected + ${'foo'} | ${'bar'} | ${'DEFAULT'} | ${'baz'} + ${'x'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} + ${'a'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} + ${''} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} + ${undefined} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} + ${'foo'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} + ${'x'} | ${'y'} | ${'DEFAULT'} | ${'z'} + ${'a'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} + ${''} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} + ${undefined} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} + ${'foo'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} + ${'x'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} + ${'a'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} + ${''} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} + ${undefined} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} + ${'foo'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} + ${'x'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} + ${'a'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} + ${''} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} + ${undefined} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} + `( + 'should return observable $expected when resource name is $resource and key is $key', + async ({ resource, key, defaultValue, expected }) => { + store.select.andReturn( + of({ + values: { foo: { bar: 'baz' }, x: { y: 'z' } }, + defaultResourceName: 'x', + }), + ); + + const result = await service.localize(resource, key, defaultValue).toPromise(); + + expect(result).toBe(expected); + }, + ); + }); + + describe('#localizeSync', () => { + test.each` + resource | key | defaultValue | expected + ${'foo'} | ${'bar'} | ${'DEFAULT'} | ${'baz'} + ${'x'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} + ${'a'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} + ${''} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} + ${undefined} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} + ${'foo'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} + ${'x'} | ${'y'} | ${'DEFAULT'} | ${'z'} + ${'a'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} + ${''} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} + ${undefined} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} + ${'foo'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} + ${'x'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} + ${'a'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} + ${''} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} + ${undefined} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} + ${'foo'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} + ${'x'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} + ${'a'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} + ${''} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} + ${undefined} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} + `( + 'should return $expected when resource name is $resource and key is $key', + ({ resource, key, defaultValue, expected }) => { + store.selectSnapshot.andReturn({ + values: { foo: { bar: 'baz' }, x: { y: 'z' } }, + defaultResourceName: 'x', + }); + + const result = service.localizeSync(resource, key, defaultValue); + + expect(result).toBe(expected); + }, + ); + }); + + describe('#localizeWithFallback', () => { + test.each` + resources | keys | defaultValue | expected + ${['foo']} | ${['bar']} | ${'DEFAULT'} | ${'baz'} + ${['x']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['a', 'b', 'c']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} + ${[]} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['foo']} | ${['y']} | ${'DEFAULT'} | ${'z'} + ${['x']} | ${['y']} | ${'DEFAULT'} | ${'z'} + ${['a', 'b', 'c']} | ${['y']} | ${'DEFAULT'} | ${'z'} + ${['']} | ${['y']} | ${'DEFAULT'} | ${'z'} + ${[]} | ${['y']} | ${'DEFAULT'} | ${'z'} + ${['foo']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'baz'} + ${['x']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} + ${['a', 'b', 'c']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} + ${['']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} + ${[]} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} + ${['foo']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['x']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['a', 'b', 'c']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} + ${[]} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['foo']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} + ${['x']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} + ${['a', 'b', 'c']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} + ${['']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} + ${[]} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} + `( + 'should return observable $expected when resource names are $resources and keys are $keys', + async ({ resources, keys, defaultValue, expected }) => { + store.select.andReturn( + of({ + values: { foo: { bar: 'baz' }, x: { y: 'z' } }, + defaultResourceName: 'x', + }), + ); + + const result = await service + .localizeWithFallback(resources, keys, defaultValue) + .toPromise(); + + expect(result).toBe(expected); + }, + ); + }); + + describe('#localizeWithFallbackSync', () => { + test.each` + resources | keys | defaultValue | expected + ${['foo']} | ${['bar']} | ${'DEFAULT'} | ${'baz'} + ${['x']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['a', 'b', 'c']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} + ${[]} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['foo']} | ${['y']} | ${'DEFAULT'} | ${'z'} + ${['x']} | ${['y']} | ${'DEFAULT'} | ${'z'} + ${['a', 'b', 'c']} | ${['y']} | ${'DEFAULT'} | ${'z'} + ${['']} | ${['y']} | ${'DEFAULT'} | ${'z'} + ${[]} | ${['y']} | ${'DEFAULT'} | ${'z'} + ${['foo']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'baz'} + ${['x']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} + ${['a', 'b', 'c']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} + ${['']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} + ${[]} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} + ${['foo']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['x']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['a', 'b', 'c']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} + ${[]} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} + ${['foo']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} + ${['x']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} + ${['a', 'b', 'c']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} + ${['']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} + ${[]} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} + `( + 'should return $expected when resource names are $resources and keys are $keys', + ({ resources, keys, defaultValue, expected }) => { + store.selectSnapshot.andReturn({ + values: { foo: { bar: 'baz' }, x: { y: 'z' } }, + defaultResourceName: 'x', + }); + + const result = service.localizeWithFallbackSync(resources, keys, defaultValue); + + expect(result).toBe(expected); + }, + ); + }); }); diff --git a/npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts new file mode 100644 index 0000000000..7d5a2c9e70 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts @@ -0,0 +1,34 @@ +import { ApplicationConfiguration } from '../models/application-configuration'; + +export function localize(resourceName: string, key: string, defaultValue: string) { + return function(localization: ApplicationConfiguration.Localization) { + if (resourceName === '_') return key; + + const resource = localization.values[resourceName]; + + if (!resource) return defaultValue; + + return resource[key] || defaultValue; + }; +} + +export function localizeWithFallback( + resourceNames: string[], + keys: string[], + defaultValue: string, +) { + return function(localization: ApplicationConfiguration.Localization) { + resourceNames = resourceNames.concat(localization.defaultResourceName).filter(Boolean); + + for (let i = 0; i < resourceNames.length; i++) { + const resourceName = resourceNames[i]; + + for (let j = 0; j < keys.length; j++) { + const localized = localize(resourceName, keys[j], null)(localization); + if (localized) return localized; + } + } + + return defaultValue; + }; +} From 3ebbb709f6ad1d9777457b6e794378ba88bbb74d Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Mon, 18 May 2020 21:08:12 +0300 Subject: [PATCH 31/58] fix: avoid lint errors --- .../packages/core/src/lib/utils/localization-utils.ts | 2 ++ .../src/lib/components/loader-bar/loader-bar.component.ts | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts index 7d5a2c9e70..530adc0621 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts @@ -1,6 +1,7 @@ import { ApplicationConfiguration } from '../models/application-configuration'; export function localize(resourceName: string, key: string, defaultValue: string) { + /* tslint:disable-next-line:only-arrow-functions */ return function(localization: ApplicationConfiguration.Localization) { if (resourceName === '_') return key; @@ -17,6 +18,7 @@ export function localizeWithFallback( keys: string[], defaultValue: string, ) { + /* tslint:disable-next-line:only-arrow-functions */ return function(localization: ApplicationConfiguration.Localization) { resourceNames = resourceNames.concat(localization.defaultResourceName).filter(Boolean); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts index 41a5850131..512d959c5f 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts @@ -30,10 +30,6 @@ export class LoaderBarComponent implements OnDestroy, OnInit { @Input() color = '#77b6ff'; - @Input() - filter = (action: StartLoader | StopLoader) => - action.payload.url.indexOf('openid-configuration') < 0; - @Input() isLoading = false; @@ -47,6 +43,10 @@ export class LoaderBarComponent implements OnDestroy, OnInit { stopDelay = 800; + @Input() + filter = (action: StartLoader | StopLoader) => + action.payload.url.indexOf('openid-configuration') < 0; + private readonly clearProgress = () => { this.progressLevel = 0; this.cdRef.detectChanges(); From 20bfd29a5c6250a6e095bd776a6959001e97b59d Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Mon, 18 May 2020 23:09:09 +0300 Subject: [PATCH 32/58] refactor: set array lengths before loops --- .../packages/core/src/lib/utils/localization-utils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts index 530adc0621..2a14b8d014 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/localization-utils.ts @@ -22,10 +22,13 @@ export function localizeWithFallback( return function(localization: ApplicationConfiguration.Localization) { resourceNames = resourceNames.concat(localization.defaultResourceName).filter(Boolean); - for (let i = 0; i < resourceNames.length; i++) { + const resourceCount = resourceNames.length; + const keyCount = keys.length; + + for (let i = 0; i < resourceCount; i++) { const resourceName = resourceNames[i]; - for (let j = 0; j < keys.length; j++) { + for (let j = 0; j < keyCount; j++) { const localized = localize(resourceName, keys[j], null)(localization); if (localized) return localized; } From 465c751ea9f5e4aa6235364785bea04057b7f863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 19 May 2020 00:16:52 +0300 Subject: [PATCH 33/58] Fixed #3999: Fix BootstrapDatepicker Script/Style Contributor namespace --- .../BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs | 2 +- .../BootstrapDatepicker/BootstrapDatepickerStyleContributor.cs | 2 +- .../Bundling/SharedThemeGlobalScriptContributor.cs | 1 + .../Bundling/SharedThemeGlobalStyleContributor.cs | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs index 7e21c8cef5..5a32cb7ca0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs @@ -4,7 +4,7 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery; using Volo.Abp.Modularity; -namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Timeago +namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker { [DependsOn(typeof(JQueryScriptContributor))] public class BootstrapDatepickerScriptContributor : BundleContributor diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerStyleContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerStyleContributor.cs index bc345e34b3..0b9b555e97 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerStyleContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerStyleContributor.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Volo.Abp.AspNetCore.Mvc.UI.Bundling; -namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Select2 +namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker { public class BootstrapDatepickerStyleContributor : BundleContributor { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs index 892f0d497a..4ff7430553 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs @@ -1,5 +1,6 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap; +using Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker; using Volo.Abp.AspNetCore.Mvc.UI.Packages.DatatablesNetBs4; using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery; using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQueryForm; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalStyleContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalStyleContributor.cs index d1703113d0..49b8c4893e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalStyleContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalStyleContributor.cs @@ -1,5 +1,6 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap; +using Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker; using Volo.Abp.AspNetCore.Mvc.UI.Packages.Core; using Volo.Abp.AspNetCore.Mvc.UI.Packages.DatatablesNetBs4; using Volo.Abp.AspNetCore.Mvc.UI.Packages.FontAwesome; From 4a56efdc48ca479de832d8c43617d3332c9de296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 19 May 2020 00:17:19 +0300 Subject: [PATCH 34/58] Show icon for boolean extension properties on datatables. --- .../ui-extensions.js | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js index dbf73618ad..40e22d3da9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js @@ -150,10 +150,9 @@ return defaultValue; } - function localizeEnumMember(property, row) { + function localizeEnumMember(property, enumMemberValue) { var enumType = property.config.type; var enumInfo = abp.objectExtensions.enums[enumType]; - var enumMemberValue = row.extraProperties[property.name]; var enumMemberName = getEnumMemberName(enumInfo, enumMemberValue); if (!enumMemberName) { @@ -199,6 +198,10 @@ return tableProperties; } + function getValueFromRow(property, row) { + return row.extraProperties[property.name];; + } + function convertPropertyToColumnConfig(property) { var columnConfig = { title: localizeDisplayName(property.name, property.config.displayName), @@ -208,10 +211,18 @@ if (property.config.typeSimple === 'enum') { columnConfig.render = function (data, type, row) { - return localizeEnumMember( - property, - row - ); + var value = getValueFromRow(property, row); + return localizeEnumMember(property, value); + } + } + else if (property.config.typeSimple === 'boolean') { + columnConfig.render = function (data, type, row) { + var value = getValueFromRow(property, row); + if (value) { + return ''; + } else { + return ''; + } } } From 7abcc0fdf8e9f285881c0acb6e1147d48376d9bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 19 May 2020 00:55:33 +0300 Subject: [PATCH 35/58] Add Format (asp-format), Name and Value properties to AbpInputTagHelper --- .../TagHelpers/Form/AbpInputTagHelper.cs | 7 +++++++ .../TagHelpers/Form/AbpInputTagHelperService.cs | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs index f264d21cd5..16a700f4bc 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs @@ -33,6 +33,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form [ViewContext] public ViewContext ViewContext { get; set; } + [HtmlAttributeName("asp-format")] + public string Format { get; set; } + + public string Name { get; set; } + + public string Value { get; set; } + public AbpInputTagHelper(AbpInputTagHelperService tagHelperService) : base(tagHelperService) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index a2bb92a14d..b03b0c8b69 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -110,7 +110,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return new TextAreaTagHelper(_generator) { For = TagHelper.AspFor, - ViewContext = TagHelper.ViewContext + ViewContext = TagHelper.ViewContext, + Name = TagHelper.Name }; } @@ -118,7 +119,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { For = TagHelper.AspFor, InputTypeName = TagHelper.InputTypeName, - ViewContext = TagHelper.ViewContext + ViewContext = TagHelper.ViewContext, + Format = TagHelper.Format, + Name = TagHelper.Name, + Value = TagHelper.Value }; } From 67e68297e2b31fca8147997dc4eec0ff32567e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 19 May 2020 00:57:52 +0300 Subject: [PATCH 36/58] Create GetInputFormatOrNull extension method for ObjectExtensionPropertyInfo --- ...UiObjectExtensionPropertyInfoExtensions.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs new file mode 100644 index 0000000000..bac7787028 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs @@ -0,0 +1,44 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; + +namespace Volo.Abp.ObjectExtending +{ + public static class MvcUiObjectExtensionPropertyInfoExtensions + { + private static readonly Type[] DateTypes = new[] + { + typeof(DateTime), typeof(DateTimeOffset) + }; + + public static string GetInputFormatOrNull(this ObjectExtensionPropertyInfo property) + { + if (IsDate(property)) + { + return "{0:yyyy-MM-dd}"; + } + + return null; + } + + private static bool IsDate(ObjectExtensionPropertyInfo property) + { + if (!DateTypes.Contains(property.Type)) + { + return false; + } + + var dataTypeAttribute = property + .Attributes + .OfType() + .FirstOrDefault(); + + if (dataTypeAttribute == null) + { + return false; + } + + return dataTypeAttribute.DataType == DataType.Date; + } + } +} From 17891a959ad50e576a37214a153c71d12159ae00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 19 May 2020 01:41:14 +0300 Subject: [PATCH 37/58] Add Default Renderers for datatables. --- .../datatables/datatables-extensions.js | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js index cd31e73bc4..40aebbc71f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js @@ -8,7 +8,7 @@ }; /************************************************************************ - * RECORD-ACTIONS extension for datatables * + * RECORD-ACTIONS extension for datatables * *************************************************************************/ (function () { if (!$.fn.dataTableExt) { @@ -328,4 +328,32 @@ })(); + /************************************************************************ + * Default Renderers * + *************************************************************************/ + + datatables.defaultRenderers = datatables.defaultRenderers || {}; + + datatables.defaultRenderers['boolean'] = function(value) { + if (value) { + return ''; + } else { + return ''; + } + }; + + datatables.defaultRenderers['date'] = function (value) { + return luxon + .DateTime + .fromISO(value, { locale: abp.localization.currentCulture.name }) + .toLocaleString(); + }; + + datatables.defaultRenderers['datetime'] = function (value) { + return luxon + .DateTime + .fromISO(value, { locale: abp.localization.currentCulture.name }) + .toLocaleString(luxon.DateTime.DATETIME_SHORT); + }; + })(jQuery); \ No newline at end of file From 2d55cbf1942ae6d56e6b2c6ef53b2b5331d31dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 19 May 2020 01:41:18 +0300 Subject: [PATCH 38/58] Update LuxonScriptContributor.cs --- .../Mvc/UI/Packages/Luxon/LuxonScriptContributor.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Luxon/LuxonScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Luxon/LuxonScriptContributor.cs index e605f6e6ae..3d2a1dc774 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Luxon/LuxonScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Luxon/LuxonScriptContributor.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; using Volo.Abp.AspNetCore.Mvc.UI.Bundling; namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Luxon From f160b368a99590e5064b4e3c69800e82b9db92c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 19 May 2020 01:41:47 +0300 Subject: [PATCH 39/58] handle dates and datetimes on datatables MVC UI --- .../ui-extensions.js | 19 ++- ...UiObjectExtensionPropertyInfoExtensions.cs | 114 +++++++++++++++--- .../CachedObjectExtensionsDtoService.cs | 24 +++- .../ObjectExtendingPropertyInfoExtensions.cs | 113 +++-------------- .../IBasicObjectExtensionPropertyInfo.cs | 38 ++++++ .../ExtensionPropertyConfiguration.cs | 2 +- .../ModuleExtensionConfigurationHelper.cs | 1 + .../ObjectExtensionPropertyInfo.cs | 2 +- 8 files changed, 185 insertions(+), 128 deletions(-) create mode 100644 framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/IBasicObjectExtensionPropertyInfo.cs diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js index 40e22d3da9..eacfecc867 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js @@ -96,7 +96,7 @@ get: _get }; })(); - + function initializeObjectExtensions() { var getShortEnumTypeName = function (enumType) { @@ -209,19 +209,18 @@ orderable: false }; + if (property.config.typeSimple === 'enum') { - columnConfig.render = function (data, type, row) { + columnConfig.render = function(data, type, row) { var value = getValueFromRow(property, row); return localizeEnumMember(property, value); } - } - else if (property.config.typeSimple === 'boolean') { - columnConfig.render = function (data, type, row) { - var value = getValueFromRow(property, row); - if (value) { - return ''; - } else { - return ''; + } else { + var defaultRenderer = abp.libs.datatables.defaultRenderers[property.config.typeSimple]; + if (defaultRenderer) { + columnConfig.render = function (data, type, row) { + var value = getValueFromRow(property, row); + return defaultRenderer(value); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs index bac7787028..608374e9e8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs @@ -1,19 +1,40 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Linq; +using Microsoft.AspNetCore.Mvc; namespace Volo.Abp.ObjectExtending { public static class MvcUiObjectExtensionPropertyInfoExtensions { - private static readonly Type[] DateTypes = new[] - { - typeof(DateTime), typeof(DateTimeOffset) + private static readonly HashSet NumberTypes = new HashSet { + typeof(int), + typeof(long), + typeof(byte), + typeof(sbyte), + typeof(short), + typeof(ushort), + typeof(uint), + typeof(long), + typeof(ulong), + typeof(float), + typeof(double), + typeof(int?), + typeof(long?), + typeof(byte?), + typeof(sbyte?), + typeof(short?), + typeof(ushort?), + typeof(uint?), + typeof(long?), + typeof(ulong?), + typeof(float?), + typeof(double?), }; - public static string GetInputFormatOrNull(this ObjectExtensionPropertyInfo property) + public static string GetInputFormatOrNull(this IBasicObjectExtensionPropertyInfo property) { - if (IsDate(property)) + if (property.IsDate()) { return "{0:yyyy-MM-dd}"; } @@ -21,24 +42,85 @@ namespace Volo.Abp.ObjectExtending return null; } - private static bool IsDate(ObjectExtensionPropertyInfo property) + public static string GetInputType(this ObjectExtensionPropertyInfo propertyInfo) + { + foreach (var attribute in propertyInfo.Attributes) + { + var inputTypeByAttribute = GetInputTypeFromAttributeOrNull(attribute); + if (inputTypeByAttribute != null) + { + return inputTypeByAttribute; + } + } + + return GetInputTypeFromTypeOrNull(propertyInfo.Type) + ?? "text"; //default + } + + private static string GetInputTypeFromAttributeOrNull(Attribute attribute) { - if (!DateTypes.Contains(property.Type)) + if (attribute is EmailAddressAttribute) { - return false; + return "email"; } - var dataTypeAttribute = property - .Attributes - .OfType() - .FirstOrDefault(); + if (attribute is UrlAttribute) + { + return "url"; + } + + if (attribute is HiddenInputAttribute) + { + return "hidden"; + } + + if (attribute is PhoneAttribute) + { + return "tel"; + } - if (dataTypeAttribute == null) + if (attribute is DataTypeAttribute dataTypeAttribute) { - return false; + switch (dataTypeAttribute.DataType) + { + case DataType.Password: + return "password"; + case DataType.Date: + return "date"; + case DataType.Time: + return "time"; + case DataType.EmailAddress: + return "email"; + case DataType.Url: + return "url"; + case DataType.PhoneNumber: + return "tel"; + case DataType.DateTime: + return "datetime-local"; + } } - return dataTypeAttribute.DataType == DataType.Date; + return null; + } + + private static string GetInputTypeFromTypeOrNull(Type type) + { + if (type == typeof(bool)) + { + return "checkbox"; + } + + if (type == typeof(DateTime)) + { + return "datetime-local"; + } + + if (NumberTypes.Contains(type)) + { + return "number"; + } + + return null; } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs index 870ff0368e..76d73820b2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs @@ -107,9 +107,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending var extensionPropertyDto = new ExtensionPropertyDto { Type = TypeHelper.GetFullNameHandlingNullableAndGenerics(propertyConfig.Type), - TypeSimple = propertyConfig.Type.IsEnum - ? "enum" - : TypeHelper.GetSimplifiedName(propertyConfig.Type), + TypeSimple = GetSimpleTypeName(propertyConfig), Attributes = new List(), DisplayName = CreateDisplayNameDto(propertyConfig), Configuration = new Dictionary(), @@ -161,6 +159,26 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending return extensionPropertyDto; } + protected virtual string GetSimpleTypeName(ExtensionPropertyConfiguration propertyConfig) + { + if (propertyConfig.Type.IsEnum) + { + return "enum"; + } + + if (propertyConfig.IsDate()) + { + return "date"; + } + + if (propertyConfig.IsDateTime()) + { + return "datetime"; + } + + return TypeHelper.GetSimplifiedName(propertyConfig.Type); + } + protected virtual LocalizableStringDto CreateDisplayNameDto(ExtensionPropertyConfiguration propertyConfig) { if (propertyConfig.DisplayName == null) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/ObjectExtending/ObjectExtendingPropertyInfoExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/ObjectExtending/ObjectExtendingPropertyInfoExtensions.cs index 04339eedd3..dc8a28e89f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/ObjectExtending/ObjectExtendingPropertyInfoExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/ObjectExtending/ObjectExtendingPropertyInfoExtensions.cs @@ -1,116 +1,35 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using Microsoft.AspNetCore.Mvc; +using System.Linq; namespace Volo.Abp.ObjectExtending { public static class ObjectExtensionPropertyInfoAspNetCoreMvcExtensions { - private static readonly HashSet NumberTypes = new HashSet { - typeof(int), - typeof(long), - typeof(byte), - typeof(sbyte), - typeof(short), - typeof(ushort), - typeof(uint), - typeof(long), - typeof(ulong), - typeof(float), - typeof(double), - typeof(int?), - typeof(long?), - typeof(byte?), - typeof(sbyte?), - typeof(short?), - typeof(ushort?), - typeof(uint?), - typeof(long?), - typeof(ulong?), - typeof(float?), - typeof(double?), + private static readonly Type[] DateTimeTypes = + { + typeof(DateTime), + typeof(DateTimeOffset) }; - public static string GetInputType(this ObjectExtensionPropertyInfo propertyInfo) + public static bool IsDate(this IBasicObjectExtensionPropertyInfo property) { - foreach (var attribute in propertyInfo.Attributes) - { - var inputTypeByAttribute = GetInputTypeFromAttributeOrNull(attribute); - if (inputTypeByAttribute != null) - { - return inputTypeByAttribute; - } - } - - return GetInputTypeFromTypeOrNull(propertyInfo.Type) - ?? "text"; //default + return DateTimeTypes.Contains(property.Type) && + property.GetDataTypeOrNull() == DataType.Date; } - private static string GetInputTypeFromAttributeOrNull(Attribute attribute) + public static bool IsDateTime(this IBasicObjectExtensionPropertyInfo property) { - if (attribute is EmailAddressAttribute) - { - return "email"; - } - - if (attribute is UrlAttribute) - { - return "url"; - } - - if (attribute is HiddenInputAttribute) - { - return "hidden"; - } - - if (attribute is PhoneAttribute) - { - return "tel"; - } - - if (attribute is DataTypeAttribute dataTypeAttribute) - { - switch (dataTypeAttribute.DataType) - { - case DataType.Password: - return "password"; - case DataType.Date: - return "date"; - case DataType.Time: - return "time"; - case DataType.EmailAddress: - return "email"; - case DataType.Url: - return "url"; - case DataType.PhoneNumber: - return "tel"; - case DataType.DateTime: - return "datetime-local"; - } - } - - return null; + return DateTimeTypes.Contains(property.Type) && + !property.IsDate(); } - private static string GetInputTypeFromTypeOrNull(Type type) + public static DataType? GetDataTypeOrNull(this IBasicObjectExtensionPropertyInfo property) { - if (type == typeof(bool)) - { - return "checkbox"; - } - - if (type == typeof(DateTime)) - { - return "datetime-local"; - } - - if (NumberTypes.Contains(type)) - { - return "number"; - } - - return null; + return property + .Attributes + .OfType() + .FirstOrDefault()?.DataType; } } } diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/IBasicObjectExtensionPropertyInfo.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/IBasicObjectExtensionPropertyInfo.cs new file mode 100644 index 0000000000..de9bb35b01 --- /dev/null +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/IBasicObjectExtensionPropertyInfo.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using Volo.Abp.Localization; + +namespace Volo.Abp.ObjectExtending +{ + public interface IBasicObjectExtensionPropertyInfo + { + [NotNull] + public string Name { get; } + + [NotNull] + public Type Type { get; } + + [NotNull] + public List Attributes { get; } + + [NotNull] + public List> Validators { get; } + + [CanBeNull] + public ILocalizableString DisplayName { get; } + + /// + /// Uses as the default value if was not set. + /// + [CanBeNull] + public object DefaultValue { get; set; } + + /// + /// Used with the first priority to create the default value for the property. + /// Uses to the if this was not set. + /// + [CanBeNull] + public Func DefaultValueFactory { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs index 6d66e51337..0a19a82018 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs @@ -6,7 +6,7 @@ using Volo.Abp.Reflection; namespace Volo.Abp.ObjectExtending.Modularity { - public class ExtensionPropertyConfiguration : IHasNameWithLocalizableDisplayName + public class ExtensionPropertyConfiguration : IHasNameWithLocalizableDisplayName, IBasicObjectExtensionPropertyInfo { [NotNull] public EntityExtensionConfiguration EntityExtensionConfiguration { get; } diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs index cb5be17c1f..f4a5b149d6 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using JetBrains.Annotations; namespace Volo.Abp.ObjectExtending.Modularity diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs index 12b32ab241..ddd8fbbc3c 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs @@ -7,7 +7,7 @@ using Volo.Abp.Reflection; namespace Volo.Abp.ObjectExtending { - public class ObjectExtensionPropertyInfo : IHasNameWithLocalizableDisplayName + public class ObjectExtensionPropertyInfo : IHasNameWithLocalizableDisplayName, IBasicObjectExtensionPropertyInfo { [NotNull] public ObjectExtensionInfo ObjectExtension { get; } From 9ef9d142ef7171881463c81a788fda08fc56fcb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 19 May 2020 01:51:29 +0300 Subject: [PATCH 40/58] Handle formatting for DateTime inputs for extension properties. --- .../MvcUiObjectExtensionPropertyInfoExtensions.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs index 608374e9e8..72338a021d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs @@ -39,6 +39,11 @@ namespace Volo.Abp.ObjectExtending return "{0:yyyy-MM-dd}"; } + if (property.IsDateTime()) + { + return "{0:yyyy-MM-ddTHH:mm}"; + } + return null; } From 0df437d876b3758d10d7d4f3dc83887edd5c0126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 19 May 2020 02:12:17 +0300 Subject: [PATCH 41/58] Add decimal to NumberTypes for extension properties --- .../MvcUiObjectExtensionPropertyInfoExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs index 72338a021d..1ebe486068 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs @@ -19,6 +19,7 @@ namespace Volo.Abp.ObjectExtending typeof(ulong), typeof(float), typeof(double), + typeof(decimal), typeof(int?), typeof(long?), typeof(byte?), @@ -30,6 +31,7 @@ namespace Volo.Abp.ObjectExtending typeof(ulong?), typeof(float?), typeof(double?), + typeof(decimal?) }; public static string GetInputFormatOrNull(this IBasicObjectExtensionPropertyInfo property) From c17868e5acef0ce3135e22d6ce07c90e5c25face Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 19 May 2020 05:17:15 +0300 Subject: [PATCH 42/58] Handle floating numbers on extension properties. --- .../Extensions/TagHelperExtensions.cs | 20 +++++-- .../Form/AbpInputTagHelperService.cs | 40 +++++++++++-- ...UiObjectExtensionPropertyInfoExtensions.cs | 17 ++++++ ...PropertiesDictionaryModelBinderProvider.cs | 1 - .../AbpExtraPropertyModelBinder.cs | 6 +- .../Volo/Abp/Reflection/TypeHelper.cs | 56 +++++++++++++++++-- 6 files changed, 122 insertions(+), 18 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs index af961a2bf6..761b72ec99 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs @@ -7,14 +7,26 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions { public static class TagHelperExtensions { - public static async Task ProcessAndGetOutputAsync(this TagHelper tagHelper, TagHelperAttributeList attributeList, TagHelperContext context, string tagName = "div", TagMode tagMode = TagMode.SelfClosing) + public static async Task ProcessAndGetOutputAsync( + this TagHelper tagHelper, + TagHelperAttributeList attributeList, + TagHelperContext context, + string tagName = "div", + TagMode tagMode = TagMode.SelfClosing) { - var innerOutput = new TagHelperOutput(tagName, attributeList, (useCachedResult, encoder) => Task.Run(() => new DefaultTagHelperContent())) + var innerOutput = new TagHelperOutput( + tagName, + attributeList, + (useCachedResult, encoder) => Task.Run(() => new DefaultTagHelperContent())) { TagMode = tagMode }; - - var innerContext = new TagHelperContext(attributeList, context.Items, Guid.NewGuid().ToString()); + + var innerContext = new TagHelperContext( + attributeList, + context.Items, + Guid.NewGuid().ToString() + ); tagHelper.Init(context); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index b03b0c8b69..932c6d5864 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -115,22 +115,40 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form }; } - return new InputTagHelper(_generator) + var inputTagHelper = new InputTagHelper(_generator) { For = TagHelper.AspFor, InputTypeName = TagHelper.InputTypeName, - ViewContext = TagHelper.ViewContext, - Format = TagHelper.Format, - Name = TagHelper.Name, - Value = TagHelper.Value + ViewContext = TagHelper.ViewContext }; + + if (!TagHelper.Format.IsNullOrEmpty()) + { + inputTagHelper.Format = TagHelper.Format; + } + + if (!TagHelper.Name.IsNullOrEmpty()) + { + inputTagHelper.Name = TagHelper.Name; + } + + if (!TagHelper.Value.IsNullOrEmpty()) + { + inputTagHelper.Value = TagHelper.Value; + } + + return inputTagHelper; } protected virtual async Task<(TagHelperOutput, bool)> GetInputTagHelperOutputAsync(TagHelperContext context, TagHelperOutput output) { var tagHelper = GetInputTagHelper(context, output); - var inputTagHelperOutput = await tagHelper.ProcessAndGetOutputAsync(GetInputAttributes(context, output), context, "input"); + var inputTagHelperOutput = await tagHelper.ProcessAndGetOutputAsync( + GetInputAttributes(context, output), + context, + "input" + ); ConvertToTextAreaIfTextArea(inputTagHelperOutput); AddDisabledAttribute(inputTagHelperOutput); @@ -349,6 +367,16 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form attrList.Add("type", TagHelper.InputTypeName); } + if (!TagHelper.Name.IsNullOrEmpty() && !attrList.ContainsName("name")) + { + attrList.Add("name", TagHelper.Name); + } + + if (!TagHelper.Value.IsNullOrEmpty() && !attrList.ContainsName("value")) + { + attrList.Add("value", TagHelper.Value); + } + return attrList; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs index 1ebe486068..7aea3cf9f2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Reflection; namespace Volo.Abp.ObjectExtending { @@ -49,6 +50,22 @@ namespace Volo.Abp.ObjectExtending return null; } + public static string GetInputValueOrNull(this IBasicObjectExtensionPropertyInfo property, object value) + { + if (value == null) + { + return null; + } + + if (TypeHelper.IsFloatingType(property.Type)) + { + return value.ToString()?.Replace(',', '.'); + } + + /* Let the ASP.NET Core handle it! */ + return null; + } + public static string GetInputType(this ObjectExtensionPropertyInfo propertyInfo) { foreach (var attribute in propertyInfo.Attributes) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertiesDictionaryModelBinderProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertiesDictionaryModelBinderProvider.cs index 3ed1eda3a1..812ca30f06 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertiesDictionaryModelBinderProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertiesDictionaryModelBinderProvider.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs index c94ad08e74..d09da73a59 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.ModelBinding; using Volo.Abp.ObjectExtending; @@ -61,7 +60,10 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding return value; } - return TypeHelper.ConvertFromString(propertyInfo.Type, value); + return TypeHelper.ConvertFromString( + propertyInfo.Type, + value + ); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs index 192043a800..83182fc965 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs @@ -2,14 +2,23 @@ using System.Collections; using System.Collections.Generic; using System.ComponentModel; +using System.Globalization; using System.Linq; using System.Reflection; using JetBrains.Annotations; +using Volo.Abp.Localization; namespace Volo.Abp.Reflection { public static class TypeHelper { + private static readonly HashSet FloatingTypes = new HashSet + { + typeof(float), + typeof(double), + typeof(decimal) + }; + private static readonly HashSet NonNullablePrimitiveTypes = new HashSet { typeof(byte), @@ -40,7 +49,7 @@ namespace Volo.Abp.Reflection { return false; } - + var type = obj.GetType(); if (!type.GetTypeInfo().IsGenericType) { @@ -169,7 +178,7 @@ namespace Volo.Abp.Reflection { return Activator.CreateInstance(type); } - + return null; } @@ -297,9 +306,39 @@ namespace Volo.Abp.Reflection public static object ConvertFromString(Type targetType, string value) { - return TypeDescriptor - .GetConverter(targetType) - .ConvertFromString(value); + if (value == null) + { + return null; + } + + var converter = TypeDescriptor.GetConverter(targetType); + + if (IsFloatingType(targetType)) + { + using (CultureHelper.Use(CultureInfo.InvariantCulture)) + { + return converter.ConvertFromString(value.Replace(',', '.')); + } + } + + return converter.ConvertFromString(value); + } + + public static bool IsFloatingType(Type type, bool includeNullable = true) + { + if (FloatingTypes.Contains(type)) + { + return true; + } + + if (includeNullable && + IsNullable(type) && + FloatingTypes.Contains(type.GenericTypeArguments[0])) + { + return true; + } + + return false; } public static object ConvertFrom(object value) @@ -313,5 +352,12 @@ namespace Volo.Abp.Reflection .GetConverter(targetType) .ConvertFrom(value); } + + public static Type StripNullable(Type type) + { + return IsNullable(type) + ? type.GenericTypeArguments[0] + : type; + } } } From d3a42228b2f983e45d648a1f59bc7354edbd1814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 19 May 2020 05:26:45 +0300 Subject: [PATCH 43/58] Set name only if there is a valid value. --- .../Form/AbpInputTagHelperService.cs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index 932c6d5864..4ddf44d2d6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -91,7 +91,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form inputHtml + label : label + inputHtml; - return innerContent + infoHtml + validation; + return innerContent + infoHtml + validation; } protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml, bool isCheckbox) @@ -103,16 +103,20 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual TagHelper GetInputTagHelper(TagHelperContext context, TagHelperOutput output) { - var textAreaAttribute = TagHelper.AspFor.ModelExplorer.GetAttribute