diff --git a/docs/en/FluentValidation.md b/docs/en/FluentValidation.md index bb45fb96d8..c087f8a185 100644 --- a/docs/en/FluentValidation.md +++ b/docs/en/FluentValidation.md @@ -1,3 +1,58 @@ # FluentValidation Integration -TODO \ No newline at end of file +ABP [Validation](Validation.md) infrastructure is extensible. [Volo.Abp.FluentValidation](https://www.nuget.org/packages/Volo.Abp.FluentValidation) NuGet package extends the validation system to work with the [FluentValidation](https://fluentvalidation.net/) library. + +## Installation + +It is suggested to use the [ABP CLI](CLI.md) to install this package. + +### Using the ABP CLI + +Open a command line window in the folder of the project (.csproj file) and type the following command: + +````bash +abp add-package Volo.Abp.FluentValidation +```` + +### Manual Installation + +If you want to manually install; + +1. Add the [Volo.Abp.FluentValidation](https://www.nuget.org/packages/Volo.Abp.FluentValidation) NuGet package to your project: + + ```` + Install-Package Volo.Abp.FluentValidation + ```` + +2. Add the `AbpFluentValidationModule` to the dependency list of your module: + +````csharp +[DependsOn( + //...other dependencies + typeof(AbpFluentValidationModule) //Add the FluentValidation module + )] +public class YourModule : AbpModule +{ +} +```` + +## Using the FluentValidation + +Follow [the FluentValidation documentation](https://fluentvalidation.net/) to create validator classes. Example: + +````csharp +public class CreateUpdateBookDtoValidator : AbstractValidator +{ + public CreateUpdateBookDtoValidator() + { + RuleFor(x => x.Name).Length(3, 10); + RuleFor(x => x.Price).ExclusiveBetween(0.0f, 999.0f); + } +} +```` + +ABP will automatically find this class and associate with the `CreateUpdateBookDto` on object validation. + +## See Also + +* [Validation System](Validation.md) \ No newline at end of file diff --git a/docs/en/Modules/Blogging.md b/docs/en/Modules/Blogging.md new file mode 100644 index 0000000000..0d2ab35c21 --- /dev/null +++ b/docs/en/Modules/Blogging.md @@ -0,0 +1,3 @@ +# IdentityServer Module + +TODO \ No newline at end of file diff --git a/docs/en/Validation.md b/docs/en/Validation.md index cf00e9f205..15c3f13412 100644 --- a/docs/en/Validation.md +++ b/docs/en/Validation.md @@ -120,6 +120,39 @@ Once ABP determines a validation error, it throws an exception of type `AbpValid * Log level of the `AbpValidationException` is set to `Warning`. It logs all the validation errors to the [logging system](Logging.md). * `AbpValidationException` is automatically caught by the ABP framework and converted to a usable error into with HTTP 400 status code. See the [exception handling](Exception-Handling.md) document for more. +## Advanced Topics + +### IObjectValidator + +In addition to the automatic validation, you may want to manually validate an object. In this case, [inject](Dependency-Injection.md) and use the `IObjectValidator` service: + +* `Validate` method validates the given object based on the validation rules and throws an `AbpValidationException` if it is not in a valid state. +* `GetErrors` doesn't throw an exception, but only returns the validation errors. + +`IObjectValidator` is implemented by the `ObjectValidator` by default. `ObjectValidator` is extensible; you can implement `IObjectValidationContributor` interface to contribute a custom logic. Example: + +````csharp +public class MyObjectValidationContributor + : IObjectValidationContributor, ITransientDependency +{ + public void AddErrors(ObjectValidationContext context) + { + //Get the validating object + var obj = context.ValidatingObject; + + //Add the validation errors if available + context.Errors.Add(...); + } +} +```` + +* Remember to register your class to the [DI](Dependency-Injection.md) (implementing `ITransientDependency` does it just like in this example) +* ABP will automatically discover your class and use on any type of object validation (including automatic method call validation). + +### IMethodInvocationValidator + +`IMethodInvocationValidator` is used to validate a method call. It internally uses the `IObjectValidator` to validate objects passes to the method call. You normally don't need to this service since it is automatically used by the framework, but you may want to reuse or replace it on your application in rare cases. + ## FluentValidation Integration -Volo.Abp.FluentValidation package integrates the FluentValidation library to the validation system. See the [FluentValidation Integration document](FluentValidation.md) for more. \ No newline at end of file +Volo.Abp.FluentValidation package integrates the FluentValidation library to the validation system (by implementing the `IObjectValidationContributor`). See the [FluentValidation Integration document](FluentValidation.md) for more. \ No newline at end of file diff --git a/docs/zh-Hans/FluentValidation.md b/docs/zh-Hans/FluentValidation.md new file mode 100644 index 0000000000..2254e896d4 --- /dev/null +++ b/docs/zh-Hans/FluentValidation.md @@ -0,0 +1,3 @@ +# FluentValidation 集成 + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/Logging.md b/docs/zh-Hans/Logging.md new file mode 100644 index 0000000000..247609fb41 --- /dev/null +++ b/docs/zh-Hans/Logging.md @@ -0,0 +1,5 @@ +# 日志 + +ABP框架没有实现任何日志基础设施. 它使用[ASP.NET Core日志系统](https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/logging). + +> .NET Core 的日志系统是独立于ASP.NET Core的,它可以在任何类型的应用程序中使用. \ No newline at end of file diff --git a/docs/zh-Hans/Validation.md b/docs/zh-Hans/Validation.md index c2b128f0bc..8aa81351ec 100644 --- a/docs/zh-Hans/Validation.md +++ b/docs/zh-Hans/Validation.md @@ -1,3 +1,163 @@ -## Validation +# 验证 -待添加 +验证系统用于验证对于特定的控制器操作或服务的方法的用户输入或客户端请求. + +ABP与ASP.NET Core模型验证系统系统兼容,[模型验证文档](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation)中的内容对于基于ABP应用程序同样有效.所以本文主要集中在ABP特征,而不是重复微软文档. + +ABP增加了以下优点: + +* 定义 `IValidationEnabled` 向任意类添加自动验证. 所有的[应用服务](Application-Services.md)都实现了该接口,所以它们会被自动验证. +* 自动将数据注解属性的验证错误信息本地化. +* 提供可扩展的服务来验证方法调用或对象的状态。 +* 提供[FluentValidation](https://fluentvalidation.net/)的集成. + +## 验证DTO + +本节简要介绍了验证系统.有关详细信息请参阅[ASP.NET Core验证文档](https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/validation). + +### 数据注解 Attribute + +使用数据注解是一种以声明式对[DTO](Data-Transfer-Objects.md)进行验证的简单方法. +示例 : + +````csharp +public class CreateBookDto +{ + [Required] + [StringLength(100)] + public string Name { get; set; } + + [Required] + [StringLength(1000)] + public string Description { get; set; } + + [Range(0, 999.99)] + public decimal Price { get; set; } +} +```` + +当使用该类作为[应用服务](Application-Services.md)或控制器的参数时,将对其自动验证并抛出本地化异常(由ABP框架[处理](Exception-Handling.md)). + +### IValidatableObject + +`IValidatableObject` can be implemented by a DTO to perform custom validation logic. `CreateBookDto` in the following example implements this interface and checks if the `Name` is equals to the `Description` and returns a validation error in this case. +可以将DTO实现 `IValidatableObject` 接口进行自定义验证逻辑. 下面的示例中 `CreateBookDto` 实现了这个接口,并检查 `Name` 是否等于 `Description` 并返回一个验证错误. + +````csharp +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace Acme.BookStore +{ + public class CreateBookDto : IValidatableObject + { + [Required] + [StringLength(100)] + public string Name { get; set; } + + [Required] + [StringLength(1000)] + public string Description { get; set; } + + [Range(0, 999.99)] + public decimal Price { get; set; } + + public IEnumerable Validate( + ValidationContext validationContext) + { + if (Name == Description) + { + yield return new ValidationResult( + "Name and Description can not be the same!", + new[] { "Name", "Description" } + ); + } + } + } +} +```` + +#### 解析服务 + +如果你需要从[依赖注入系统](Dependency-Injection.md)解析服务,可以使用 `ValidationContext` 对象. +例: + +````csharp +var myService = validationContext.GetRequiredService(); +```` + +> 虽然可以在 `Validate` 方法中解析服务实现任何可能性,但在DTO中实现领域验证逻辑不是一个很好的做法. 应保持简单的DTO,他们的目的是传输数据(DTO:数据传输对象). + +## 验证基础设施 + +本节介绍了ABP框架提供的一些额外的服务. + +### IValidationEnabled 接口 + +`IValidationEnabled` 是可以由任何类来实现的空标记接口(注册到[DI](Dependency-Injection.md)并从中解析),让ABP框架为该类执行验证系统. +示例 : + +````csharp +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Validation; + +namespace Acme.BookStore +{ + public class MyService : ITransientDependency, IValidationEnabled + { + public virtual async Task DoItAsync(MyInput input) + { + //... + } + } +} +```` + +> ABP框架使用[动态代理/拦截](Dynamic-Proxying-Interceptors.md)系统来执行验证.为了使其工作,你的方法应该是 **virtual** 的,服务应该被注入并通过接口(如`IMyService`)使用. + +### AbpValidationException + +一旦ABP确定了一个验证错误,它就会抛出类型为 `AbpValidationException` 的异常. 你的应用程序代码可以抛出 `AbpValidationException`,但大多数情况不会使用它. + +* `ValidationErrors` 是 `AbpValidationException` 的属性,它包含了验证错误列表. +* `AbpValidationException` 的日志级别设置为 `Warning`. 它将所有验证错误记录到[日志系统](Logging.md). +* `AbpValidationException` 由ABP框架自动捕获并将HTTP状态码设置为400转换成可用的错误响应. 参阅[异常处理](Exception-Handling.md)文档了解更多. + +## 高级主题 + +### IObjectValidator + +除了自动验证你可能需要手动验证对象,这种情况下[注入](Dependency-Injection.md)并使用 `IObjectValidator` 服务: + +* `Validate` 方法根据验证​​规则验证给定对象,如果对象没有被验证通过会抛出 `AbpValidationException` 异常. +* `GetErrors` 不会抛出异常,只返回验证错误. + +`IObjectValidator` 默认由 `ObjectValidator` 实现. `ObjectValidator`是可扩展的; 可以实现`IObjectValidationContributor`接口提供自定义逻辑. +示例 : + +````csharp +public class MyObjectValidationContributor + : IObjectValidationContributor, ITransientDependency +{ + public void AddErrors(ObjectValidationContext context) + { + //Get the validating object + var obj = context.ValidatingObject; + + //Add the validation errors if available + context.Errors.Add(...); + } +} +```` + +* 记录将类注册到[DI](Dependency-Injection.md)(实现`ITransientDependency` 如同本例) +* ABP会自动发现验证类,并用于任何类型的对象验证(包括自动方法调用验证). + +### IMethodInvocationValidator + +`IMethodInvocationValidator` 用于验证方法调用. 它在内部使用 `IObjectValidator` 来验证传递给方法调用的对象. 由于框架会自动使用此服务,通常你并不需要此服务,但在少数情况下你可能在应用程序中重用或替换它. + +## FluentValidation Integration + +Volo.Abp.FluentValidation 包将FluentValidation库集成到了验证系统(通过实现 `IObjectValidationContributor`). 请参阅[FluentValidation集成文档](FluentValidation.md)了解更多信息. \ No newline at end of file diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index 301af0fd6a..72f92203db 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -75,7 +75,14 @@ "path": "Exception-Handling.md" }, { - "text": "验证" + "text": "验证", + "path": "Validation.md", + "items": [ + { + "text": "FluentValidation集成", + "path": "FluentValidation.md" + } + ] }, { "text": "授权" diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 088a9437a8..b731aff187 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -1,4 +1,4 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28922.388 MinimumVisualStudioVersion = 10.0.40219.1 @@ -256,6 +256,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Minify", "src\Volo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Minify.Tests", "test\Volo.Abp.Minify.Tests\Volo.Abp.Minify.Tests.csproj", "{E69182B3-350A-43F5-A935-5EBBEBECEF97}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Serilog", "src\Volo.Abp.AspNetCore.Serilog\Volo.Abp.AspNetCore.Serilog.csproj", "{3B801003-BE74-49ED-9749-DA5E99F45EBF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Serilog.Tests", "test\Volo.Abp.AspNetCore.Serilog.Tests\Volo.Abp.AspNetCore.Serilog.Tests.csproj", "{9CAA07ED-FE5C-4427-A6FA-6C6CB5B4CC62}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -762,6 +766,14 @@ Global {E69182B3-350A-43F5-A935-5EBBEBECEF97}.Debug|Any CPU.Build.0 = Debug|Any CPU {E69182B3-350A-43F5-A935-5EBBEBECEF97}.Release|Any CPU.ActiveCfg = Release|Any CPU {E69182B3-350A-43F5-A935-5EBBEBECEF97}.Release|Any CPU.Build.0 = Release|Any CPU + {3B801003-BE74-49ED-9749-DA5E99F45EBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B801003-BE74-49ED-9749-DA5E99F45EBF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B801003-BE74-49ED-9749-DA5E99F45EBF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B801003-BE74-49ED-9749-DA5E99F45EBF}.Release|Any CPU.Build.0 = Release|Any CPU + {9CAA07ED-FE5C-4427-A6FA-6C6CB5B4CC62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9CAA07ED-FE5C-4427-A6FA-6C6CB5B4CC62}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9CAA07ED-FE5C-4427-A6FA-6C6CB5B4CC62}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9CAA07ED-FE5C-4427-A6FA-6C6CB5B4CC62}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -892,6 +904,8 @@ Global {73559227-EBF0-475F-835B-1FF0CD9132AA} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {928DC30D-C078-4BB4-A9F8-FE7252C67DC6} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {E69182B3-350A-43F5-A935-5EBBEBECEF97} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {3B801003-BE74-49ED-9749-DA5E99F45EBF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {9CAA07ED-FE5C-4427-A6FA-6C6CB5B4CC62} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/AbpValidationActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/AbpValidationActionFilter.cs index 963459ab58..ae047fa30a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/AbpValidationActionFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/AbpValidationActionFilter.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Filters; -using Volo.Abp.Aspects; using Volo.Abp.DependencyInjection; namespace Volo.Abp.AspNetCore.Mvc.Validation diff --git a/framework/src/Volo.Abp.AspNetCore.Serilog/Microsoft/AspNetCore/Builder/AbpAspNetCoreSerilogApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Serilog/Microsoft/AspNetCore/Builder/AbpAspNetCoreSerilogApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..cf15367e15 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Serilog/Microsoft/AspNetCore/Builder/AbpAspNetCoreSerilogApplicationBuilderExtensions.cs @@ -0,0 +1,13 @@ +using Volo.Abp.AspNetCore.Serilog; + +namespace Microsoft.AspNetCore.Builder +{ + public static class AbpAspNetCoreSerilogApplicationBuilderExtensions + { + public static IApplicationBuilder UseAbpSerilogEnrichers(this IApplicationBuilder app) + { + return app + .UseMiddleware(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Serilog/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Serilog/Properties/launchSettings.json new file mode 100644 index 0000000000..cb1bd6546c --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Serilog/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:61851/", + "sslPort": 44301 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Volo.Abp.AspNetCore.Serilog": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj b/framework/src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj new file mode 100644 index 0000000000..f450e04423 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj @@ -0,0 +1,27 @@ + + + + + + netcoreapp3.1 + Volo.Abp.AspNetCore.Serilog + Volo.Abp.AspNetCore.Serilog + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + true + Library + + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpAspNetCoreSerilogModule.cs b/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpAspNetCoreSerilogModule.cs new file mode 100644 index 0000000000..dc8e7c1ce4 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpAspNetCoreSerilogModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.AspNetCore.Serilog +{ + [DependsOn( + typeof(AbpMultiTenancyModule), + typeof(AbpAspNetCoreModule) + )] + public class AbpAspNetCoreSerilogModule : AbpModule + { + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpAspNetCoreSerilogOptions.cs b/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpAspNetCoreSerilogOptions.cs new file mode 100644 index 0000000000..84f212ff4a --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpAspNetCoreSerilogOptions.cs @@ -0,0 +1,30 @@ +namespace Volo.Abp.AspNetCore.Serilog +{ + public class AbpAspNetCoreSerilogOptions + { + public AllEnricherPropertyNames EnricherPropertyNames { get; } = new AllEnricherPropertyNames(); + + public class AllEnricherPropertyNames + { + /// + /// Default value: "TenantId". + /// + public string TenantId { get; set; } = "TenantId"; + + /// + /// Default value: "UserId". + /// + public string UserId { get; set; } = "UserId"; + + /// + /// Default value: "ClientId". + /// + public string ClientId { get; set; } = "ClientId"; + + /// + /// Default value: "CorrelationId". + /// + public string CorrelationId { get; set; } = "CorrelationId"; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpSerilogMiddleware.cs b/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpSerilogMiddleware.cs new file mode 100644 index 0000000000..8a8f032766 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpSerilogMiddleware.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using Serilog.Context; +using Serilog.Core; +using Serilog.Core.Enrichers; +using Volo.Abp.Clients; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Tracing; +using Volo.Abp.Users; + +namespace Volo.Abp.AspNetCore.Serilog +{ + public class AbpSerilogMiddleware : IMiddleware, ITransientDependency + { + private readonly ICurrentClient _currentClient; + private readonly ICurrentTenant _currentTenant; + private readonly ICurrentUser _currentUser; + private readonly ICorrelationIdProvider _correlationIdProvider; + private readonly AbpAspNetCoreSerilogOptions _options; + + public AbpSerilogMiddleware( + ICurrentTenant currentTenant, + ICurrentUser currentUser, + ICurrentClient currentClient, + ICorrelationIdProvider correlationIdProvider, + IOptions options) + { + _currentTenant = currentTenant; + _currentUser = currentUser; + _currentClient = currentClient; + _correlationIdProvider = correlationIdProvider; + _options = options.Value; + } + + public async Task InvokeAsync(HttpContext context, RequestDelegate next) + { + var enrichers = new List(); + + if (_currentTenant?.Id != null) + { + enrichers.Add(new PropertyEnricher(_options.EnricherPropertyNames.TenantId, _currentTenant.Id)); + } + + if (_currentUser?.Id != null) + { + enrichers.Add(new PropertyEnricher(_options.EnricherPropertyNames.UserId, _currentUser.Id)); + } + + if (_currentClient?.Id != null) + { + enrichers.Add(new PropertyEnricher(_options.EnricherPropertyNames.ClientId, _currentClient.Id)); + } + + var correlationId = _correlationIdProvider.Get(); + if (!string.IsNullOrEmpty(correlationId)) + { + enrichers.Add(new PropertyEnricher(_options.EnricherPropertyNames.CorrelationId, correlationId)); + } + + using (LogContext.Push(enrichers.ToArray())) + { + await next(context).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs index 66467bfa36..9af114dfff 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs @@ -2,6 +2,7 @@ { public enum NuGetPackageTarget : byte { + Undefined = 0, DomainShared = 1, Domain = 2, ApplicationContracts = 3, diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs index 9c6edf479d..9d155ba079 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using JetBrains.Annotations; namespace Volo.Abp.Cli.ProjectModification { public static class ProjectFinder { + [CanBeNull] public static string FindNuGetTargetProjectFile(string[] projectFiles, NuGetPackageTarget target) { if (!projectFiles.Any()) @@ -40,7 +42,7 @@ namespace Volo.Abp.Cli.ProjectModification case NuGetPackageTarget.HttpApiClient: return FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi.Client"); default: - throw new ApplicationException($"{nameof(NuGetPackageTarget)}.{target} has not implemented!"); + return null; } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs index 5e20904812..012d5e7bcf 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs @@ -68,7 +68,7 @@ namespace Volo.Abp.Cli.ProjectModification var targetProjectFile = ProjectFinder.FindNuGetTargetProjectFile(projectFiles, nugetPackage.Target); if (targetProjectFile == null) { - Logger.LogDebug($"Target project is not available for NuGet package '{nugetPackage.Name}'"); + Logger.LogDebug($"Target project is not available for this NuGet package '{nugetPackage.Name}'"); continue; } diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json index ac0c014967..282423a44a 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json @@ -8,7 +8,7 @@ "The {0} field only accepts files with the following extensions: {1}": "{0}字段只允许以下扩展名的文件: {1}", "The field {0} must be a string or array type with a maximum length of '{1}'.": "字段{0}必须是最大长度为'{1}'的字符串或数组.", "The field {0} must be a string or array type with a minimum length of '{1}'.": "字段{0}必须是最小长度为'{1}'的字符串或数组.", - "The {0} field is not a valid phone number.": "字段{0}不是有效的身份证号码.", + "The {0} field is not a valid phone number.": "字段{0}不是有效的手机号码.", "The field {0} must be between {1} and {2}.": "字段{0}值必须在{1}和{2}范围内.", "The field {0} must match the regular expression '{1}'.": "字段{0}必须匹配正则表达式'{1}'.", "The {0} field is required.": "字段{0}不可为空.", @@ -22,7 +22,7 @@ "ThisFieldOnlyAcceptsFilesWithTheFollowingExtensions:{0}": "字段只允许以下扩展名的文件: {0}", "ThisFieldMustBeAStringOrArrayTypeWithAMaximumLengthoOf{0}": "字段必须是最大长度为'{0}'的字符串或数组.", "ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf{0}": "字段必须是最小长度为'{0}'的字符串或数组.", - "ThisFieldIsNotAValidPhoneNumber.": "字段不是有效的身份证号码.", + "ThisFieldIsNotAValidPhoneNumber.": "字段不是有效的手机号码.", "ThisFieldMustBeBetween{0}And{1}": "字段值必须在{0}和{1}范围内.", "ThisFieldMustMatchTheRegularExpression{0}": "字段必须匹配正则表达式'{0}'.", "ThisFieldIsRequired.": "字段不可为空.", diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj index 3384fcf332..931eb63088 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj @@ -9,7 +9,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo.Abp.AspNetCore.Serilog.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo.Abp.AspNetCore.Serilog.Tests.csproj new file mode 100644 index 0000000000..740c57949d --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo.Abp.AspNetCore.Serilog.Tests.csproj @@ -0,0 +1,24 @@ + + + + + + netcoreapp3.1 + Volo.Abp.AspNetCore.Serilog.Tests + Volo.Abp.AspNetCore.Serilog.Tests + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/AbpSerilogTestModule.cs b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/AbpSerilogTestModule.cs new file mode 100644 index 0000000000..557288cd7d --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/AbpSerilogTestModule.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Volo.Abp.AspNetCore.MultiTenancy; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Serilog; +using Volo.Abp.AspNetCore.TestBase; +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.AspNetCore.App +{ + [DependsOn( + typeof(AbpAspNetCoreTestBaseModule), + typeof(AbpAspNetCoreMvcModule), + typeof(AbpAspNetCoreMultiTenancyModule), + typeof(AbpAspNetCoreSerilogModule), + typeof(AbpAutofacModule) + )] + public class AbpSerilogTestModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => { options.IsEnabled = true; }); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + + app.UseCorrelationId(); + app.UseRouting(); + app.UseAuthorization(); + app.UseMultiTenancy(); + app.UseAuditing(); + app.UseAbpSerilogEnrichers(); + app.UseMvcWithDefaultRouteAndArea(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/CollectingSink.cs b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/CollectingSink.cs new file mode 100644 index 0000000000..fe403e02ef --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/CollectingSink.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Linq; +using Serilog.Core; +using Serilog.Events; + +namespace Volo.Abp.AspNetCore.App +{ + public class CollectingSink : ILogEventSink + { + public List Events { get; } = new List(); + + public LogEvent SingleEvent => Events.Single(); + + public void Emit(LogEvent logEvent) + { + Events.Add(logEvent); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/SerilogTestController.cs b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/SerilogTestController.cs new file mode 100644 index 0000000000..972af4c801 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/SerilogTestController.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Tracing; + +namespace Volo.Abp.AspNetCore.App +{ + public class SerilogTestController : AbpController + { + private readonly ICorrelationIdProvider _correlationIdProvider; + + public SerilogTestController(ICorrelationIdProvider correlationIdProvider) + { + _correlationIdProvider = correlationIdProvider; + } + + public ActionResult Index() + { + return Content("Index-Result"); + } + + public ActionResult CorrelationId() + { + return Content(_correlationIdProvider.Get()); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/Startup.cs b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/Startup.cs new file mode 100644 index 0000000000..a1a58100d5 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/App/Startup.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Volo.Abp.AspNetCore.App +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddApplication(); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env,ILoggerFactory loggerFactory) + { + app.InitializeApplication(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/Serilog/AbpSerilogTestBase.cs b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/Serilog/AbpSerilogTestBase.cs new file mode 100644 index 0000000000..96a13ac45b --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/Serilog/AbpSerilogTestBase.cs @@ -0,0 +1,31 @@ +using System.Linq; +using Microsoft.Extensions.Hosting; +using Serilog; +using Serilog.Events; +using Volo.Abp.AspNetCore.App; + +namespace Volo.Abp.AspNetCore.Serilog +{ + public class AbpSerilogTestBase : AbpAspNetCoreTestBase + { + protected readonly CollectingSink CollectingSink = new CollectingSink(); + + protected override IHostBuilder CreateHostBuilder() + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.Sink(CollectingSink) + .CreateLogger(); + + return base.CreateHostBuilder() + .UseSerilog(); + } + + protected LogEvent GetLogEvent(string text) + { + return CollectingSink.Events.FirstOrDefault(m => m.MessageTemplate.Text == text); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/Serilog/Serilog_Enrichers_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/Serilog/Serilog_Enrichers_Tests.cs new file mode 100644 index 0000000000..0a7a189a35 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo/Abp/AspNetCore/Serilog/Serilog_Enrichers_Tests.cs @@ -0,0 +1,97 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Serilog.Events; +using Shouldly; +using Volo.Abp.AspNetCore.App; +using Volo.Abp.AspNetCore.MultiTenancy; +using Volo.Abp.MultiTenancy; +using Volo.Abp.MultiTenancy.ConfigurationStore; +using Xunit; + +namespace Volo.Abp.AspNetCore.Serilog +{ + public class Serilog_Enrichers_Tests : AbpSerilogTestBase + { + private const string ExecutedEndpointLogEventText = "Executed endpoint '{EndpointName}'"; + + private readonly Guid _testTenantId = Guid.NewGuid(); + private readonly string _testTenantName = "acme"; + + private readonly AbpAspNetCoreMultiTenancyOptions _tenancyOptions; + private readonly AbpAspNetCoreSerilogOptions _serilogOptions; + private readonly ILogger _logger; + + public Serilog_Enrichers_Tests() + { + _tenancyOptions = ServiceProvider.GetRequiredService>().Value; + _serilogOptions = + ServiceProvider.GetRequiredService>().Value; + _logger = ServiceProvider.GetRequiredService>(); + } + + protected override IHostBuilder CreateHostBuilder() + { + return base.CreateHostBuilder().ConfigureServices(services => + { + services.Configure(options => + { + options.Tenants = new[] + { + new TenantConfiguration(_testTenantId, _testTenantName) + }; + }); + }); + } + + [Fact] + public async Task TenantId_Not_Set_Test() + { + var url = GetUrl(nameof(SerilogTestController.Index)); + var result = await GetResponseAsStringAsync(url).ConfigureAwait(false); + + var executedLogEvent = GetLogEvent(ExecutedEndpointLogEventText); + + executedLogEvent.ShouldNotBeNull(); + executedLogEvent.Properties.ContainsKey(_serilogOptions.EnricherPropertyNames.TenantId) + .ShouldBe(false); + } + + [Fact] + public async Task TenantId_Set_Test() + { + var url = + GetUrl(nameof(SerilogTestController.Index)) + + $"?{_tenancyOptions.TenantKey}={_testTenantName}"; + var result = await GetResponseAsStringAsync(url).ConfigureAwait(false); + + var executedLogEvent = GetLogEvent(ExecutedEndpointLogEventText); + + executedLogEvent.ShouldNotBeNull(); + executedLogEvent.Properties.ContainsKey(_serilogOptions.EnricherPropertyNames.TenantId) + .ShouldBe(true); + ((ScalarValue) executedLogEvent.Properties[_serilogOptions.EnricherPropertyNames.TenantId]).Value + .ShouldBe(_testTenantId); + } + + [Fact] + public async Task CorrelationId_Enrichers_Test() + { + var url = GetUrl(nameof(SerilogTestController.CorrelationId)); + var result = await GetResponseAsStringAsync(url).ConfigureAwait(false); + + var executedLogEvent = GetLogEvent(ExecutedEndpointLogEventText); + + executedLogEvent.ShouldNotBeNull(); + + executedLogEvent.Properties.ContainsKey(_serilogOptions.EnricherPropertyNames.CorrelationId) + .ShouldNotBeNull(); + + ((ScalarValue) executedLogEvent.Properties[_serilogOptions.EnricherPropertyNames.CorrelationId]).Value + .ShouldBe(result); + } + } +} \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json index 391dee0531..c778a28dd6 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json @@ -29,7 +29,7 @@ "DisplayName:Surname": "姓", "DisplayName:Password": "密码", "DisplayName:EmailAddress": "电子邮件地址", - "DisplayName:PhoneNumber": "电话号码", + "DisplayName:PhoneNumber": "手机号码", "PersonalSettings": "个人设置", "PersonalSettingsSaved": "个人设置已保存", "PasswordChanged": "修改密码", diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs index 5abb564b09..40b819e0e1 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Account.Settings; +using Volo.Abp.Identity; using Volo.Abp.Settings; using Volo.Abp.Uow; using IdentityUser = Volo.Abp.Identity.IdentityUser; @@ -55,16 +56,16 @@ namespace Volo.Abp.Account.Web.Pages.Account public class PostInput { [Required] - [StringLength(32)] + [StringLength(IdentityUserConsts.MaxUserNameLength)] public string UserName { get; set; } [Required] [EmailAddress] - [StringLength(255)] + [StringLength(IdentityUserConsts.MaxEmailLength)] public string EmailAddress { get; set; } [Required] - [StringLength(32)] + [StringLength(IdentityUserConsts.MaxPasswordLength)] [DataType(DataType.Password)] public string Password { get; set; } } diff --git a/modules/blogging/README.md b/modules/blogging/README.md index 9eda941757..716713666f 100644 --- a/modules/blogging/README.md +++ b/modules/blogging/README.md @@ -1,3 +1,10 @@ # Blogging Module -This module is used for ABP blog: https://abp.io/blog/abp/ +This module adds a simple blog to your ABP application; + +* Allows to create multiple blogs in a single application. +* Supports the Markdown format. +* Allows to write comment for a post. +* Allows to assign tags to the blog posts. + +See the [blog.abp.io](https://blog.abp.io/) website as a live example of the blogging module. \ No newline at end of file diff --git a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj index 750481d6a1..1ef5fb5c4c 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj +++ b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj @@ -12,7 +12,7 @@ - + diff --git a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/Volo.ClientSimulation.Demo.csproj b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/Volo.ClientSimulation.Demo.csproj index 8544cf6cff..0f9f7bde5e 100644 --- a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/Volo.ClientSimulation.Demo.csproj +++ b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/Volo.ClientSimulation.Demo.csproj @@ -8,7 +8,7 @@ - + diff --git a/modules/docs/README.md b/modules/docs/README.md index ee4e33a785..99117426d3 100644 --- a/modules/docs/README.md +++ b/modules/docs/README.md @@ -1,13 +1,15 @@ # Docs Module -This module is used to create technical documentation web sites. [abp.io](https://abp.io) web site uses this module for its documentation. +This module is used to create technical documentation web sites; -### Screenshot +* Built-in **GitHub integration**: Directly write and manage documents on GitHub. +* **Versioning** support directly integrated to GitHub releases. +* Supports **multi-language** (with fallback support to the default language). +* Supports the **Markdown** and HTML formats. +* Provides a **navigation** and an **outline** section. +* Allows to host **multiple projects** documentation in a single application. +* Links to the file on GitHub, so anyone can easily contribute by clicking to the **Edit link**. +* In addition to the GitHub source, allows to simply use a folder as the documentation source. -![screenshot](screenshot.png) +ABP Framework uses this module to host its documentation. See [docs.abp.io](https://docs.abp.io/). A screenshot from the ABP documentation: -### Main Features - -* Retrieves documents from a Github repository. -* Supports Markdown document formatting. -* Supports versioning (integrated to Github releases). -* Supports multiple projects. +![screenshot](screenshot2.png) diff --git a/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj b/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj index ef8fc992e3..c5e164247b 100644 --- a/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj +++ b/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj @@ -14,7 +14,7 @@ - + diff --git a/modules/docs/screenshot.png b/modules/docs/screenshot.png deleted file mode 100644 index 55ff411f0c..0000000000 Binary files a/modules/docs/screenshot.png and /dev/null differ diff --git a/modules/docs/screenshot2.png b/modules/docs/screenshot2.png new file mode 100644 index 0000000000..0dcdf921b9 Binary files /dev/null and b/modules/docs/screenshot2.png differ diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs index f56a59e967..ef2cc57ef4 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs @@ -18,7 +18,7 @@ namespace Volo.Abp.Identity public const int MaxPhoneNumberLength = AbpUserConsts.MaxPhoneNumberLength; - public const int MaxPasswordLength = 32; + public const int MaxPasswordLength = 128; public const int MaxPasswordHashLength = 256; diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json index f57029eb9e..4908f170fa 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json @@ -82,7 +82,7 @@ "DisplayName:Abp.Identity.Lockout.LockoutDuration": "锁定时间(秒)", "DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts": "最大失败访问尝试次数", "DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail": "要求验证的电子邮箱", - "DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "要求验证的电话号码", + "DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "要求验证的手机号码", "DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled": "启用用户名更新", "DisplayName:Abp.Identity.User.IsEmailUpdateEnabled": "启用电子邮箱更新", "Description:Abp.Identity.Password.RequiredLength": "密码的最小长度.", @@ -95,7 +95,7 @@ "Description:Abp.Identity.Lockout.LockoutDuration": "当锁定发生时用户被的锁定的时间(秒).", "Description:Abp.Identity.Lockout.MaxFailedAccessAttempts": "如果启用锁定, 当用户被锁定前失败的访问尝试次数.", "Description:Abp.Identity.SignIn.RequireConfirmedEmail": "登录时是否需要验证的电子邮箱.", - "Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "登录时是否需要验证的电话号码.", + "Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "登录时是否需要验证的手机号码.", "Description:Abp.Identity.User.IsUserNameUpdateEnabled": "是否允许用户更新用户名.", "Description:Abp.Identity.User.IsEmailUpdateEnabled": "是否允许用户更新电子邮箱." diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRole.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRole.cs index 825266e993..dac173719a 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRole.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRole.cs @@ -100,6 +100,22 @@ namespace Volo.Abp.Identity Claims.RemoveAll(c => c.ClaimType == claim.Type && c.ClaimValue == claim.Value); } + public virtual void ChangeName(string name) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + + var oldName = Name; + Name = name; + + AddLocalEvent( + new IdentityRoleNameChangedEvent + { + IdentityRole = this, + OldName = oldName + } + ); + } + public override string ToString() { return $"{base.ToString()}, Name = {Name}"; diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleNameChangedEvent.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleNameChangedEvent.cs new file mode 100644 index 0000000000..f14f0a71ed --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleNameChangedEvent.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.Identity +{ + public class IdentityRoleNameChangedEvent + { + public IdentityRole IdentityRole { get; set; } + public string OldName { get; set; } + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleStore.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleStore.cs index 1ca46b0830..004fc0c20c 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleStore.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleStore.cs @@ -165,7 +165,7 @@ namespace Volo.Abp.Identity Check.NotNull(role, nameof(role)); - role.Name = roleName; + role.ChangeName(roleName); return Task.CompletedTask; } diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo.Abp.PermissionManagement.Domain.Identity.csproj b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo.Abp.PermissionManagement.Domain.Identity.csproj index ea663ba49b..ee8e336088 100644 --- a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo.Abp.PermissionManagement.Domain.Identity.csproj +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo.Abp.PermissionManagement.Domain.Identity.csproj @@ -16,6 +16,7 @@ + diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleUpdateEventHandler.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleUpdateEventHandler.cs new file mode 100644 index 0000000000..54e8bec3b1 --- /dev/null +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleUpdateEventHandler.cs @@ -0,0 +1,43 @@ +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus; +using Volo.Abp.Identity; + +namespace Volo.Abp.PermissionManagement.Identity +{ + public class RoleUpdateEventHandler : + ILocalEventHandler, + ITransientDependency + { + protected IIdentityRoleRepository RoleRepository { get; } + protected IPermissionManager PermissionManager { get; } + protected IPermissionGrantRepository PermissionGrantRepository { get; } + + public RoleUpdateEventHandler( + IIdentityRoleRepository roleRepository, + IPermissionManager permissionManager, + IPermissionGrantRepository permissionGrantRepository) + { + RoleRepository = roleRepository; + PermissionManager = permissionManager; + PermissionGrantRepository = permissionGrantRepository; + } + + public async Task HandleEventAsync(IdentityRoleNameChangedEvent eventData) + { + var role = await RoleRepository.FindAsync(eventData.IdentityRole.Id, false).ConfigureAwait(false); + if (role == null) + { + return; + } + + var permissionGrantsInRole = await PermissionGrantRepository.GetListAsync(RolePermissionValueProvider.ProviderName, eventData.OldName).ConfigureAwait(false); + foreach (var permissionGrant in permissionGrantsInRole) + { + await PermissionManager.UpdateProviderKeyAsync(permissionGrant, eventData.IdentityRole.Name).ConfigureAwait(false); + } + } + } +} diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/RoleChangingEvents_Test.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/RoleChangingEvents_Test.cs new file mode 100644 index 0000000000..1a4405c9e4 --- /dev/null +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/RoleChangingEvents_Test.cs @@ -0,0 +1,58 @@ +using Microsoft.AspNetCore.Identity; +using Shouldly; +using System.Threading.Tasks; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.Guids; +using Volo.Abp.PermissionManagement; +using Volo.Abp.Uow; +using Xunit; + +namespace Volo.Abp.Identity +{ + public class RoleChangingEvents_Test : AbpIdentityDomainTestBase + { + protected readonly IIdentityRoleRepository RoleRepository; + protected readonly IPermissionGrantRepository PermissionGrantRepository; + protected readonly IdentityRoleManager RoleManager; + protected readonly ILookupNormalizer LookupNormalizer; + protected readonly IGuidGenerator GuidGenerator; + protected readonly IUnitOfWorkManager UowManager; + + public RoleChangingEvents_Test() + { + RoleRepository = GetRequiredService(); ; + PermissionGrantRepository = GetRequiredService(); ; + RoleManager = GetRequiredService(); ; + LookupNormalizer = GetRequiredService(); ; + GuidGenerator = GetRequiredService(); + UowManager = GetRequiredService(); + } + + [Fact] + public async Task Role_Update_Event_Test() + { + var role = await RoleRepository + .FindByNormalizedNameAsync(LookupNormalizer.NormalizeName("moderator")) + .ConfigureAwait(false); + + var permissionGrantsInRole = await PermissionGrantRepository.GetListAsync("R", role.Name).ConfigureAwait(false); + permissionGrantsInRole.ShouldNotBeNull(); + permissionGrantsInRole.Count.ShouldBeGreaterThan(0); + var count = permissionGrantsInRole.Count; + + using (var uow = UowManager.Begin()) + { + var identityResult = await RoleManager.SetRoleNameAsync(role, "TestModerator").ConfigureAwait(false); + identityResult.Succeeded.ShouldBeTrue(); + var xx = await RoleRepository.UpdateAsync(role).ConfigureAwait(false); + await uow.CompleteAsync().ConfigureAwait(false); + } + + role = await RoleRepository.GetAsync(role.Id).ConfigureAwait(false); + role.Name.ShouldBe("TestModerator"); + + permissionGrantsInRole = await PermissionGrantRepository.GetListAsync("R", role.Name).ConfigureAwait(false); + permissionGrantsInRole.Count.ShouldBe(count); + } + } +} diff --git a/modules/permission-management/Volo.Abp.PermissionManagement.sln b/modules/permission-management/Volo.Abp.PermissionManagement.sln index 5812918a3c..16071f9e10 100644 --- a/modules/permission-management/Volo.Abp.PermissionManagement.sln +++ b/modules/permission-management/Volo.Abp.PermissionManagement.sln @@ -19,7 +19,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.PermissionManageme EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.PermissionManagement.Web", "src\Volo.Abp.PermissionManagement.Web\Volo.Abp.PermissionManagement.Web.csproj", "{97A386F8-DAE0-4BEA-ADE5-1D57C3336515}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.PermissionManagement.Tests", "test\Volo.Abp.PermissionManagement.Tests\Volo.Abp.PermissionManagement.Tests.csproj", "{48297098-79D0-413B-939C-6C432142C42D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.PermissionManagement.Domain.Tests", "test\Volo.Abp.PermissionManagement.Tests\Volo.Abp.PermissionManagement.Domain.Tests.csproj", "{48297098-79D0-413B-939C-6C432142C42D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.PermissionManagement.TestBase", "test\Volo.Abp.PermissionManagement.TestBase\Volo.Abp.PermissionManagement.TestBase.csproj", "{49259427-CAEB-4FAE-81E4-848F789487EA}" EndProject @@ -33,7 +33,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.PermissionManageme EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.PermissionManagement.HttpApi.Client", "src\Volo.Abp.PermissionManagement.HttpApi.Client\Volo.Abp.PermissionManagement.HttpApi.Client.csproj", "{1CD80519-9431-48DB-B0EA-291A73FF9F49}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.PermissionManagement.Application.Tests", "test\Volo.Abp.PermissionManagement.Application.Tests\Volo.Abp.PermissionManagement.Application.Tests.csproj", "{A0F72F5F-3713-4E06-ADB7-15ADFDCB79B1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.PermissionManagement.Application.Tests", "test\Volo.Abp.PermissionManagement.Application.Tests\Volo.Abp.PermissionManagement.Application.Tests.csproj", "{A0F72F5F-3713-4E06-ADB7-15ADFDCB79B1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionManager.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionManager.cs index 888f06235e..26310a12fe 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionManager.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionManager.cs @@ -13,5 +13,7 @@ namespace Volo.Abp.PermissionManagement Task> GetAllAsync([NotNull] string providerName, [NotNull] string providerKey); Task SetAsync(string permissionName, string providerName, string providerKey, bool isGranted); + + Task UpdateProviderKeyAsync(PermissionGrant permissionGrant, string providerKey); } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrant.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrant.cs index 7ec99fbe3a..4f951a92cc 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrant.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrant.cs @@ -17,7 +17,7 @@ namespace Volo.Abp.PermissionManagement public virtual string ProviderName { get; protected set; } [CanBeNull] - public virtual string ProviderKey { get; protected set; } + public virtual string ProviderKey { get; protected internal set; } protected PermissionGrant() { diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManager.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManager.cs index d7d1dd70c1..4f7d177802 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManager.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManager.cs @@ -99,6 +99,12 @@ namespace Volo.Abp.PermissionManagement await provider.SetAsync(permissionName, providerKey, isGranted).ConfigureAwait(false); } + public async Task UpdateProviderKeyAsync(PermissionGrant permissionGrant, string providerKey) + { + permissionGrant.ProviderKey = providerKey; + return await PermissionGrantRepository.UpdateAsync(permissionGrant).ConfigureAwait(false); + } + protected virtual async Task GetInternalAsync(PermissionDefinition permission, string providerName, string providerKey) { var result = new PermissionWithGrantedProviders(permission.Name, false); diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo.Abp.PermissionManagement.Application.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo.Abp.PermissionManagement.Application.Tests.csproj index 92cd6ddd5d..81e3861ed9 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo.Abp.PermissionManagement.Application.Tests.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo.Abp.PermissionManagement.Application.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo.Abp.PermissionManagement.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj similarity index 100% rename from modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo.Abp.PermissionManagement.Tests.csproj rename to modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo/Abp/PermissionManagement/PermissionManager_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo/Abp/PermissionManagement/PermissionManager_Tests.cs index 6b838a4fc0..66f0de1755 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo/Abp/PermissionManagement/PermissionManager_Tests.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo/Abp/PermissionManagement/PermissionManager_Tests.cs @@ -104,5 +104,20 @@ namespace Volo.Abp.PermissionManagement true).ConfigureAwait(false)).ConfigureAwait(false); } + [Fact] + public async Task UpdateProviderKey() + { + await _permissionGrantRepository.InsertAsync(new PermissionGrant( + Guid.NewGuid(), + "MyPermission1", + "Test", + "Test") + ).ConfigureAwait(false); + var permissionGrant = await _permissionGrantRepository.FindAsync("MyPermission1", "Test", "Test").ConfigureAwait(false); + permissionGrant.ProviderKey.ShouldBe("Test"); + + await _permissionManager.UpdateProviderKeyAsync(permissionGrant, "NewProviderKey").ConfigureAwait(false); + (await _permissionGrantRepository.FindAsync("MyPermission1", "Test", "NewProviderKey").ConfigureAwait(false)).ShouldNotBeNull(); + } } } diff --git a/npm/ng-packs/packages/core/src/lib/actions/replaceable-components.actions.ts b/npm/ng-packs/packages/core/src/lib/actions/replaceable-components.actions.ts index e611459960..3b948cf63d 100644 --- a/npm/ng-packs/packages/core/src/lib/actions/replaceable-components.actions.ts +++ b/npm/ng-packs/packages/core/src/lib/actions/replaceable-components.actions.ts @@ -1,5 +1,8 @@ import { ReplaceableComponents } from '../models/replaceable-components'; +/** + * @see usage: https://github.com/abpframework/abp/pull/2522#issue-358333183 + */ export class AddReplaceableComponent { static readonly type = '[ReplaceableComponents] Add'; constructor(public payload: ReplaceableComponents.ReplaceableComponent) {} diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index 1e959b3e54..2232fd0419 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -137,6 +137,7 @@ export class ConfigState { key: string | Config.LocalizationWithDefault, ...interpolateParams: string[] ) { + if (!key) key = ''; let defaultValue: string; if (typeof key !== 'string') { @@ -144,8 +145,6 @@ export class ConfigState { key = key.key; } - if (!key) key = ''; - const keys = key.split('::') as string[]; const selector = createSelector([ConfigState], (state: Config.State) => { if (!state.localization) return defaultValue || key; diff --git a/npm/ng-packs/packages/core/src/lib/states/session.state.ts b/npm/ng-packs/packages/core/src/lib/states/session.state.ts index 7ceaa4a1bc..7ea742dce7 100644 --- a/npm/ng-packs/packages/core/src/lib/states/session.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/session.state.ts @@ -67,7 +67,7 @@ export class SessionState { this.store.dispatch(new ModifyOpenedTabCount('increase')); - fromEvent(window, 'beforeunload').subscribe(() => { + fromEvent(window, 'unload').subscribe(event => { this.store.dispatch(new ModifyOpenedTabCount('decrease')); }); }); diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/account-layout/account-layout.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/account-layout/account-layout.component.ts index 57b8772304..105c756e54 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/account-layout/account-layout.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/account-layout/account-layout.component.ts @@ -6,7 +6,7 @@ import { eLayoutType } from '@abp/ng.core'; template: ` - + `, }) export class AccountLayoutComponent { diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html index 2730c0e2ef..890ed1baab 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html @@ -34,7 +34,8 @@ @@ -47,7 +48,9 @@ class="nav-item dropdown" display="static" (click)=" - navbarRootDropdown.expand ? (navbarRootDropdown.expand = false) : (navbarRootDropdown.expand = true) + navbarRootDropdown.expand + ? (navbarRootDropdown.expand = false) + : (navbarRootDropdown.expand = true) " >
-
+
- + {{ appInfo.name }} @@ -213,12 +220,12 @@ [class.d-block]="smallScreen" [class.abp-mh-25]="smallScreen && currentUserDropdown.isOpen()" > - {{ - 'AbpAccount::ManageYourProfile' | abpLocalization - }} - {{ - 'AbpUi::Logout' | abpLocalization - }} + {{ 'AbpAccount::ManageYourProfile' | abpLocalization }} + {{ 'AbpUi::Logout' | abpLocalization }}
diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/empty-layout/empty-layout.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/empty-layout/empty-layout.component.ts index c6082aadd4..4f1dac52ca 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/empty-layout/empty-layout.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/empty-layout/empty-layout.component.ts @@ -6,7 +6,7 @@ import { eLayoutType } from '@abp/ng.core'; template: ` - + `, }) export class EmptyLayoutComponent { diff --git a/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts b/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts index 269fd0071a..24c3e84e42 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts @@ -48,12 +48,6 @@ export default ` .container > .card { box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; } -.abp-confirm .abp-confirm-footer { - background-color: #f4f4f7 !important; -} -.abp-confirm .ui-toast-message-content { - background-color: #fff !important; -} @media screen and (min-width: 768px) { .navbar .dropdown:hover > .dropdown-menu { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts b/npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts deleted file mode 100644 index d66c29fc3b..0000000000 --- a/npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { MessageService } from 'primeng/components/common/messageservice'; -import { Observable, Subject } from 'rxjs'; -import { Toaster } from '../models/toaster'; -import { Config } from '@abp/ng.core'; - -export abstract class AbstractToaster { - status$: Subject; - - key = 'abpToast'; - - sticky = false; - - constructor(protected messageService: MessageService) {} - - info(message: Config.LocalizationParam, title: Config.LocalizationParam, options?: T): Observable { - return this.show(message, title, 'info', options); - } - - success(message: Config.LocalizationParam, title: Config.LocalizationParam, options?: T): Observable { - return this.show(message, title, 'success', options); - } - - warn(message: Config.LocalizationParam, title: Config.LocalizationParam, options?: T): Observable { - return this.show(message, title, 'warn', options); - } - - error(message: Config.LocalizationParam, title: Config.LocalizationParam, options?: T): Observable { - return this.show(message, title, 'error', options); - } - - protected show( - message: Config.LocalizationParam, - title: Config.LocalizationParam, - severity: Toaster.Severity, - options?: T, - ): Observable { - this.messageService.clear(this.key); - - this.messageService.add({ - severity, - detail: message || '', - summary: title || '', - ...options, - key: this.key, - ...(typeof (options || ({} as any)).sticky === 'undefined' && { sticky: this.sticky }), - }); - this.status$ = new Subject(); - return this.status$; - } - - clear(status?: Toaster.Status) { - this.messageService.clear(this.key); - this.status$.next(status || Toaster.Status.dismiss); - this.status$.complete(); - } -} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/animations/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/animations/index.ts index 816afbaaa4..d625d76d8e 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/animations/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/animations/index.ts @@ -3,3 +3,4 @@ export * from './collapse.animations'; export * from './fade.animations'; export * from './modal.animations'; export * from './slide.animations'; +export * from './toast.animations'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/animations/toast.animations.ts b/npm/ng-packs/packages/theme-shared/src/lib/animations/toast.animations.ts new file mode 100644 index 0000000000..7ac6a7519b --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/animations/toast.animations.ts @@ -0,0 +1,17 @@ +import { animate, query, style, transition, trigger } from '@angular/animations'; + +export const toastInOut = trigger('toastInOut', [ + transition('* <=> *', [ + query( + ':enter', + [ + style({ opacity: 0, transform: 'translateY(20px)' }), + animate('350ms ease', style({ opacity: 1, transform: 'translateY(0)' })), + ], + { optional: true }, + ), + query(':leave', animate('450ms ease', style({ opacity: 0 })), { + optional: true, + }), + ]), +]); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.html b/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.html new file mode 100644 index 0000000000..07faa3a8a7 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.html @@ -0,0 +1,34 @@ +
+
+
+
+ +
+
+

+ {{ data.title | abpLocalization: data.options?.titleLocalizationParams }} +

+

+ {{ data.message | abpLocalization: data.options?.messageLocalizationParams }} +

+
+ +
+
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.scss new file mode 100644 index 0000000000..752bb22eee --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.scss @@ -0,0 +1,120 @@ +.confirmation { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: none; + place-items: center; + z-index: 1060; + &.show { + display: grid; + } + .confirmation-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(#000, 0.7); + z-index: 1061 !important; + } + .confirmation-dialog { + display: flex; + flex-direction: column; + margin: 20px auto; + padding: 0; + border: none; + border-radius: 10px; + width: 450px; + min-height: 300px; + background-color: #fff; + box-shadow: 0 0 10px -5px rgba(#000, 0.5); + z-index: 1062 !important; + .icon-container { + display: flex; + align-items: center; + justify-content: center; + margin: 0 0 10px 0; + padding: 20px; + .icon { + width: 100px; + height: 100px; + stroke-width: 1; + fill: #fff; + font-size: 80px; + text-align: center; + } + &.neutral .icon { + } + &.info .icon { + stroke: #2f96b4; + color: #2f96b4; + } + &.success .icon { + stroke: #51a351; + color: #51a351; + } + &.warning .icon { + stroke: #f89406; + color: #f89406; + } + &.error .icon { + stroke: #bd362f; + color: #bd362f; + } + } + .content { + flex-grow: 1; + display: block; + .title { + display: block; + margin: 0; + padding: 0; + font-size: 27px; + font-weight: 600; + text-align: center; + } + .message { + display: block; + margin: 10px auto; + padding: 20px; + color: #777; + font-size: 16px; + font-weight: 400; + text-align: center; + } + } + .footer { + display: flex; + align-items: center; + justify-content: flex-end; + margin: 10px 0 0 0; + padding: 20px; + width: 100%; + .confirmation-button { + display: inline-block; + margin: 0px 5px; + padding: 10px 20px; + border: none; + border-radius: 6px; + color: #777; + font-size: 14px; + font-weight: 600; + background-color: #eee; + &:hover { + background-color: darken(#eee, 5); + } + &-reject { + } + &-approve { + background-color: #2f96b4; + color: #fff; + &:hover { + background-color: darken(#2f96b4, 5); + } + } + } + } + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts index 6ddb5899f1..afdb024888 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts @@ -1,59 +1,47 @@ import { Component } from '@angular/core'; import { ConfirmationService } from '../../services/confirmation.service'; +import { Confirmation } from '../../models/confirmation'; +import { LocalizationService } from '@abp/ng.core'; import { Toaster } from '../../models/toaster'; @Component({ selector: 'abp-confirmation', - // tslint:disable-next-line: component-max-inline-declarations - template: ` - - - -
- {{ message.summary | abpLocalization: message.titleLocalizationParams }} -
-
- {{ message.detail | abpLocalization: message.messageLocalizationParams }} -
- - -
-
- `, + templateUrl: './confirmation.component.html', + styleUrls: ['./confirmation.component.scss'], }) export class ConfirmationComponent { confirm = Toaster.Status.confirm; reject = Toaster.Status.reject; dismiss = Toaster.Status.dismiss; - constructor(private confirmationService: ConfirmationService) {} + visible = false; + + data: Confirmation.DialogData; + + get iconClass(): string { + switch (this.data.severity) { + case 'info': + return 'fa-info-circle'; + case 'success': + return 'fa-check-circle'; + case 'warning': + return 'fa-exclamation-triangle'; + case 'error': + return 'fa-times-circle'; + default: + return 'fa-question-circle'; + } + } + + constructor( + private confirmationService: ConfirmationService, + private localizationService: LocalizationService, + ) { + this.confirmationService.confirmation$.subscribe(confirmation => { + this.data = confirmation; + this.visible = !!confirmation; + }); + } close(status: Toaster.Status) { this.confirmationService.clear(status); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.scss new file mode 100644 index 0000000000..023830ceeb --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.scss @@ -0,0 +1,25 @@ +.modal { + &.show { + display: block !important; + } + + &-backdrop { + background-color: rgba(0, 0, 0, 0.6); + } + + &::-webkit-scrollbar { + width: 7px; + } + + &::-webkit-scrollbar-track { + background: #ddd; + } + + &::-webkit-scrollbar-thumb { + background: #8a8686; + } + + &-dialog { + z-index: 1050; + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts index f95e0093b9..8aaaa58076 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts @@ -24,6 +24,7 @@ export type ModalSize = 'sm' | 'md' | 'lg' | 'xl'; selector: 'abp-modal', templateUrl: './modal.component.html', animations: [fadeAnimation], + styleUrls: ['./modal.component.scss'], }) export class ModalComponent implements OnDestroy { @Input() @@ -100,6 +101,20 @@ export class ModalComponent implements OnDestroy { destroy$ = new Subject(); + get isFormDirty(): boolean { + let node: HTMLDivElement; + if (!this.modalContent) { + node = document.getElementById('modal-container') as HTMLDivElement; + } + + const nodes = getFlatNodes( + ((node || this.modalContent.nativeElement).querySelector('#abp-modal-body') as HTMLElement) + .childNodes, + ); + + return hasNgDirty(nodes); + } + constructor(private renderer: Renderer2, private confirmationService: ConfirmationService) {} ngOnDestroy(): void { @@ -109,21 +124,15 @@ export class ModalComponent implements OnDestroy { close() { if (this.busy) return; - let node: HTMLDivElement; - if (!this.modalContent) { - node = document.getElementById('modal-container') as HTMLDivElement; - } - - const nodes = getFlatNodes( - ((node || this.modalContent.nativeElement).querySelector('#abp-modal-body') as HTMLElement).childNodes, - ); - - if (hasNgDirty(nodes)) { + if (this.isFormDirty) { if (this.isConfirmationOpen) return; this.isConfirmationOpen = true; this.confirmationService - .warn('AbpAccount::AreYouSureYouWantToCancelEditingWarningMessage', 'AbpAccount::AreYouSure') + .warn( + 'AbpAccount::AreYouSureYouWantToCancelEditingWarningMessage', + 'AbpAccount::AreYouSure', + ) .subscribe((status: Toaster.Status) => { this.isConfirmationOpen = false; if (status === Toaster.Status.confirm) { @@ -146,6 +155,16 @@ export class ModalComponent implements OnDestroy { this.close(); }); + fromEvent(window, 'beforeunload') + .pipe(takeUntil(this.destroy$)) + .subscribe(event => { + if (this.isFormDirty) { + event.returnValue = true; + } else { + delete event.returnValue; + } + }); + setTimeout(() => { if (!this.abpClose) return; fromEvent(this.abpClose.nativeElement, 'click') @@ -162,7 +181,10 @@ export class ModalComponent implements OnDestroy { function getFlatNodes(nodes: NodeList): HTMLElement[] { return Array.from(nodes).reduce( - (acc, val) => [...acc, ...(val.childNodes && val.childNodes.length ? getFlatNodes(val.childNodes) : [val])], + (acc, val) => [ + ...acc, + ...(val.childNodes && val.childNodes.length ? getFlatNodes(val.childNodes) : [val]), + ], [], ); } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.html b/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.html new file mode 100644 index 0000000000..88d45ff04b --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.html @@ -0,0 +1,11 @@ +
+ +
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.scss new file mode 100644 index 0000000000..ee8289dc56 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.scss @@ -0,0 +1,13 @@ +.toast-container { + position: fixed; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-end; + min-width: 350px; + min-height: 80px; + z-index: 1900; + &.new-on-top { + flex-direction: column-reverse; + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.ts new file mode 100644 index 0000000000..124dcc5185 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.ts @@ -0,0 +1,46 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Toaster } from '../../models/toaster'; +import { toastInOut } from '../../animations/toast.animations'; +import { ToasterService } from '../../services/toaster.service'; + +@Component({ + selector: 'abp-toast-container', + templateUrl: './toast-container.component.html', + styleUrls: ['./toast-container.component.scss'], + animations: [toastInOut], +}) +export class ToastContainerComponent implements OnInit { + toasts = [] as Toaster.Toast[]; + + @Input() + top: string; + + @Input() + right = '30px'; + + @Input() + bottom = '30px'; + + @Input() + left: string; + + @Input() + toastKey: string; + + constructor(private toastService: ToasterService) {} + + ngOnInit() { + this.toastService.toasts$.subscribe(toasts => { + this.toasts = this.toastKey + ? toasts.filter(t => { + return t.options && t.options.containerKey !== this.toastKey; + }) + : toasts; + }); + } + + trackByFunc(index, toast) { + if (!toast) return null; + return toast.options.id; + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.html b/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.html new file mode 100644 index 0000000000..985ea58888 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.html @@ -0,0 +1,16 @@ +
+
+ +
+
+ +
+ {{ toast.title | abpLocalization: toast.options?.titleLocalizationParams }} +
+
+ {{ toast.message | abpLocalization: toast.options?.messageLocalizationParams }} +
+
+
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.scss new file mode 100644 index 0000000000..1bd780815c --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.scss @@ -0,0 +1,80 @@ +@mixin fillColor($background, $color) { + border: 2px solid $background; + background-color: $background; + color: $color; + box-shadow: 0 0 10px -5px rgba(#000, 0.4); + &:hover { + border: 2px solid darken($background, 5); + background-color: darken($background, 5); + box-shadow: 0 0 15px -5px rgba(#000, 0.4); + } +} + +.toast { + display: grid; + grid-template-columns: 50px 1fr; + gap: 10px; + margin: 5px 0; + padding: 10px; + border-radius: 0px; + width: 350px; + user-select: none; + box-shadow: 0 0 10px -5px rgba(#000, 0.4); + z-index: 9999; + @include fillColor(#f0f0f0, #000); + opacity: 1; + &.toast-success { + @include fillColor(#51a351, #fff); + } + &.toast-info { + @include fillColor(#2f96b4, #fff); + } + &.toast-warning { + @include fillColor(#f89406, #fff); + } + &.toast-error { + @include fillColor(#bd362f, #fff); + } + .toast-icon { + display: flex; + align-items: center; + justify-content: center; + .icon { + font-size: 36px; + } + } + .toast-content { + position: relative; + .close-button { + position: absolute; + top: 0; + right: 0; + display: flex; + align-items: center; + justify-content: center; + margin: 0; + padding: 5px 10px 5px 5px; + width: 25px; + height: 25px; + border: none; + border-radius: 50%; + background: transparent; + &:focus { + outline: none; + } + .close-icon { + width: 16px; + height: 16px; + stroke: #000; + } + } + .toast-title { + margin: 0; + padding: 0; + font-size: 1rem; + font-weight: 600; + } + .toast-message { + } + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts index 5d21a535e8..b4465934b6 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts @@ -1,26 +1,56 @@ -import { Component } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; +import { Toaster } from '../../models/toaster'; +import { ToasterService } from '../../services/toaster.service'; +import { LocalizationService } from '@abp/ng.core'; +import snq from 'snq'; @Component({ selector: 'abp-toast', - // tslint:disable-next-line: component-max-inline-declarations - template: ` - - - -
-
{{ message.summary | abpLocalization: message.titleLocalizationParams }}
-
{{ message.detail | abpLocalization: message.messageLocalizationParams }}
-
-
-
- `, + templateUrl: './toast.component.html', + styleUrls: ['./toast.component.scss'], }) -export class ToastComponent {} +export class ToastComponent implements OnInit { + @Input() + toast: Toaster.Toast; + + get severityClass(): string { + if (!this.toast || !this.toast.severity) return ''; + return `toast-${this.toast.severity}`; + } + + get iconClass(): string { + switch (this.toast.severity) { + case 'success': + return 'fa-check-circle'; + case 'info': + return 'fa-info-circle'; + case 'warning': + return 'fa-exclamation-triangle'; + case 'error': + return 'fa-times-circle'; + default: + return 'fa-exclamation-circle'; + } + } + + constructor( + private toastService: ToasterService, + private localizationService: LocalizationService, + ) {} + + ngOnInit() { + if (snq(() => this.toast.options.sticky)) return; + const timeout = snq(() => this.toast.options.life) || 5000; + setTimeout(() => { + this.close(); + }, timeout); + } + + close() { + this.toastService.remove(this.toast.options.id); + } + + tap() { + if (this.toast.options && this.toast.options.tapToDismiss) this.close(); + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/constants/styles.ts b/npm/ng-packs/packages/theme-shared/src/lib/constants/styles.ts index 24f7a9a8ea..4458117d16 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/constants/styles.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/constants/styles.ts @@ -41,30 +41,6 @@ export default ` background: #8a8686; } -.modal.show { - display: block !important; -} - -.modal-backdrop { - background-color: rgba(0, 0, 0, 0.6); -} - -.modal::-webkit-scrollbar { - width: 7px; -} - -.modal::-webkit-scrollbar-track { - background: #ddd; -} - -.modal::-webkit-scrollbar-thumb { - background: #8a8686; -} - -.modal-dialog { - z-index: 1050; -} - .abp-ellipsis-inline { display: inline-block; overflow: hidden; @@ -78,112 +54,6 @@ export default ` white-space: nowrap; } -.abp-toast .ui-toast-message { - box-sizing: border-box; - border: 2px solid transparent; - border-radius: 4px; - color: #1b1d29; -} - -.abp-toast .ui-toast-message-content { - padding: 10px; -} - -.abp-toast .ui-toast-message-content .ui-toast-icon { - top: 0; - left: 0; - padding: 10px; -} - -.abp-toast .ui-toast-summary { - margin: 0; - font-weight: 700; -} - -body abp-toast .ui-toast .ui-toast-message.ui-toast-message-error { - border: 2px solid #ba1659; - background-color: #f4f4f7; -} - -body abp-toast .ui-toast .ui-toast-message.ui-toast-message-error .ui-toast-message-content .ui-toast-icon { - color: #ba1659; -} - -body abp-toast .ui-toast .ui-toast-message.ui-toast-message-warn { - border: 2px solid #ed5d98; - background-color: #f4f4f7; -} - -body abp-toast .ui-toast .ui-toast-message.ui-toast-message-warn .ui-toast-message-content .ui-toast-icon { - color: #ed5d98; -} - -body abp-toast .ui-toast .ui-toast-message.ui-toast-message-success { - border: 2px solid #1c9174; - background-color: #f4f4f7; -} - -body abp-toast .ui-toast .ui-toast-message.ui-toast-message-success .ui-toast-message-content .ui-toast-icon { - color: #1c9174; -} - -body abp-toast .ui-toast .ui-toast-message.ui-toast-message-info { - border: 2px solid #fccb31; - background-color: #f4f4f7; -} - -body abp-toast .ui-toast .ui-toast-message.ui-toast-message-info .ui-toast-message-content .ui-toast-icon { - color: #fccb31; -} - -.abp-confirm .ui-toast-message { - box-sizing: border-box; - padding: 0px; - border:0 none; - border-radius: 4px; - background-color: transparent !important; - font-family: "Poppins", sans-serif; - text-align: center; -} - -.abp-confirm .ui-toast-message-content { - padding: 0px; -} - -.abp-confirm .abp-confirm-icon { - margin: 32px 50px 5px !important; - color: #f8bb86 !important; - font-size: 52px !important; -} - -.abp-confirm .ui-toast-close-icon { - display: none !important; -} - -.abp-confirm .abp-confirm-summary { - display: block !important; - margin-bottom: 13px !important; - padding: 13px 16px 0px !important; - font-weight: 600 !important; - font-size: 18px !important; -} - -.abp-confirm .abp-confirm-body { - display: inline-block !important; - padding: 0px 10px !important; -} - -.abp-confirm .abp-confirm-footer { - display: block; - margin-top: 30px; - padding: 16px; - text-align: right; -} - -.abp-confirm .abp-confirm-footer .btn { - margin-left: 10px !important; -} - .ui-widget-overlay { z-index: 1000; } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts b/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts index 3249860214..40925c1b2c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts @@ -1,11 +1,23 @@ -import { Toaster } from './toaster'; import { Config } from '@abp/ng.core'; export namespace Confirmation { - export interface Options extends Toaster.Options { + export interface Options { + id?: any; + closable?: boolean; + messageLocalizationParams?: string[]; + titleLocalizationParams?: string[]; hideCancelBtn?: boolean; hideYesBtn?: boolean; cancelText?: Config.LocalizationParam; yesText?: Config.LocalizationParam; } + + export interface DialogData { + message: Config.LocalizationParam; + title?: Config.LocalizationParam; + severity?: Severity; + options?: Partial; + } + + export type Severity = 'neutral' | 'success' | 'info' | 'warning' | 'error'; } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/models/toaster.ts b/npm/ng-packs/packages/theme-shared/src/lib/models/toaster.ts index f029911dae..34fe04f351 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/models/toaster.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/models/toaster.ts @@ -1,17 +1,27 @@ +import { Config } from '@abp/ng.core'; + export namespace Toaster { - export interface Options { - id?: any; - closable?: boolean; + export interface ToastOptions { life?: number; sticky?: boolean; - data?: any; + closable?: boolean; + tapToDismiss?: boolean; messageLocalizationParams?: string[]; titleLocalizationParams?: string[]; + id: any; + containerKey?: string; + } + + export interface Toast { + message: Config.LocalizationParam; + title?: Config.LocalizationParam; + severity?: string; + options?: ToastOptions; } - export type Severity = 'success' | 'info' | 'warn' | 'error'; + export type Severity = 'neutral' | 'success' | 'info' | 'warning' | 'error'; - export const enum Status { + export enum Status { confirm = 'confirm', reject = 'reject', dismiss = 'dismiss', diff --git a/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts b/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts index cceedc22cb..650019392b 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts @@ -1,43 +1,73 @@ import { Injectable } from '@angular/core'; -import { AbstractToaster } from '../abstracts/toaster'; import { Confirmation } from '../models/confirmation'; -import { MessageService } from 'primeng/components/common/messageservice'; -import { fromEvent, Observable, Subject } from 'rxjs'; +import { fromEvent, Observable, Subject, ReplaySubject } from 'rxjs'; import { takeUntil, debounceTime, filter } from 'rxjs/operators'; import { Toaster } from '../models/toaster'; +import { Config } from '@abp/ng.core'; @Injectable({ providedIn: 'root' }) -export class ConfirmationService extends AbstractToaster { - key = 'abpConfirmation'; +export class ConfirmationService { + status$: Subject; + confirmation$ = new ReplaySubject(1); - sticky = true; + info( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, + ): Observable { + return this.show(message, title, 'info', options); + } - destroy$ = new Subject(); + success( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, + ): Observable { + return this.show(message, title, 'success', options); + } - constructor(protected messageService: MessageService) { - super(messageService); + warn( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, + ): Observable { + return this.show(message, title, 'warning', options); + } + + error( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, + ): Observable { + return this.show(message, title, 'error', options); } show( - message: string, - title: string, - severity: Toaster.Severity, - options?: Confirmation.Options, + message: Config.LocalizationParam, + title: Config.LocalizationParam, + severity?: Toaster.Severity, + options?: Partial, ): Observable { + this.confirmation$.next({ + message, + title: title || 'AbpUi:AreYouSure', + severity: severity || 'neutral', + options, + }); + this.status$ = new Subject(); this.listenToEscape(); - return super.show(message, title, severity, options); + return this.status$; } clear(status?: Toaster.Status) { - super.clear(status); - - this.destroy$.next(); + this.confirmation$.next(); + this.status$.next(status || Toaster.Status.dismiss); } listenToEscape() { fromEvent(document, 'keyup') .pipe( - takeUntil(this.destroy$), + takeUntil(this.status$), debounceTime(150), filter((key: KeyboardEvent) => key && key.key === 'Escape'), ) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/services/toaster.service.ts b/npm/ng-packs/packages/theme-shared/src/lib/services/toaster.service.ts index 9e5858007a..021aa60273 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/services/toaster.service.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/services/toaster.service.ts @@ -1,15 +1,116 @@ import { Injectable } from '@angular/core'; -import { AbstractToaster } from '../abstracts/toaster'; -import { Message } from 'primeng/components/common/message'; -import { MessageService } from 'primeng/components/common/messageservice'; +import { Toaster } from '../models'; +import { ReplaySubject } from 'rxjs'; +import { Config } from '@abp/ng.core'; +import snq from 'snq'; -@Injectable({ providedIn: 'root' }) -export class ToasterService extends AbstractToaster { - constructor(protected messageService: MessageService) { - super(messageService); +@Injectable({ + providedIn: 'root', +}) +export class ToasterService { + toasts$ = new ReplaySubject(1); + + private lastId = -1; + + private toasts = [] as Toaster.Toast[]; + + /** + * Creates an info toast with given parameters. + * @param message Content of the toast + * @param title Title of the toast + * @param options Spesific style or structural options for individual toast + */ + info( + message: Config.LocalizationParam, + title?: Config.LocalizationParam, + options?: Partial, + ) { + return this.show(message, title, 'info', options); + } + + /** + * Creates a success toast with given parameters. + * @param message Content of the toast + * @param title Title of the toast + * @param options Spesific style or structural options for individual toast + */ + success( + message: Config.LocalizationParam, + title?: Config.LocalizationParam, + options?: Partial, + ) { + return this.show(message, title, 'success', options); + } + + /** + * Creates a warning toast with given parameters. + * @param message Content of the toast + * @param title Title of the toast + * @param options Spesific style or structural options for individual toast + */ + warn( + message: Config.LocalizationParam, + title?: Config.LocalizationParam, + options?: Partial, + ) { + return this.show(message, title, 'warning', options); + } + + /** + * Creates an error toast with given parameters. + * @param message Content of the toast + * @param title Title of the toast + * @param options Spesific style or structural options for individual toast + */ + error( + message: Config.LocalizationParam, + title?: Config.LocalizationParam, + options?: Partial, + ) { + return this.show(message, title, 'error', options); + } + + /** + * Creates a toast with given parameters. + * @param message Content of the toast + * @param title Title of the toast + * @param severity Sets color of the toast. "success", "warning" etc. + * @param options Spesific style or structural options for individual toast + */ + + show( + message: Config.LocalizationParam, + title: Config.LocalizationParam = null, + severity: Toaster.Severity = 'neutral', + options = {} as Partial, + ) { + const id = ++this.lastId; + this.toasts.push({ + message, + title, + severity, + options: { closable: true, id, ...options }, + }); + this.toasts$.next(this.toasts); + return id; + } + + /** + * Removes the toast with given id. + * @param id ID of the toast to be removed. + */ + remove(id: number) { + this.toasts = this.toasts.filter(toast => snq(() => toast.options.id) !== id); + this.toasts$.next(this.toasts); } - addAll(messages: Message[]): void { - this.messageService.addAll(messages.map(message => ({ key: this.key, ...message }))); + /** + * Removes all open toasts at once. + */ + clear(key?: string) { + this.toasts = !key + ? [] + : this.toasts.filter(toast => snq(() => toast.options.containerKey) !== key); + this.toasts$.next(this.toasts); } } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts index c810df9367..aa2481419d 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts @@ -33,45 +33,44 @@ describe('ConfirmationService', () => { service = spectator.get(ConfirmationService); }); - it('should display a confirmation popup', () => { + test('should display a confirmation popup', () => { service.info('test', 'title'); spectator.detectChanges(); - expect(spectator.query('p-toast')).toBeTruthy(); - expect(spectator.query('p-toastitem')).toBeTruthy(); - expect(spectator.query('div.abp-confirm-summary')).toHaveText('title'); - expect(spectator.query('div.abp-confirm-body')).toHaveText('test'); + expect(spectator.query('div.confirmation .title')).toHaveText('title'); + expect(spectator.query('div.confirmation .message')).toHaveText('test'); }); - it('should close with ESC key', done => { - service.info('test', 'title'); - spectator.detectChanges(); + test('should close with ESC key', done => { + service.info('test', 'title').subscribe(() => { + setTimeout(() => { + spectator.detectComponentChanges(); + expect(spectator.query('div.confirmation')).toBeFalsy(); + done(); + }, 0); + }); - expect(spectator.query('p-toastitem')).toBeTruthy(); + spectator.detectChanges(); + expect(spectator.query('div.confirmation')).toBeTruthy(); + spectator.dispatchKeyboardEvent('div.confirmation', 'keyup', 'Escape'); + }); - spectator.dispatchKeyboardEvent('abp-confirmation', 'keyup', 'Escape'); - service.destroy$.subscribe(() => { - // expect(spectator.query('p-toastitem')).toBeFalsy(); + test('should close when click cancel button', done => { + service.info('test', 'title', { yesText: 'Sure', cancelText: 'Exit' }).subscribe(() => { spectator.detectComponentChanges(); - expect(spectator.query('p-toastitem')).toBeFalsy(); - done(); + setTimeout(() => { + expect(spectator.query('div.confirmation')).toBeFalsy(); + done(); + }, 0); }); - }); - it('should close when click cancel button', done => { - service.info('test', 'title', { yesText: 'Sure', cancelText: 'Exit' }); spectator.detectChanges(); - expect(spectator.query('p-toastitem')).toBeTruthy(); + expect(spectator.query('div.confirmation')).toBeTruthy(); expect(spectator.query('button#cancel')).toHaveText('Exit'); expect(spectator.query('button#confirm')).toHaveText('Sure'); - service.status$.subscribe(() => { - spectator.detectComponentChanges(); - expect(spectator.query('p-toastitem')).toBeFalsy(); - done(); - }); spectator.click('button#cancel'); }); }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts index 1be1e31d1c..aa22e9c567 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts @@ -73,10 +73,10 @@ describe('ErrorHandler', () => { it('should display the confirmation when not found error occurs', () => { store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 404 }))); spectator.detectChanges(); - expect(spectator.query('.abp-confirm-summary')).toHaveText( + expect(spectator.query('.confirmation .title')).toHaveText( DEFAULT_ERROR_MESSAGES.defaultError404.title, ); - expect(spectator.query('.abp-confirm-body')).toHaveText( + expect(spectator.query('.confirmation .message')).toHaveText( DEFAULT_ERROR_MESSAGES.defaultError404.details, ); }); @@ -84,10 +84,10 @@ describe('ErrorHandler', () => { it('should display the confirmation when default error occurs', () => { store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 412 }))); spectator.detectChanges(); - expect(spectator.query('.abp-confirm-summary')).toHaveText( + expect(spectator.query('.confirmation .title')).toHaveText( DEFAULT_ERROR_MESSAGES.defaultError.title, ); - expect(spectator.query('.abp-confirm-body')).toHaveText( + expect(spectator.query('.confirmation .message')).toHaveText( DEFAULT_ERROR_MESSAGES.defaultError.details, ); }); @@ -128,8 +128,8 @@ describe('ErrorHandler', () => { ); spectator.detectChanges(); - expect(spectator.query('.abp-confirm-summary')).toHaveText('test message'); - expect(spectator.query('.abp-confirm-body')).toHaveText('test detail'); + expect(spectator.query('.title')).toHaveText('test message'); + expect(spectator.query('.confirmation .message')).toHaveText('test detail'); }); }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts index 08656ff2d7..ead6d29a23 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts @@ -5,14 +5,18 @@ import { MessageService } from 'primeng/components/common/messageservice'; import { ToastModule } from 'primeng/toast'; import { timer } from 'rxjs'; import { ButtonComponent, ConfirmationComponent, ModalComponent } from '../components'; +import { RouterTestingModule } from '@angular/router/testing'; describe('ModalComponent', () => { - let spectator: SpectatorHost; + let spectator: SpectatorHost< + ModalComponent, + { visible: boolean; busy: boolean; ngDirty: boolean } + >; let appearFn; let disappearFn; const createHost = createHostFactory({ component: ModalComponent, - imports: [ToastModule], + imports: [ToastModule, RouterTestingModule], declarations: [ConfirmationComponent, LocalizationPipe, ButtonComponent], providers: [MessageService], mocks: [Store], @@ -82,7 +86,7 @@ describe('ModalComponent', () => { spectator.click('#abp-modal-close-button'); expect(disappearFn).not.toHaveBeenCalled(); - expect(spectator.query('p-toast')).toBeTruthy(); + expect(spectator.query('div.confirmation')).toBeTruthy(); spectator.click('button#cancel'); expect(spectator.query('div.modal')).toBeTruthy(); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts index 93272e4d30..d7fbd7219e 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts @@ -11,7 +11,7 @@ import { OAuthService } from 'angular-oauth2-oidc'; @Component({ selector: 'abp-dummy', template: ` - + `, }) class DummyComponent { @@ -33,58 +33,57 @@ describe('ToasterService', () => { service = spectator.get(ToasterService); }); - it('should display an error toast', () => { + test('should display an error toast', () => { service.error('test', 'title'); spectator.detectChanges(); - expect(spectator.query('p-toast')).toBeTruthy(); - expect(spectator.query('p-toastitem')).toBeTruthy(); - expect(spectator.query('span.ui-toast-icon')).toHaveClass('pi-times'); - expect(spectator.query('div.ui-toast-summary')).toHaveText('title'); - expect(spectator.query('div.ui-toast-detail')).toHaveText('test'); + expect(spectator.query('div.toast')).toBeTruthy(); + expect(spectator.query('.toast-icon i')).toHaveClass('fa-times-circle'); + expect(spectator.query('div.toast-title')).toHaveText('title'); + expect(spectator.query('div.toast-message')).toHaveText('test'); }); - it('should display a warning toast', () => { + test('should display a warning toast', () => { service.warn('test', 'title'); spectator.detectChanges(); - expect(spectator.query('span.ui-toast-icon')).toHaveClass('pi-exclamation-triangle'); + expect(spectator.query('.toast-icon i')).toHaveClass('fa-exclamation-triangle'); }); - it('should display a success toast', () => { + test('should display a success toast', () => { service.success('test', 'title'); spectator.detectChanges(); - expect(spectator.query('span.ui-toast-icon')).toHaveClass('pi-check'); + expect(spectator.query('.toast-icon i')).toHaveClass('fa-check-circle'); }); - it('should display an info toast', () => { + test('should display an info toast', () => { service.info('test', 'title'); spectator.detectChanges(); - expect(spectator.query('span.ui-toast-icon')).toHaveClass('pi-info-circle'); + expect(spectator.query('.toast-icon i')).toHaveClass('fa-info-circle'); }); - it('should display multiple toasts', () => { - service.addAll([ - { summary: 'summary1', detail: 'detail1' }, - { summary: 'summary2', detail: 'detail2' }, - ]); + test('should display multiple toasts', () => { + service.info('detail1', 'summary1'); + service.info('detail2', 'summary2'); + spectator.detectChanges(); - expect( - spectator.queryAll('div.ui-toast-summary').map(node => node.textContent.trim()), - ).toEqual(['summary1', 'summary2']); - expect(spectator.queryAll('div.ui-toast-detail').map(node => node.textContent.trim())).toEqual([ + expect(spectator.queryAll('div.toast-title').map(node => node.textContent.trim())).toEqual([ + 'summary1', + 'summary2', + ]); + expect(spectator.queryAll('div.toast-message').map(node => node.textContent.trim())).toEqual([ 'detail1', 'detail2', ]); }); - it('should remove the opened toast', () => { + test('should remove the opened toasts', () => { service.info('test', 'title'); spectator.detectChanges(); - expect(spectator.query('p-toastitem')).toBeTruthy(); + expect(spectator.query('div.toast')).toBeTruthy(); service.clear(); spectator.detectChanges(); - expect(spectator.query('p-toastitem')).toBeFalsy(); + expect(spectator.query('p-div.toast')).toBeFalsy(); }); }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index c67f553d37..69d719fe08 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -14,6 +14,7 @@ import { LoaderBarComponent } from './components/loader-bar/loader-bar.component import { ModalComponent } from './components/modal/modal.component'; import { SortOrderIconComponent } from './components/sort-order-icon/sort-order-icon.component'; import { TableEmptyMessageComponent } from './components/table-empty-message/table-empty-message.component'; +import { ToastContainerComponent } from './components/toast-container/toast-container.component'; import { TableComponent } from './components/table/table.component'; import { ToastComponent } from './components/toast/toast.component'; import styles from './constants/styles'; @@ -53,6 +54,7 @@ export function appendScript(injector: Injector) { TableComponent, TableEmptyMessageComponent, ToastComponent, + ToastContainerComponent, SortOrderIconComponent, LoadingDirective, TableSortDirective, @@ -69,6 +71,7 @@ export function appendScript(injector: Injector) { TableComponent, TableEmptyMessageComponent, ToastComponent, + ToastContainerComponent, SortOrderIconComponent, LoadingDirective, TableSortDirective, diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index eeac13eac8..a582f8c115 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -40,6 +40,7 @@ $projects = ( "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets", + "framework/src/Volo.Abp.AspNetCore.Serilog", "framework/src/Volo.Abp.AspNetCore.TestBase", "framework/src/Volo.Abp.Auditing", "framework/src/Volo.Abp.Authorization", diff --git a/samples/BookStore-Angular-MongoDb/aspnet-core/src/Acme.BookStore.HttpApi.Host/Acme.BookStore.HttpApi.Host.csproj b/samples/BookStore-Angular-MongoDb/aspnet-core/src/Acme.BookStore.HttpApi.Host/Acme.BookStore.HttpApi.Host.csproj index 46638e1958..ad0861edc7 100644 --- a/samples/BookStore-Angular-MongoDb/aspnet-core/src/Acme.BookStore.HttpApi.Host/Acme.BookStore.HttpApi.Host.csproj +++ b/samples/BookStore-Angular-MongoDb/aspnet-core/src/Acme.BookStore.HttpApi.Host/Acme.BookStore.HttpApi.Host.csproj @@ -10,7 +10,7 @@ - + diff --git a/samples/BookStore-Modular/application/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj b/samples/BookStore-Modular/application/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj index b71bc65d91..9d4115179c 100644 --- a/samples/BookStore-Modular/application/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj +++ b/samples/BookStore-Modular/application/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj @@ -32,7 +32,7 @@ - + diff --git a/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.HttpApi.Host/Acme.BookStore.BookManagement.HttpApi.Host.csproj b/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.HttpApi.Host/Acme.BookStore.BookManagement.HttpApi.Host.csproj index db35047e19..60f118bc93 100644 --- a/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.HttpApi.Host/Acme.BookStore.BookManagement.HttpApi.Host.csproj +++ b/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.HttpApi.Host/Acme.BookStore.BookManagement.HttpApi.Host.csproj @@ -10,7 +10,7 @@ - + diff --git a/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.IdentityServer/Acme.BookStore.BookManagement.IdentityServer.csproj b/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.IdentityServer/Acme.BookStore.BookManagement.IdentityServer.csproj index c0eebd425a..d9b39f407e 100644 --- a/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.IdentityServer/Acme.BookStore.BookManagement.IdentityServer.csproj +++ b/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.IdentityServer/Acme.BookStore.BookManagement.IdentityServer.csproj @@ -8,7 +8,7 @@ - + diff --git a/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.Web.Host/Acme.BookStore.BookManagement.Web.Host.csproj b/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.Web.Host/Acme.BookStore.BookManagement.Web.Host.csproj index 0534b56365..1df46016b4 100644 --- a/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.Web.Host/Acme.BookStore.BookManagement.Web.Host.csproj +++ b/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.Web.Host/Acme.BookStore.BookManagement.Web.Host.csproj @@ -10,7 +10,7 @@ - + diff --git a/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.Web.Unified/Acme.BookStore.BookManagement.Web.Unified.csproj b/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.Web.Unified/Acme.BookStore.BookManagement.Web.Unified.csproj index 9fb034d5fe..04993f9b69 100644 --- a/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.Web.Unified/Acme.BookStore.BookManagement.Web.Unified.csproj +++ b/samples/BookStore-Modular/modules/book-management/host/Acme.BookStore.BookManagement.Web.Unified/Acme.BookStore.BookManagement.Web.Unified.csproj @@ -10,7 +10,7 @@ - + diff --git a/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs b/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs index a97917f774..80488be0e8 100644 --- a/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs +++ b/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.AutoMapper; +using Volo.Abp.Account; +using Volo.Abp.AutoMapper; using Volo.Abp.FeatureManagement; using Volo.Abp.Identity; using Volo.Abp.Modularity; @@ -10,6 +11,7 @@ namespace Acme.BookStore [DependsOn( typeof(BookStoreDomainModule), typeof(BookStoreApplicationContractsModule), + typeof(AbpAccountApplicationModule), typeof(AbpIdentityApplicationModule), typeof(AbpPermissionManagementApplicationModule), typeof(AbpTenantManagementApplicationModule), diff --git a/samples/BookStore/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj b/samples/BookStore/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj index 153b6f38e3..832114ce3b 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj +++ b/samples/BookStore/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj @@ -23,7 +23,7 @@ - + diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemo.Web.csproj b/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemo.Web.csproj index 466a7359bc..f00f144407 100644 --- a/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemo.Web.csproj +++ b/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemo.Web.csproj @@ -31,7 +31,7 @@ - + diff --git a/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServer.Host.csproj b/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServer.Host.csproj index 337657a831..47bf7813f8 100644 --- a/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServer.Host.csproj +++ b/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServer.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/applications/BackendAdminApp.Host/BackendAdminApp.Host.csproj b/samples/MicroserviceDemo/applications/BackendAdminApp.Host/BackendAdminApp.Host.csproj index 911593b16a..a22e073228 100644 --- a/samples/MicroserviceDemo/applications/BackendAdminApp.Host/BackendAdminApp.Host.csproj +++ b/samples/MicroserviceDemo/applications/BackendAdminApp.Host/BackendAdminApp.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/PublicWebSite.Host.csproj b/samples/MicroserviceDemo/applications/PublicWebSite.Host/PublicWebSite.Host.csproj index 5574e33b3b..3b4780b2cd 100644 --- a/samples/MicroserviceDemo/applications/PublicWebSite.Host/PublicWebSite.Host.csproj +++ b/samples/MicroserviceDemo/applications/PublicWebSite.Host/PublicWebSite.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj b/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj index 9d2d4c3cae..554555940d 100644 --- a/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj +++ b/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGateway.Host.csproj b/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGateway.Host.csproj index 8d7d8bf135..73479d93ad 100644 --- a/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGateway.Host.csproj +++ b/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGateway.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGateway.Host.csproj b/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGateway.Host.csproj index 2c01555e87..8ece4fe5f4 100644 --- a/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGateway.Host.csproj +++ b/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGateway.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingService.Host.csproj b/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingService.Host.csproj index 5a9c5375d0..cd5bbc8b01 100644 --- a/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingService.Host.csproj +++ b/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingService.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/microservices/IdentityService.Host/IdentityService.Host.csproj b/samples/MicroserviceDemo/microservices/IdentityService.Host/IdentityService.Host.csproj index 09e91582ab..b9dd199a6c 100644 --- a/samples/MicroserviceDemo/microservices/IdentityService.Host/IdentityService.Host.csproj +++ b/samples/MicroserviceDemo/microservices/IdentityService.Host/IdentityService.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/microservices/ProductService.Host/ProductService.Host.csproj b/samples/MicroserviceDemo/microservices/ProductService.Host/ProductService.Host.csproj index f871ed5983..dc4f6aface 100644 --- a/samples/MicroserviceDemo/microservices/ProductService.Host/ProductService.Host.csproj +++ b/samples/MicroserviceDemo/microservices/ProductService.Host/ProductService.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj index 19435bb1fa..d41d991542 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj @@ -10,7 +10,7 @@ - + @@ -20,6 +20,7 @@ + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs index 59ba519ce4..401c3641fc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs @@ -15,6 +15,7 @@ using Microsoft.OpenApi.Models; using Volo.Abp; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy; +using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Autofac; using Volo.Abp.Caching; using Volo.Abp.Localization; @@ -28,7 +29,8 @@ namespace MyCompanyName.MyProjectName typeof(AbpAutofacModule), typeof(AbpAspNetCoreMvcUiMultiTenancyModule), typeof(MyProjectNameApplicationModule), - typeof(MyProjectNameEntityFrameworkCoreDbMigrationsModule) + typeof(MyProjectNameEntityFrameworkCoreDbMigrationsModule), + typeof(AbpAspNetCoreSerilogModule) )] public class MyProjectNameHttpApiHostModule : AbpModule { @@ -178,6 +180,7 @@ namespace MyCompanyName.MyProjectName }); app.UseAuditing(); + app.UseAbpSerilogEnrichers(); app.UseMvcWithDefaultRouteAndArea(); } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj index 038e2e4016..9c7f66798e 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj @@ -10,7 +10,7 @@ - + @@ -20,6 +20,7 @@ + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs index a60d2ddb35..beeca41292 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs @@ -18,6 +18,7 @@ using Volo.Abp.Account.Web; using Volo.Abp.AspNetCore.Authentication.JwtBearer; using Volo.Abp.AspNetCore.MultiTenancy; using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Autofac; using Volo.Abp.Localization; using Volo.Abp.Modularity; @@ -34,7 +35,8 @@ namespace MyCompanyName.MyProjectName typeof(MyProjectNameEntityFrameworkCoreDbMigrationsModule), typeof(AbpAspNetCoreMvcUiBasicThemeModule), typeof(AbpAspNetCoreAuthenticationJwtBearerModule), - typeof(AbpAccountWebIdentityServerModule) + typeof(AbpAccountWebIdentityServerModule), + typeof(AbpAspNetCoreSerilogModule) )] public class MyProjectNameHttpApiHostModule : AbpModule { @@ -172,6 +174,7 @@ namespace MyCompanyName.MyProjectName }); app.UseAuditing(); + app.UseAbpSerilogEnrichers(); app.UseMvcWithDefaultRouteAndArea(); } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj index aecc6831be..6ff7bde240 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj @@ -32,7 +32,7 @@ - + @@ -43,6 +43,8 @@ + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs index 2e2211791e..1dfe7f56eb 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs @@ -18,6 +18,7 @@ using Volo.Abp.AspNetCore.Mvc.UI; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Auditing; using Volo.Abp.Autofac; using Volo.Abp.BackgroundJobs; @@ -35,7 +36,8 @@ namespace MyCompanyName.MyProjectName typeof(AbpAccountWebIdentityServerModule), typeof(AbpAccountApplicationModule), typeof(AbpAspNetCoreMvcUiBasicThemeModule), - typeof(MyProjectNameEntityFrameworkCoreDbMigrationsModule) + typeof(MyProjectNameEntityFrameworkCoreDbMigrationsModule), + typeof(AbpAspNetCoreSerilogModule) )] public class MyProjectNameIdentityServerModule : AbpModule { @@ -149,6 +151,7 @@ namespace MyCompanyName.MyProjectName app.UseAuthorization(); app.UseAbpRequestLocalization(); app.UseAuditing(); + app.UseAbpSerilogEnrichers(); app.UseMvcWithDefaultRouteAndArea(); } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj index 66864acc82..0bff1f34fc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj @@ -16,7 +16,7 @@ - + @@ -29,6 +29,8 @@ + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs index 28afed0c7c..d2b050644f 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs @@ -22,6 +22,7 @@ using Volo.Abp.AspNetCore.Mvc.UI; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Autofac; using Volo.Abp.AutoMapper; using Volo.Abp.Caching; @@ -49,7 +50,8 @@ namespace MyCompanyName.MyProjectName.Web typeof(AbpFeatureManagementWebModule), typeof(AbpHttpClientIdentityModelModule), typeof(AbpIdentityWebModule), - typeof(AbpTenantManagementWebModule) + typeof(AbpTenantManagementWebModule), + typeof(AbpAspNetCoreSerilogModule) )] public class MyProjectNameWebModule : AbpModule { @@ -241,7 +243,7 @@ namespace MyCompanyName.MyProjectName.Web }); app.UseAuditing(); - + app.UseAbpSerilogEnrichers(); app.UseMvcWithDefaultRouteAndArea(); } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj index 9540447c74..db3998cc62 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj @@ -32,7 +32,7 @@ - + @@ -47,6 +47,7 @@ + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs index 87626719f3..697abb9ce2 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs @@ -23,6 +23,7 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Autofac; using Volo.Abp.AutoMapper; using Volo.Abp.FeatureManagement; @@ -47,7 +48,8 @@ namespace MyCompanyName.MyProjectName.Web typeof(AbpAccountWebIdentityServerModule), typeof(AbpAspNetCoreMvcUiBasicThemeModule), typeof(AbpAspNetCoreAuthenticationJwtBearerModule), - typeof(AbpTenantManagementWebModule) + typeof(AbpTenantManagementWebModule), + typeof(AbpAspNetCoreSerilogModule) )] public class MyProjectNameWebModule : AbpModule { @@ -198,7 +200,6 @@ namespace MyCompanyName.MyProjectName.Web { app.UseErrorPage(); } - app.UseVirtualFiles(); app.UseRouting(); app.UseAuthentication(); @@ -208,18 +209,16 @@ namespace MyCompanyName.MyProjectName.Web { app.UseMultiTenancy(); } - app.UseIdentityServer(); app.UseAuthorization(); app.UseAbpRequestLocalization(); - app.UseSwagger(); app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "MyProjectName API"); }); - app.UseAuditing(); + app.UseAbpSerilogEnrichers(); app.UseMvcWithDefaultRouteAndArea(); } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj index fe10668840..f041445109 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj @@ -10,7 +10,7 @@ - + @@ -19,6 +19,7 @@ + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs index f48706663e..8ebec4ad9b 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs @@ -15,6 +15,7 @@ using Swashbuckle.AspNetCore.Swagger; using Volo.Abp; using Volo.Abp.AspNetCore.MultiTenancy; using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy; +using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.AuditLogging.EntityFrameworkCore; using Volo.Abp.Autofac; using Volo.Abp.Caching; @@ -39,7 +40,8 @@ namespace MyCompanyName.MyProjectName typeof(AbpEntityFrameworkCoreSqlServerModule), typeof(AbpAuditLoggingEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), - typeof(AbpSettingManagementEntityFrameworkCoreModule) + typeof(AbpSettingManagementEntityFrameworkCoreModule), + typeof(AbpAspNetCoreSerilogModule) )] public class MyProjectNameHttpApiHostModule : AbpModule { @@ -166,6 +168,7 @@ namespace MyCompanyName.MyProjectName options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support APP API"); }); app.UseAuditing(); + app.UseAbpSerilogEnrichers(); app.UseMvcWithDefaultRouteAndArea(); } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj index b25ce4b603..2815d81262 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj @@ -8,7 +8,7 @@ - + @@ -21,6 +21,7 @@ + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs index 5f0f80c05e..4be099efaa 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs @@ -17,6 +17,7 @@ using Volo.Abp.AspNetCore.Authentication.JwtBearer; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; +using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Auditing; using Volo.Abp.AuditLogging.EntityFrameworkCore; using Volo.Abp.Autofac; @@ -66,7 +67,8 @@ namespace MyCompanyName.MyProjectName typeof(AbpTenantManagementApplicationModule), typeof(AbpTenantManagementHttpApiModule), typeof(AbpAspNetCoreAuthenticationJwtBearerModule), - typeof(MyProjectNameApplicationContractsModule) + typeof(MyProjectNameApplicationContractsModule), + typeof(AbpAspNetCoreSerilogModule) )] public class MyProjectNameIdentityServerModule : AbpModule { @@ -189,6 +191,7 @@ namespace MyCompanyName.MyProjectName options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support APP API"); }); app.UseAuditing(); + app.UseAbpSerilogEnrichers(); app.UseMvcWithDefaultRouteAndArea(); SeedData(context); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj index 663a908ad8..6d6b11231a 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj @@ -10,7 +10,7 @@ - + @@ -51,4 +51,10 @@ + + + ..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Serilog\bin\Debug\netcoreapp3.1\Volo.Abp.AspNetCore.Serilog.dll + + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostModule.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostModule.cs index 473bdab2cf..41fb8fc7f2 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostModule.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostModule.cs @@ -21,6 +21,7 @@ using Volo.Abp.AspNetCore.Mvc.UI; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Autofac; using Volo.Abp.AutoMapper; using Volo.Abp.Caching; @@ -55,7 +56,8 @@ namespace MyCompanyName.MyProjectName typeof(AbpTenantManagementWebModule), typeof(AbpTenantManagementHttpApiClientModule), typeof(AbpFeatureManagementHttpApiClientModule), - typeof(AbpPermissionManagementHttpApiClientModule) + typeof(AbpPermissionManagementHttpApiClientModule), + typeof(AbpAspNetCoreSerilogModule) )] public class MyProjectNameWebHostModule : AbpModule { @@ -249,7 +251,7 @@ namespace MyCompanyName.MyProjectName }); app.UseAuditing(); - + app.UseAbpSerilogEnrichers(); app.UseMvcWithDefaultRouteAndArea(); } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj index 1099836b9c..47f8629dde 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj @@ -10,13 +10,14 @@ - + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyProjectNameWebUnifiedModule.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyProjectNameWebUnifiedModule.cs index 8390297a9f..aa253e89d3 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyProjectNameWebUnifiedModule.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyProjectNameWebUnifiedModule.cs @@ -12,6 +12,7 @@ using Volo.Abp.Account; using Volo.Abp.Account.Web; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.AuditLogging.EntityFrameworkCore; using Volo.Abp.Autofac; using Volo.Abp.Data; @@ -56,7 +57,8 @@ namespace MyCompanyName.MyProjectName typeof(AbpTenantManagementWebModule), typeof(AbpTenantManagementApplicationModule), typeof(AbpTenantManagementEntityFrameworkCoreModule), - typeof(AbpAspNetCoreMvcUiBasicThemeModule) + typeof(AbpAspNetCoreMvcUiBasicThemeModule), + typeof(AbpAspNetCoreSerilogModule) )] public class MyProjectNameWebUnifiedModule : AbpModule { @@ -137,6 +139,7 @@ namespace MyCompanyName.MyProjectName app.UseAbpRequestLocalization(); app.UseAuditing(); + app.UseAbpSerilogEnrichers(); app.UseMvcWithDefaultRouteAndArea(); using (var scope = context.ServiceProvider.CreateScope())