Browse Source

Merge branch 'dev' into eventboxes

pull/10008/head
Halil İbrahim Kalkan 4 years ago
parent
commit
6cf2a5bb85
  1. 2
      .github/workflows/build-and-test.yml
  2. 6
      Directory.Build.props
  3. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en-GB.json
  4. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json
  5. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json
  6. 41
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  7. 5
      common.props
  8. 2
      configureawait.props
  9. 22
      docs/en/Background-Jobs-Hangfire.md
  10. 29
      docs/en/Best-Practices/Entity-Framework-Core-Integration.md
  11. 26
      docs/en/Best-Practices/MongoDB-Integration.md
  12. 46
      docs/en/Community-Articles/2020-08-07-Passwordless-Authentication/POST.md
  13. BIN
      docs/en/Community-Articles/2021-09-25-How-to-Override-Localization-Strings-Of-Depending-Modules/1.png
  14. BIN
      docs/en/Community-Articles/2021-09-25-How-to-Override-Localization-Strings-Of-Depending-Modules/2.png
  15. 88
      docs/en/Community-Articles/2021-09-25-How-to-Override-Localization-Strings-Of-Depending-Modules/POST.md
  16. 9
      docs/en/Road-Map.md
  17. 5
      docs/en/Tutorials/Part-1.md
  18. 5
      docs/en/Tutorials/Part-10.md
  19. 7
      docs/en/Tutorials/Part-2.md
  20. 5
      docs/en/Tutorials/Part-3.md
  21. 5
      docs/en/Tutorials/Part-4.md
  22. 7
      docs/en/Tutorials/Part-5.md
  23. 7
      docs/en/Tutorials/Part-6.md
  24. 5
      docs/en/Tutorials/Part-7.md
  25. 5
      docs/en/Tutorials/Part-8.md
  26. 5
      docs/en/Tutorials/Part-9.md
  27. 67
      docs/en/UI/Angular/How-Replaceable-Components-Work-with-Extensions.md
  28. 276
      docs/en/UI/Angular/Permission-Management-Component-Replacement.md
  29. 6
      docs/zh-Hans/Text-Templating.md
  30. 20
      framework/Volo.Abp.sln
  31. 2
      framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj
  32. 2
      framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo.Abp.AspNetCore.Authentication.OAuth.csproj
  33. 2
      framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo.Abp.AspNetCore.Authentication.OpenIdConnect.csproj
  34. 2
      framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Volo.Abp.AspNetCore.Components.Server.Theming.csproj
  35. 2
      framework/src/Volo.Abp.AspNetCore.Components.Server/Volo.Abp.AspNetCore.Components.Server.csproj
  36. 2
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Volo.Abp.AspNetCore.Components.Web.Theming.csproj
  37. 2
      framework/src/Volo.Abp.AspNetCore.Components.Web/Volo.Abp.AspNetCore.Components.Web.csproj
  38. 2
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.csproj
  39. 2
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj
  40. 2
      framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj
  41. 2
      framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo.Abp.AspNetCore.MultiTenancy.csproj
  42. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj
  43. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs
  44. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.csproj
  45. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions.csproj
  46. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj
  47. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj
  48. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo.Abp.AspNetCore.Mvc.UI.Packages.csproj
  49. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/SweetAlert2/Sweetalert2ScriptContributor.cs
  50. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj
  51. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs
  52. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj
  53. 24
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js
  54. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj
  55. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj
  56. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj
  57. 25
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs
  58. 13
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs
  59. 24
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs
  60. 24
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs
  61. 7
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Response/AbpNoContentActionFilter.cs
  62. 2
      framework/src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj
  63. 2
      framework/src/Volo.Abp.AspNetCore.SignalR/Volo.Abp.AspNetCore.SignalR.csproj
  64. 2
      framework/src/Volo.Abp.AspNetCore.TestBase/Volo.Abp.AspNetCore.TestBase.csproj
  65. 6
      framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/DynamicProxying/AspNetCoreTestProxyHttpClientFactory.cs
  66. 17
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Http/AbpHttpRequestExtensions.cs
  67. 2
      framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj
  68. 7
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpAuthorizationExceptionHandlerOptions.cs
  69. 45
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs
  70. 67
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultAbpAuthorizationExceptionHandler.cs
  71. 11
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IAbpAuthorizationExceptionHandler.cs
  72. 3
      framework/src/Volo.Abp.Auditing.Contracts/FodyWeavers.xml
  73. 30
      framework/src/Volo.Abp.Auditing.Contracts/FodyWeavers.xsd
  74. 21
      framework/src/Volo.Abp.Auditing.Contracts/Volo.Abp.Auditing.Contracts.csproj
  75. 9
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/AbpAuditingContractsModule.cs
  76. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/AuditedAttribute.cs
  77. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/DisableAuditingAttribute.cs
  78. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/EntityChangeType.cs
  79. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IAuditedObject.cs
  80. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IAuditingEnabled.cs
  81. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/ICreationAuditedObject.cs
  82. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IDeletionAuditedObject.cs
  83. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IFullAuditedObject.cs
  84. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasCreationTime.cs
  85. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasDeletionTime.cs
  86. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasModificationTime.cs
  87. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IMayHaveCreator.cs
  88. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IModificationAuditedObject.cs
  89. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IMustHaveCreator.cs
  90. 1
      framework/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj
  91. 3
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
  92. 4
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs
  93. 25
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/PermissionsRequirement.cs
  94. 31
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/PermissionsRequirementHandler.cs
  95. 1
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs
  96. 2
      framework/src/Volo.Abp.Autofac.WebAssembly/Volo.Abp.Autofac.WebAssembly.csproj
  97. 2
      framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj
  98. 6
      framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/AbpRabbitMqBackgroundJobOptions.cs
  99. 22
      framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs
  100. 35
      framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueueConfiguration.cs

2
.github/workflows/build-and-test.yml

@ -21,7 +21,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@master
with:
dotnet-version: 5.0.201
dotnet-version: 6.0.100-rc.1.21458.32
- name: Build All
run: .\build-all.ps1

6
Directory.Build.props

@ -2,10 +2,10 @@
<PropertyGroup>
<!-- All Microsoft packages -->
<MicrosoftPackageVersion>5.0.*</MicrosoftPackageVersion>
<MicrosoftPackageVersion>6.0.0-rc.*</MicrosoftPackageVersion>
<!-- Microsoft.NET.Test.Sdk https://www.nuget.org/packages/Microsoft.NET.Test.Sdk -->
<MicrosoftNETTestSdkPackageVersion>16.9.1</MicrosoftNETTestSdkPackageVersion>
<MicrosoftNETTestSdkPackageVersion>16.11.0</MicrosoftNETTestSdkPackageVersion>
<!-- NSubstitute https://www.nuget.org/packages/NSubstitute -->
<NSubstitutePackageVersion>4.2.2</NSubstitutePackageVersion>
@ -23,7 +23,7 @@
<xUnitRunnerVisualstudioPackageVersion>2.4.3</xUnitRunnerVisualstudioPackageVersion>
<!-- Mongo2Go https://www.nuget.org/packages/Mongo2Go -->
<Mongo2GoPackageVersion>3.1.1</Mongo2GoPackageVersion>
<Mongo2GoPackageVersion>3.1.3</Mongo2GoPackageVersion>
</PropertyGroup>
</Project>

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en-GB.json

@ -9,6 +9,7 @@
"OfficialBlog": "Official blog",
"CommercialHomePage": "Commercial home page",
"CommercialSupportWebSite": "Commercial support web site",
"CommunityWebSite": "ABP community web site"
"CommunityWebSite": "ABP community web site",
"ManageAccount": "My Account | ABP.IO"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json

@ -9,6 +9,7 @@
"OfficialBlog": "Official blog",
"CommercialHomePage": "Commercial home page",
"CommercialSupportWebSite": "Commercial support web site",
"CommunityWebSite": "ABP community web site"
"CommunityWebSite": "ABP community web site",
"ManageAccount": "My Account | ABP.IO"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json

@ -9,6 +9,7 @@
"OfficialBlog": "Resmi blog",
"CommercialHomePage": "Kurumsal ana sayfa",
"CommercialSupportWebSite": "Kurumsal destek web sitesi",
"CommunityWebSite": "ABP topluluk web sitesi"
"CommunityWebSite": "ABP topluluk web sitesi",
"ManageAccount": "Hesabım | ABP.IO"
}
}

41
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -310,6 +310,45 @@
"DeleteInvoice" : "Delete Invoice",
"SuccessfullyDeleted": "Successfully deleted!",
"PaymentStateSetTo" : "Payment state set to {0}",
"ChangeState": "Change State"
"ChangeState": "Change State",
"Permission:TrialLicense" : "Trial License",
"Permission:ManageTrialLicense": "Manage Trial License",
"Menu:TrialLicenses": "Trial Licenses",
"TrialLicenses": "Trial Licenses",
"UserNameFilter": "Username",
"TrialLicenseStatusFilter": "Status",
"TrialLicenseStartDateFilter": "Start date",
"TrialLicenseEndDateFilter": "End date",
"FirsName": "First name",
"LastName": "Last name",
"Status": "Status",
"StartDate": "Start date",
"EndDate": "End date",
"CompanyName": "Company name",
"PurchasedDate": "Purchased date",
"OrganizationDetail": "Organization Detail",
"SendActivationMail": "Send Activation Mail",
"ActivationMailSentSuccessfully": "Activation mail sent successfully!",
"TrialLicenseStatus": "Trial license status",
"TrialLicenseDetail": "Trial License Detail",
"AcceptsMarketingCommunications": "Marketing Communications",
"PurposeOfUsage": "Purpose of usage",
"CountryName": "Country name",
"CompanySize": "Company size",
"DetailTrialLicense": "Details",
"Requested": "Requested",
"Activated": "Activated",
"PurchasedToNormalLicense": "Purchased",
"Expired": "Expired",
"TrialLicenseDeletionWarningMessage": "Trial license and if any organization and qa organization will be deleted!",
"IsTrial": "Is trial",
"Volo.AbpIo.Commercial:030000": "You already used your trial period.",
"Volo.AbpIo.Commercial:030001": "This organization name already exists.",
"Volo.AbpIo.Commercial:030002": "Once activated, trial license cannot be set to requested!",
"Volo.AbpIo.Commercial:030003": "There is no such status!",
"Volo.AbpIo.Commercial:030004": "Status could not be changed due to an unexpected error!",
"Volo.AbpIo.Commercial:030005": "Start date and end date cannot be given when the trial license is in the requested state!",
"Volo.AbpIo.Commercial:030006": "End date must always be greater than start date!",
"Volo.AbpIo.Commercial:030007": "This trial license has already been activated once!"
}
}

5
common.props

@ -28,4 +28,9 @@
</None>
</ItemGroup>
<ItemGroup Condition="$(AssemblyName.EndsWith('HttpApi.Client'))">
<EmbeddedResource Include="**\*generate-proxy.json" />
<Content Remove="**\*generate-proxy.json" />
</ItemGroup>
</Project>

2
configureawait.props

@ -1,7 +1,7 @@
<Project>
<ItemGroup>
<PackageReference Include="ConfigureAwait.Fody" Version="3.3.1" PrivateAssets="All" />
<PackageReference Include="Fody" Version="6.5.0">
<PackageReference Include="Fody" Version="6.5.3">
<PrivateAssets>All</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>

22
docs/en/Background-Jobs-Hangfire.md

@ -88,17 +88,21 @@ To make it secure by default, only local requests are allowed, however you can c
You can integrate the Hangfire dashboard to [ABP authorization system](Authorization.md) using the **AbpHangfireAuthorizationFilter**
class. This class is defined in the `Volo.Abp.Hangfire` package. The following example, checks if the current user is logged in to the application:
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
AsyncAuthorization = new[] { new AbpHangfireAuthorizationFilter() }
});
```csharp
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
AsyncAuthorization = new[] { new AbpHangfireAuthorizationFilter() }
});
```
If you want to require an additional permission, you can pass it into the constructor as below:
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
AsyncAuthorization = new[] { new AbpHangfireAuthorizationFilter("MyHangFireDashboardPermissionName") }
});
```csharp
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
AsyncAuthorization = new[] { new AbpHangfireAuthorizationFilter("MyHangFireDashboardPermissionName") }
});
```
**Important**: `UseHangfireDashboard` should be called after the authentication middleware in your `Startup` class (probably at the last line). Otherwise,
**Important**: `UseHangfireDashboard` should be called after the authentication and authorization middlewares in your `Startup` class (probably at the last line). Otherwise,
authorization will always fail!

29
docs/en/Best-Practices/Entity-Framework-Core-Integration.md

@ -66,12 +66,7 @@ public static string Schema { get; set; } = AbpIdentityConsts.DefaultDbSchema;
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ConfigureIdentity(options =>
{
options.TablePrefix = TablePrefix;
options.Schema = Schema;
});
builder.ConfigureIdentity();
}
````
@ -80,25 +75,20 @@ protected override void OnModelCreating(ModelBuilder builder)
````C#
public static class IdentityDbContextModelBuilderExtensions
{
public static void ConfigureIdentity(
[NotNull] this ModelBuilder builder,
Action<IdentityModelBuilderConfigurationOptions> optionsAction = null)
public static void ConfigureIdentity([NotNull] this ModelBuilder builder)
{
Check.NotNull(builder, nameof(builder));
var options = new IdentityModelBuilderConfigurationOptions();
optionsAction?.Invoke(options);
builder.Entity<IdentityUser>(b =>
{
b.ToTable(options.TablePrefix + "Users", options.Schema);
b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users", AbpIdentityDbProperties.DbSchema);
b.ConfigureByConvention();
//code omitted for brevity
});
builder.Entity<IdentityUserClaim>(b =>
{
b.ToTable(options.TablePrefix + "UserClaims", options.Schema);
b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "UserClaims", AbpIdentityDbProperties.DbSchema);
b.ConfigureByConvention();
//code omitted for brevity
});
@ -109,17 +99,6 @@ public static class IdentityDbContextModelBuilderExtensions
````
* **Do** call `b.ConfigureByConvention();` for each entity mapping (as shown above).
* **Do** create a **configuration options** class by inheriting from the `AbpModelBuilderConfigurationOptions`. Example:
````C#
public class IdentityModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions
{
public IdentityModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix, AbpIdentityConsts.DefaultDbSchema)
{
}
}
````
### Repository Implementation

26
docs/en/Best-Practices/MongoDB-Integration.md

@ -55,10 +55,7 @@ protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
base.CreateModel(modelBuilder);
modelBuilder.ConfigureIdentity(options =>
{
options.CollectionPrefix = CollectionPrefix;
});
modelBuilder.ConfigureIdentity();
}
```
@ -73,36 +70,19 @@ public static class AbpIdentityMongoDbContextExtensions
{
Check.NotNull(builder, nameof(builder));
var options = new IdentityMongoModelBuilderConfigurationOptions();
optionsAction?.Invoke(options);
builder.Entity<IdentityUser>(b =>
{
b.CollectionName = options.CollectionPrefix + "Users";
b.CollectionName = AbpIdentityDbProperties.DbTablePrefix + "Users";
});
builder.Entity<IdentityRole>(b =>
{
b.CollectionName = options.CollectionPrefix + "Roles";
b.CollectionName = AbpIdentityDbProperties.DbTablePrefix + "Roles";
});
}
}
```
- **Do** create a **configuration options** class by inheriting from the `AbpMongoModelBuilderConfigurationOptions`. Example:
```c#
public class IdentityMongoModelBuilderConfigurationOptions
: AbpMongoModelBuilderConfigurationOptions
{
public IdentityMongoModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix)
{
}
}
```
### Repository Implementation
- **Do** **inherit** the repository from the `MongoDbRepository<TMongoDbContext, TEntity, TKey>` class and implement the corresponding repository interface. Example:

46
docs/en/Community-Articles/2020-08-07-Passwordless-Authentication/POST.md

@ -194,16 +194,11 @@ We implemented token generation infrastructure, now it's time validate the token
```csharp
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Identity;
using Volo.Abp.Security.Claims;
using Volo.Abp.Users;
using Volo.Abp.Identity.AspNetCore;
namespace PasswordlessAuthentication.Web.Controllers
{
@ -211,9 +206,12 @@ namespace PasswordlessAuthentication.Web.Controllers
{
protected IdentityUserManager UserManager { get; }
public PasswordlessController(IdentityUserManager userManager)
protected AbpSignInManager SignInManager { get; }
public PasswordlessController(IdentityUserManager userManager, AbpSignInManager signInManager)
{
UserManager = userManager;
SignInManager = signInManager;
}
public virtual async Task<IActionResult> Login(string token, string userId)
@ -228,45 +226,15 @@ namespace PasswordlessAuthentication.Web.Controllers
await UserManager.UpdateSecurityStampAsync(user);
var roles = await UserManager.GetRolesAsync(user);
var principal = new ClaimsPrincipal(
new ClaimsIdentity(CreateClaims(user, roles), IdentityConstants.ApplicationScheme)
);
await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, principal);
await SignInManager.SignInAsync(user, isPersistent: false);
return Redirect("/");
}
private static IEnumerable<Claim> CreateClaims(IUser user, IEnumerable<string> roles)
{
var claims = new List<Claim>
{
new Claim("sub", user.Id.ToString()),
new Claim(AbpClaimTypes.UserId, user.Id.ToString()),
new Claim(AbpClaimTypes.Email, user.Email),
new Claim(AbpClaimTypes.UserName, user.UserName),
new Claim(AbpClaimTypes.EmailVerified, user.EmailConfirmed.ToString().ToLower()),
};
if (!string.IsNullOrWhiteSpace(user.PhoneNumber))
{
claims.Add(new Claim(AbpClaimTypes.PhoneNumber, user.PhoneNumber));
}
foreach (var role in roles)
{
claims.Add(new Claim(AbpClaimTypes.Role, role));
}
return claims;
}
}
}
```
We created an endpoint for `/Passwordless/Login` that gets the token and the user Id. In this action, we find the user via repository and validate the token via `UserManager.VerifyUserTokenAsync()` method. If it's valid, we create claims of the user then call `HttpContext.SignInAsync` to be able to create an encrypted cookie and add it to the current response. Finally we redirect the page to the root URL.
We created an endpoint for `/Passwordless/Login` that gets the token and the user Id. In this action, we find the user via repository and validate the token via `UserManager.VerifyUserTokenAsync()` method. If it's valid, we call `SignInManager.SignInAsync` to be able to create an encrypted cookie and add it to the current response. Finally we redirect the page to the root URL.
That's all! We created a passwordless login with 7 steps.

BIN
docs/en/Community-Articles/2021-09-25-How-to-Override-Localization-Strings-Of-Depending-Modules/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/en/Community-Articles/2021-09-25-How-to-Override-Localization-Strings-Of-Depending-Modules/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

88
docs/en/Community-Articles/2021-09-25-How-to-Override-Localization-Strings-Of-Depending-Modules/POST.md

@ -0,0 +1,88 @@
# How to override localization strings of depending modules
## Source Code
You can find the source of the example solution used in this article [here](https://github.com/abpframework/abp-samples/tree/master/DocumentationSamples/ExtendLocalizationResource).
## Getting Started
This example is based on the following document
https://docs.abp.io/en/abp/latest/Localization#extending-existing-resource
We will change the default `DisplayName:Abp.Timing.Timezone` and `Description:Abp.Timing.Timezone` of [`AbpTimingResource`](https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/AbpTimingResource.cs) and add localized text in [Russian language(`ru`)](https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/en.json).
I created the `AbpTiming` folder in the `Localization` directory of the `ExtendLocalizationResource.Domain.Shared` project.
Create `en.json` and `ru.json` in its directory.
`en.json`
```json
{
"culture": "en",
"texts": {
"DisplayName:Abp.Timing.Timezone": "My Time zone",
"Description:Abp.Timing.Timezone": "My Application time zone"
}
}
```
`ru.json`
```json
{
"culture": "ru",
"texts": {
"DisplayName:Abp.Timing.Timezone": "Часовой пояс",
"Description:Abp.Timing.Timezone": "Часовой пояс приложения"
}
}
```
![](1.png)
We have below content in `ExtendLocalizationResource.Domain.Shared.csproj` file, See [Virtual-File-System](https://docs.abp.io/en/abp/latest/Virtual-File-System#working-with-the-embedded-files) understand how it works.
```xml
<ItemGroup>
<EmbeddedResource Include="Localization\ExtendLocalizationResource\*.json" />
<Content Remove="Localization\ExtendLocalizationResource\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="5.0.*" />
</ItemGroup>
```
Change the code of the `ConfigureServices` method in `ExtendLocalizationResourceDomainSharedModule`.
```cs
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<ExtendLocalizationResourceResource>("en")
.AddBaseTypes(typeof(AbpValidationResource))
.AddVirtualJson("/Localization/ExtendLocalizationResource");
//add following code
options.Resources
.Get<AbpTimingResource>()
.AddVirtualJson("/Localization/AbpTiming");
options.DefaultResourceType = typeof(ExtendLocalizationResourceResource);
});
```
Execute `ExtendLocalizationResource.DbMigrator` to migrate the database and run `ExtendLocalizationResource.Web`.
We have changed the English localization text and added Russian localization.
### Index page
```cs
<p>@AbpTimingResource["DisplayName:Abp.Timing.Timezone"]</p>
@using(CultureHelper.Use("ru"))
{
<p>@AbpTimingResource["DisplayName:Abp.Timing.Timezone"]</p>
}
```
![](2.png)

9
docs/en/Road-Map.md

@ -10,12 +10,9 @@ This version will focus on the following works:
* Upgrading to .NET 6.0 ([#9004](https://github.com/abpframework/abp/issues/9004))
* Upgrading to Bootstrap 5.x ([#8922](https://github.com/abpframework/abp/issues/8922))
* Alternative to IdentityServer4 ([#7221](https://github.com/abpframework/abp/issues/7221))
* C# and JavaScript Static Client Proxy Generation ([#9864](https://github.com/abpframework/abp/issues/9864))
* Revisit the microservice demo solution ([#8385](https://github.com/abpframework/abp/issues/8385))
* Dapr integration ([#2183](https://github.com/abpframework/abp/issues/2183))
* Publishing distributed events as transactional ([#6126](https://github.com/abpframework/abp/issues/6126))
* Resource based authorization system ([#236](https://github.com/abpframework/abp/issues/236))
* API Versioning system: finalize & document ([#497](https://github.com/abpframework/abp/issues/497))
* Performance optimizations; Enabling .NET Trimming, using source generators and reducing reflection, etc.
* Improving the abp.io platform and work more on contents and documents
@ -27,6 +24,10 @@ The *Next Versions* section above shows the main focus of the planned versions.
Here, a list of major items in the backlog we are considering to work on in the next versions.
* ([#497](https://github.com/abpframework/abp/issues/497)) API Versioning system: finalize & document
* ([#7221](https://github.com/abpframework/abp/issues/7221)) Alternative to IdentityServer4
* ([#2183](https://github.com/abpframework/abp/issues/2183)) Dapr integration
* ([#236](https://github.com/abpframework/abp/issues/236)) Resource based authorization system
* [#2882](https://github.com/abpframework/abp/issues/2882) / Providing a gRPC integration infrastructure (while it is [already possible](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo) to create or consume gRPC endpoints for your application, we plan to create endpoints for the [standard application modules](https://docs.abp.io/en/abp/latest/Modules/Index))
* [#1754](https://github.com/abpframework/abp/issues/1754) / Multi-lingual entities
* [#57](https://github.com/abpframework/abp/issues/57) / Built-in CQRS infrastructure

5
docs/en/Tutorials/Part-1.md

@ -34,6 +34,11 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
> `git config --system core.longpaths true`
{{if UI == "MVC" && DB == "EF"}}
### Video Tutorial

5
docs/en/Tutorials/Part-10.md

@ -34,6 +34,11 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
> `git config --system core.longpaths true`
## Introduction
We have created `Book` and `Author` functionalities for the book store application. However, currently there is no relation between these entities.

7
docs/en/Tutorials/Part-2.md

@ -34,6 +34,11 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
> `git config --system core.longpaths true`
{{if UI == "MVC" && DB == "EF"}}
### Video Tutorial
@ -85,6 +90,8 @@ acme.bookStore.books.book.create({
});
````
> If you downloaded the source code of the tutorial and following the steps from the sample, you should also pass the `authorId` parameter to the create method for **creating a new book**.
You should see a message in the console something like that:
````text

5
docs/en/Tutorials/Part-3.md

@ -34,6 +34,11 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
> `git config --system core.longpaths true`
{{if UI == "MVC" && DB == "EF"}}
### Video Tutorial

5
docs/en/Tutorials/Part-4.md

@ -34,6 +34,11 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
> `git config --system core.longpaths true`
{{if UI == "MVC" && DB == "EF"}}
### Video Tutorial

7
docs/en/Tutorials/Part-5.md

@ -34,6 +34,11 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
> `git config --system core.longpaths true`
{{if UI == "MVC" && DB == "EF"}}
### Video Tutorial
@ -627,4 +632,4 @@ private async Task ConfigureMainMenuAsync(MenuConfigurationContext context)
## The Next Part
See the [next part](Part-6.md) of this tutorial.
See the [next part](Part-6.md) of this tutorial.

7
docs/en/Tutorials/Part-6.md

@ -34,6 +34,11 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
> `git config --system core.longpaths true`
## Introduction
In the previous parts, we've used the ABP infrastructure to easily build some services;
@ -270,4 +275,4 @@ This part covered the domain layer of the authors functionality of the book stor
## The Next Part
See the [next part](Part-7.md) of this tutorial.
See the [next part](Part-7.md) of this tutorial.

5
docs/en/Tutorials/Part-7.md

@ -34,6 +34,11 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
> `git config --system core.longpaths true`
## Introduction
This part explains how to configure the database integration for the `Author` entity introduced in the previous part.

5
docs/en/Tutorials/Part-8.md

@ -34,6 +34,11 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
> `git config --system core.longpaths true`
## Introduction
This part explains to create an application layer for the `Author` entity created before.

5
docs/en/Tutorials/Part-9.md

@ -34,6 +34,11 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
> `git config --system core.longpaths true`
## Introduction
This part explains how to create a CRUD page for the `Author` entity introduced in the previous parts.

67
docs/en/UI/Angular/How-Replaceable-Components-Work-with-Extensions.md

@ -17,30 +17,18 @@ yarn ng generate component my-roles/my-roles --flat --export
Open the generated `src/app/my-roles/my-roles.component.ts` file and replace its content with the following:
```js
import { ListService, PagedAndSortedResultRequestDto } from '@abp/ng.core';
import {
CreateRole,
DeleteRole,
eIdentityComponents,
GetRoleById,
GetRoles,
IdentityRoleDto,
IdentityState,
RolesComponent,
UpdateRole,
} from '@abp/ng.identity';
import { ListService, PagedAndSortedResultRequestDto, PagedResultDto } from '@abp/ng.core';
import { eIdentityComponents, IdentityRoleDto, IdentityRoleService, RolesComponent } from '@abp/ng.identity';
import { ePermissionManagementComponents } from '@abp/ng.permission-management';
import { Confirmation, ConfirmationService } from '@abp/ng.theme.shared';
import {
EXTENSIONS_IDENTIFIER,
FormPropData,
generateFormFromProps,
generateFormFromProps
} from '@abp/ng.theme.shared/extensions';
import { Component, Injector, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { finalize, pluck } from 'rxjs/operators';
import { finalize } from 'rxjs/operators';
@Component({
selector: 'app-my-roles',
@ -51,15 +39,14 @@ import { finalize, pluck } from 'rxjs/operators';
provide: EXTENSIONS_IDENTIFIER,
useValue: eIdentityComponents.Roles,
},
{ provide: RolesComponent, useExisting: MyRolesComponent },
{
provide: RolesComponent,
useExisting: MyRolesComponent
}
],
})
export class MyRolesComponent implements OnInit {
@Select(IdentityState.getRoles)
data$: Observable<IdentityRoleDto[]>;
@Select(IdentityState.getRolesTotalCount)
totalCount$: Observable<number>;
data: PagedResultDto<IdentityRoleDto> = { items: [], totalCount: 0 };
form: FormGroup;
@ -82,8 +69,8 @@ export class MyRolesComponent implements OnInit {
constructor(
public readonly list: ListService<PagedAndSortedResultRequestDto>,
protected confirmationService: ConfirmationService,
protected store: Store,
protected injector: Injector
protected injector: Injector,
protected service: IdentityRoleService,
) {}
ngOnInit() {
@ -106,25 +93,21 @@ export class MyRolesComponent implements OnInit {
}
edit(id: string) {
this.store
.dispatch(new GetRoleById(id))
.pipe(pluck('IdentityState', 'selectedRole'))
.subscribe(selectedRole => {
this.selected = selectedRole;
this.openModal();
});
this.service.get(id).subscribe(res => {
this.selected = res;
this.openModal();
});
}
save() {
if (!this.form.valid) return;
this.modalBusy = true;
this.store
.dispatch(
this.selected.id
? new UpdateRole({ ...this.selected, ...this.form.value, id: this.selected.id })
: new CreateRole(this.form.value)
)
const { id } = this.selected;
(id
? this.service.update(id, { ...this.selected, ...this.form.value })
: this.service.create(this.form.value)
)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.isModalVisible = false;
@ -139,13 +122,13 @@ export class MyRolesComponent implements OnInit {
})
.subscribe((status: Confirmation.Status) => {
if (status === Confirmation.Status.confirm) {
this.store.dispatch(new DeleteRole(id)).subscribe(() => this.list.get());
this.service.delete(id).subscribe(() => this.list.get());
}
});
}
private hookToQuery() {
this.list.hookToQuery(query => this.store.dispatch(new GetRoles(query))).subscribe();
this.list.hookToQuery(query => this.service.getList(query)).subscribe(res => (this.data = res));
}
openPermissionsModal(providerKey: string) {
@ -189,15 +172,15 @@ Open the generated `src/app/my-role/my-role.component.html` file and replace its
<h5 class="card-title">My Roles</h5>
</div>
<div class="text-right col col-md-6">
<abp-page-toolbar [record]="data$ | async"></abp-page-toolbar>
<abp-page-toolbar [record]="data.items"></abp-page-toolbar>
</div>
</div>
</div>
<div class="card-body">
<abp-extensible-table
[data]="data$ | async"
[recordsTotal]="totalCount$ | async"
[data]="data.items"
[recordsTotal]="data.totalCount"
[list]="list"
></abp-extensible-table>
</div>

276
docs/en/UI/Angular/Permission-Management-Component-Replacement.md

@ -5,37 +5,27 @@
Run the following command in `angular` folder to create a new component called `PermissionManagementComponent`.
```bash
yarn ng generate component permission-management --entryComponent --inlineStyle
# You don't need the --entryComponent option in Angular 9
yarn ng generate component permission-management --inlineStyle
```
Open the generated `permission-management.component.ts` in `src/app/permission-management` folder and replace the content with the following:
```js
import { ConfigStateService, CurrentUserDto, ReplaceableComponents } from '@abp/ng.core';
import {
GetPermissionListResultDto,
PermissionGrantInfoDto, PermissionGroupDto, PermissionManagement, PermissionsService, ProviderInfoDto, UpdatePermissionDto
} from '@abp/ng.permission-management';
import { LocaleDirection } from '@abp/ng.theme.shared';
import {
Component,
EventEmitter,
Input,
Output,
Renderer2,
TrackByFunction,
Inject,
Optional,
EventEmitter, Inject, Input, Optional, Output, TrackByFunction
} from '@angular/core';
import { ReplaceableComponents } from '@abp/ng.core';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { finalize, map, pluck, take, tap } from 'rxjs/operators';
import {
GetPermissions,
UpdatePermissions,
PermissionManagement,
PermissionManagementState,
} from '@abp/ng.permission-management';
import { of } from 'rxjs';
import { finalize, switchMap, tap } from 'rxjs/operators';
type PermissionWithMargin = PermissionManagement.Permission & {
margin: number;
type PermissionWithStyle = PermissionGrantInfoDto & {
style: string;
};
@Component({
@ -52,8 +42,8 @@ type PermissionWithMargin = PermissionManagement.Permission & {
})
export class PermissionManagementComponent
implements
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs {
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs {
protected _providerName: string;
@Input()
get providerName(): string {
@ -115,15 +105,11 @@ export class PermissionManagementComponent
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Select(PermissionManagementState.getPermissionGroups)
groups$: Observable<PermissionManagement.Group[]>;
data: GetPermissionListResultDto = { groups: [], entityDisplayName: null };
@Select(PermissionManagementState.getEntityDisplayName)
entityName$: Observable<string>;
selectedGroup: PermissionGroupDto;
selectedGroup: PermissionManagement.Group;
permissions: PermissionManagement.Permission[] = [];
permissions: PermissionGrantInfoDto[] = [];
selectThisTab = false;
@ -131,25 +117,26 @@ export class PermissionManagementComponent
modalBusy = false;
trackByFn: TrackByFunction<PermissionManagement.Group> = (_, item) => item.name;
get selectedGroupPermissions$(): Observable<PermissionWithMargin[]> {
return this.groups$.pipe(
map((groups) =>
this.selectedGroup
? groups.find((group) => group.name === this.selectedGroup.name).permissions
: []
),
map<PermissionManagement.Permission[], PermissionWithMargin[]>((permissions) =>
permissions.map(
(permission) =>
(({
...permission,
margin: findMargin(permissions, permission),
isGranted: this.permissions.find((per) => per.name === permission.name).isGranted,
} as any) as PermissionWithMargin)
)
)
trackByFn: TrackByFunction<PermissionGroupDto> = (_, item) => item.name;
get selectedGroupPermissions(): PermissionWithStyle[] {
if (!this.selectedGroup) return [];
const margin = `margin-${
(document.body.dir as LocaleDirection) === 'rtl' ? 'right' : 'left'
}.px`;
const permissions = this.data.groups.find(
group => group.name === this.selectedGroup.name,
).permissions;
return permissions.map(
permission =>
({
...permission,
style: { [margin]: findMargin(permissions, permission) },
isGranted: this.permissions.find(per => per.name === permission.name).isGranted,
} as unknown as PermissionWithStyle),
);
}
@ -166,21 +153,22 @@ export class PermissionManagementComponent
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs
>,
private store: Store
private service: PermissionsService,
private configState: ConfigStateService
) {}
getChecked(name: string) {
return (this.permissions.find((per) => per.name === name) || { isGranted: false }).isGranted;
return (this.permissions.find(per => per.name === name) || { isGranted: false }).isGranted;
}
isGrantedByOtherProviderName(grantedProviders: PermissionManagement.GrantedProvider[]): boolean {
isGrantedByOtherProviderName(grantedProviders: ProviderInfoDto[]): boolean {
if (grantedProviders.length) {
return grantedProviders.findIndex((p) => p.providerName !== this.providerName) > -1;
return grantedProviders.findIndex(p => p.providerName !== this.providerName) > -1;
}
return false;
}
onClickCheckbox(clickedPermission: PermissionManagement.Permission, value) {
onClickCheckbox(clickedPermission: PermissionGrantInfoDto, value) {
if (
clickedPermission.isGranted &&
this.isGrantedByOtherProviderName(clickedPermission.grantedProviders)
@ -188,7 +176,7 @@ export class PermissionManagementComponent
return;
setTimeout(() => {
this.permissions = this.permissions.map((per) => {
this.permissions = this.permissions.map(per => {
if (clickedPermission.name === per.name) {
return { ...per, isGranted: !per.isGranted };
} else if (clickedPermission.name === per.parentName && clickedPermission.isGranted) {
@ -206,24 +194,22 @@ export class PermissionManagementComponent
}
setTabCheckboxState() {
this.selectedGroupPermissions$.pipe(take(1)).subscribe((permissions) => {
const selectedPermissions = permissions.filter((per) => per.isGranted);
const element = document.querySelector('#select-all-in-this-tabs') as any;
if (selectedPermissions.length === permissions.length) {
element.indeterminate = false;
this.selectThisTab = true;
} else if (selectedPermissions.length === 0) {
element.indeterminate = false;
this.selectThisTab = false;
} else {
element.indeterminate = true;
}
});
const selectedPermissions = this.selectedGroupPermissions.filter(per => per.isGranted);
const element = document.querySelector('#select-all-in-this-tabs') as any;
if (selectedPermissions.length === this.selectedGroupPermissions.length) {
element.indeterminate = false;
this.selectThisTab = true;
} else if (selectedPermissions.length === 0) {
element.indeterminate = false;
this.selectThisTab = false;
} else {
element.indeterminate = true;
}
}
setGrantCheckboxState() {
const selectedAllPermissions = this.permissions.filter((per) => per.isGranted);
const selectedAllPermissions = this.permissions.filter(per => per.isGranted);
const checkboxElement = document.querySelector('#select-all-in-all-tabs') as any;
if (selectedAllPermissions.length === this.permissions.length) {
@ -238,26 +224,24 @@ export class PermissionManagementComponent
}
onClickSelectThisTab() {
this.selectedGroupPermissions$.pipe(take(1)).subscribe((permissions) => {
permissions.forEach((permission) => {
if (permission.isGranted && this.isGrantedByOtherProviderName(permission.grantedProviders))
return;
const index = this.permissions.findIndex((per) => per.name === permission.name);
this.permissions = [
...this.permissions.slice(0, index),
{ ...this.permissions[index], isGranted: !this.selectThisTab },
...this.permissions.slice(index + 1),
];
});
this.selectedGroupPermissions.forEach(permission => {
if (permission.isGranted && this.isGrantedByOtherProviderName(permission.grantedProviders))
return;
const index = this.permissions.findIndex(per => per.name === permission.name);
this.permissions = [
...this.permissions.slice(0, index),
{ ...this.permissions[index], isGranted: !this.selectThisTab },
...this.permissions.slice(index + 1),
];
});
this.setGrantCheckboxState();
}
onClickSelectAll() {
this.permissions = this.permissions.map((permission) => ({
this.permissions = this.permissions.map(permission => ({
...permission,
isGranted:
this.isGrantedByOtherProviderName(permission.grantedProviders) || !this.selectAllTab,
@ -266,43 +250,41 @@ export class PermissionManagementComponent
this.selectThisTab = !this.selectAllTab;
}
onChangeGroup(group: PermissionManagement.Group) {
onChangeGroup(group: PermissionGroupDto) {
this.selectedGroup = group;
this.setTabCheckboxState();
}
submit() {
this.modalBusy = true;
const unchangedPermissions = getPermissions(
this.store.selectSnapshot(PermissionManagementState.getPermissionGroups)
);
const unchangedPermissions = getPermissions(this.data.groups);
const changedPermissions: PermissionManagement.MinimumPermission[] = this.permissions
.filter((per) =>
unchangedPermissions.find((unchanged) => unchanged.name === per.name).isGranted ===
const changedPermissions: UpdatePermissionDto[] = this.permissions
.filter(per =>
unchangedPermissions.find(unchanged => unchanged.name === per.name).isGranted ===
per.isGranted
? false
: true
: true,
)
.map(({ name, isGranted }) => ({ name, isGranted }));
if (changedPermissions.length) {
this.store
.dispatch(
new UpdatePermissions({
providerKey: this.providerKey,
providerName: this.providerName,
permissions: changedPermissions,
})
)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.visible = false;
});
} else {
this.modalBusy = false;
if (!changedPermissions.length) {
this.visible = false;
return;
}
this.modalBusy = true;
this.service
.update(this.providerName, this.providerKey, { permissions: changedPermissions })
.pipe(
switchMap(() =>
this.shouldFetchAppConfig() ? this.configState.refreshAppState() : of(null),
),
finalize(() => (this.modalBusy = false)),
)
.subscribe(() => {
this.visible = false;
});
}
openModal() {
@ -310,20 +292,13 @@ export class PermissionManagementComponent
throw new Error('Provider Key and Provider Name are required.');
}
return this.store
.dispatch(
new GetPermissions({
providerKey: this.providerKey,
providerName: this.providerName,
})
)
.pipe(
pluck('PermissionManagementState', 'permissionRes'),
tap((permissionRes: PermissionManagement.Response) => {
this.selectedGroup = permissionRes.groups[0];
this.permissions = getPermissions(permissionRes.groups);
})
);
return this.service.get(this.providerName, this.providerKey).pipe(
tap((permissionRes: GetPermissionListResultDto) => {
this.data = permissionRes;
this.selectedGroup = permissionRes.groups[0];
this.permissions = getPermissions(permissionRes.groups);
}),
);
}
initModal() {
@ -331,6 +306,13 @@ export class PermissionManagementComponent
this.setGrantCheckboxState();
}
getAssignedCount(groupName: string) {
return this.permissions.reduce(
(acc, val) => (val.name.split('.')[0] === groupName && val.isGranted ? acc + 1 : acc),
0,
);
}
onVisibleChange(visible: boolean) {
this.visible = visible;
@ -339,13 +321,20 @@ export class PermissionManagementComponent
this.replaceableData.outputs.visibleChange(visible);
}
}
shouldFetchAppConfig() {
const currentUser = this.configState.getOne('currentUser') as CurrentUserDto;
if (this.providerName === 'R') return currentUser.roles.some(role => role === this.providerKey);
if (this.providerName === 'U') return currentUser.id === this.providerKey;
return false;
}
}
function findMargin(
permissions: PermissionManagement.Permission[],
permission: PermissionManagement.Permission
) {
const parentPermission = permissions.find((per) => per.name === permission.parentName);
function findMargin(permissions: PermissionGrantInfoDto[], permission: PermissionGrantInfoDto) {
const parentPermission = permissions.find(per => per.name === permission.parentName);
if (parentPermission && parentPermission.parentName) {
let margin = 20;
@ -355,7 +344,7 @@ function findMargin(
return parentPermission ? 20 : 0;
}
function getPermissions(groups: PermissionManagement.Group[]): PermissionManagement.Permission[] {
function getPermissions(groups: PermissionGroupDto[]): PermissionGrantInfoDto[] {
return groups.reduce((acc, val) => [...acc, ...val.permissions], []);
}
```
@ -363,16 +352,12 @@ function getPermissions(groups: PermissionManagement.Group[]): PermissionManagem
Open the generated `permission-management.component.html` in `src/app/permission-management` folder and replace the content with the below:
```html
<abp-modal
[visible]="isVisible"
(visibleChange)="onVisibleChange($event)"
(init)="initModal()"
[busy]="modalBusy"
>
<ng-container *ngIf="{ entityName: entityName$ | async } as data">
<abp-modal [visible]="isVisible" (visibleChange)="onVisibleChange($event)" [busy]="modalBusy">
<ng-container *ngIf="data.entityDisplayName">
<ng-template #abpHeader>
<h4>
{%{{{ 'AbpPermissionManagement::Permissions' | abpLocalization }}}%} - {%{{{ data.entityName }}}%}
{%{{{ 'AbpPermissionManagement::Permissions' | abpLocalization }}}%} -
{%{{{ data.entityDisplayName }}}%}
</h4>
</ng-template>
<ng-template #abpBody>
@ -394,13 +379,18 @@ Open the generated `permission-management.component.html` in `src/app/permission
<div class="row">
<div class="overflow-scroll col-md-4">
<ul class="nav nav-pills flex-column">
<li *ngFor="let group of groups$ | async; trackBy: trackByFn" class="nav-item">
<li *ngFor="let group of data.groups; trackBy: trackByFn" class="nav-item">
<a
*ngIf="{ assignedCount: getAssignedCount(group.name) } as count"
class="nav-link pointer"
[class.active]="selectedGroup?.name === group?.name"
(click)="onChangeGroup(group)"
>{%{{{ group?.displayName }}}%}</a
>
<div [class.font-weight-bold]="count.assignedCount">
{%{{{ group?.displayName }}}%}
<span>({%{{{ count.assignedCount }}}%})</span>
</div>
</a>
</li>
</ul>
</div>
@ -423,12 +413,8 @@ Open the generated `permission-management.component.html` in `src/app/permission
</div>
<hr class="mb-3" />
<div
*ngFor="
let permission of selectedGroupPermissions$ | async;
let i = index;
trackBy: trackByFn
"
[style.margin-left]="permission.margin + 'px'"
*ngFor="let permission of selectedGroupPermissions; let i = index; trackBy: trackByFn"
[ngStyle]="permission.style"
class="custom-checkbox custom-control mb-2"
>
<input

6
docs/zh-Hans/Text-Templating.md

@ -187,9 +187,9 @@ var result = await _templateRenderer.RenderAsync(
示例中我们并没有创建模型类,但是创建了一个匿名对象模型.
### 大驼峰 与 小驼峰
### PascalCase 与 snake_case
PascalCase 属性名(如 `UserName`) 在模板中用做小驼峰(如 `userName`).
PascalCase 属性名(如 `UserName`) 在模板中使用蛇形命名(如 `user_name`).
## 本地化
@ -454,4 +454,4 @@ public class MyTemplateContentProvider
* 本文开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo).
* [本地化系统](Localization.md).
* [虚拟文件系统](Virtual-File-System.md).
* [虚拟文件系统](Virtual-File-System.md).

20
framework/Volo.Abp.sln

@ -383,11 +383,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating.Scr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.MongoDB.Tests.SecondContext", "test\Volo.Abp.MongoDB.Tests.SecondContext\Volo.Abp.MongoDB.Tests.SecondContext.csproj", "{90B1866A-EF99-40B9-970E-B898E5AA523F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.IdentityModel.Tests", "test\Volo.Abp.IdentityModel.Tests\Volo.Abp.IdentityModel.Tests.csproj", "{40C6740E-BFCA-4D37-8344-3D84E2044BB2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Threading.Tests", "test\Volo.Abp.Threading.Tests\Volo.Abp.Threading.Tests.csproj", "{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Boxes", "src\Volo.Abp.EventBus.Boxes\Volo.Abp.EventBus.Boxes.csproj", "{6E289F31-7924-418B-9DAC-62A7CFADF916}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.DistributedLocking", "src\Volo.Abp.DistributedLocking\Volo.Abp.DistributedLocking.csproj", "{9A7EEA08-15BE-476D-8168-53039867038E}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Auditing.Contracts", "src\Volo.Abp.Auditing.Contracts\Volo.Abp.Auditing.Contracts.csproj", "{508B6355-AD28-4E60-8549-266D21DBF2CF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Client.Web", "src\Volo.Abp.Http.Client.Web\Volo.Abp.Http.Client.Web.csproj", "{F7407459-8AFA-45E4-83E9-9BB01412CC08}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -1147,6 +1152,10 @@ Global
{90B1866A-EF99-40B9-970E-B898E5AA523F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90B1866A-EF99-40B9-970E-B898E5AA523F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90B1866A-EF99-40B9-970E-B898E5AA523F}.Release|Any CPU.Build.0 = Release|Any CPU
{40C6740E-BFCA-4D37-8344-3D84E2044BB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{40C6740E-BFCA-4D37-8344-3D84E2044BB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{40C6740E-BFCA-4D37-8344-3D84E2044BB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{40C6740E-BFCA-4D37-8344-3D84E2044BB2}.Release|Any CPU.Build.0 = Release|Any CPU
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -1159,6 +1168,14 @@ Global
{9A7EEA08-15BE-476D-8168-53039867038E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A7EEA08-15BE-476D-8168-53039867038E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A7EEA08-15BE-476D-8168-53039867038E}.Release|Any CPU.Build.0 = Release|Any CPU
{508B6355-AD28-4E60-8549-266D21DBF2CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{508B6355-AD28-4E60-8549-266D21DBF2CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{508B6355-AD28-4E60-8549-266D21DBF2CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{508B6355-AD28-4E60-8549-266D21DBF2CF}.Release|Any CPU.Build.0 = Release|Any CPU
{F7407459-8AFA-45E4-83E9-9BB01412CC08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F7407459-8AFA-45E4-83E9-9BB01412CC08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7407459-8AFA-45E4-83E9-9BB01412CC08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7407459-8AFA-45E4-83E9-9BB01412CC08}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1352,9 +1369,12 @@ Global
{C996F458-98FB-483D-9306-4701290E2FC1} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{75D8DADB-3FA9-4C1D-B23A-DBFD08133B7C} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{90B1866A-EF99-40B9-970E-B898E5AA523F} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{40C6740E-BFCA-4D37-8344-3D84E2044BB2} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{6E289F31-7924-418B-9DAC-62A7CFADF916} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{9A7EEA08-15BE-476D-8168-53039867038E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{508B6355-AD28-4E60-8549-266D21DBF2CF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{F7407459-8AFA-45E4-83E9-9BB01412CC08} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

2
framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Authentication.JwtBearer</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Authentication.JwtBearer</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo.Abp.AspNetCore.Authentication.OAuth.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Authentication.OAuth</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Authentication.OAuth</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo.Abp.AspNetCore.Authentication.OpenIdConnect.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>

2
framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Volo.Abp.AspNetCore.Components.Server.Theming.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>true</IsPackable>
<OutputType>Library</OutputType>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>

2
framework/src/Volo.Abp.AspNetCore.Components.Server/Volo.Abp.AspNetCore.Components.Server.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>true</IsPackable>
<OutputType>Library</OutputType>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>

2
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Volo.Abp.AspNetCore.Components.Web.Theming.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

2
framework/src/Volo.Abp.AspNetCore.Components.Web/Volo.Abp.AspNetCore.Components.Web.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>

2
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

2
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Components.WebAssembly</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Components.WebAssembly</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Components</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Components</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo.Abp.AspNetCore.MultiTenancy.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.MultiTenancy</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.MultiTenancy</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.Client</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.Client</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

1
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs

@ -1,7 +1,6 @@
using System;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending;
using Volo.Abp.AspNetCore.Mvc.MultiTenancy;
using Volo.Abp.Timing;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
{

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Bootstrap</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Bootstrap</PackageId>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Bundling</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Bundling</PackageId>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy</PackageId>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo.Abp.AspNetCore.Mvc.UI.Packages.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Packages</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Packages</PackageId>

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/SweetAlert/SweetalertScriptContributor.cs → framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/SweetAlert2/Sweetalert2ScriptContributor.cs

@ -3,14 +3,14 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Core;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.SweetAlert
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.SweetAlert2
{
[DependsOn(typeof(CoreScriptContributor))]
public class SweetalertScriptContributor : BundleContributor
public class Sweetalert2ScriptContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/sweetalert/sweetalert.min.js");
context.Files.AddIfNotContains("/libs/sweetalert2/sweetalert2.all.min.js");
}
}
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo</PackageId>

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs

@ -9,7 +9,7 @@ using Volo.Abp.AspNetCore.Mvc.UI.Packages.Lodash;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Luxon;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.MalihuCustomScrollbar;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Select2;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.SweetAlert;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.SweetAlert2;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Timeago;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Toastr;
using Volo.Abp.Modularity;
@ -24,7 +24,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling
typeof(JQueryFormScriptContributor),
typeof(Select2ScriptContributor),
typeof(DatatablesNetBs4ScriptContributor),
typeof(SweetalertScriptContributor),
typeof(Sweetalert2ScriptContributor),
typeof(ToastrScriptBundleContributor),
typeof(MalihuCustomScrollbarPluginScriptBundleContributor),
typeof(LuxonScriptContributor),
@ -44,9 +44,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling
"/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js"
});
}
}
}
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared</PackageId>

24
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js → framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js

@ -1,6 +1,6 @@
var abp = abp || {};
(function ($) {
if (!sweetAlert || !$) {
if (!Swal || !$) {
return;
}
@ -27,7 +27,8 @@
confirm: {
icon: 'warning',
title: 'Are you sure?',
buttons: ['Cancel', 'Yes']
showCancelButton: true,
reverseButtons: true
}
}
};
@ -51,7 +52,7 @@
);
return $.Deferred(function ($dfd) {
sweetAlert(opts).then(function () {
Swal.fire(opts).then(function () {
$dfd.resolve();
});
});
@ -73,7 +74,7 @@
return showMessage('error', message, title);
};
abp.message.confirm = function (message, titleOrCallback, callback, closeOnEsc) {
abp.message.confirm = function (message, titleOrCallback, callback) {
var userOpts = {
text: message
@ -86,8 +87,6 @@
userOpts.title = titleOrCallback;
};
userOpts.closeOnEsc = closeOnEsc;
var opts = $.extend(
{},
abp.libs.sweetAlert.config['default'],
@ -96,10 +95,10 @@
);
return $.Deferred(function ($dfd) {
sweetAlert(opts).then(function (isConfirmed) {
callback && callback(isConfirmed);
$dfd.resolve(isConfirmed);
});
Swal.fire(opts).then(result => {
callback && callback(result.value);
$dfd.resolve(result.value);
})
});
};
@ -107,7 +106,8 @@
var l = abp.localization.getResource('AbpUi');
abp.libs.sweetAlert.config.confirm.title = l('AreYouSure');
abp.libs.sweetAlert.config.confirm.buttons = [l('Cancel'), l('Yes')];
abp.libs.sweetAlert.config.confirm.showCancelButton = true;
abp.libs.sweetAlert.config.confirm.reverseButtons = true;
});
})(jQuery);
})(jQuery);

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Widgets</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Widgets</PackageId>

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI</PackageId>
@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUglify" Version="1.13.8" />
<PackageReference Include="NUglify" Version="1.16.0" />
</ItemGroup>
<ItemGroup>

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc</PackageId>

25
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs

@ -86,6 +86,7 @@ namespace Volo.Abp.AspNetCore.Mvc
var controllerModel = moduleModel.GetOrAddController(
_options.ControllerNameGenerator(controllerType, setting),
FindGroupName(controllerType) ?? apiDescription.GroupName,
controllerType,
_modelOptions.IgnoredInterfaces
);
@ -113,6 +114,14 @@ namespace Volo.Abp.AspNetCore.Mvc
allowAnonymous = false;
}
var implementFrom = controllerType.FullName;
var interfaceType = controllerType.GetInterfaces().FirstOrDefault(i => i.GetMethods().Any(x => x.ToString() == method.ToString()));
if (interfaceType != null)
{
implementFrom = TypeHelper.GetFullNameHandlingNullableAndGenerics(interfaceType);
}
var actionModel = controllerModel.AddAction(
uniqueMethodName,
ActionApiDescriptionModel.Create(
@ -121,7 +130,8 @@ namespace Volo.Abp.AspNetCore.Mvc
apiDescription.RelativePath,
apiDescription.HttpMethod,
GetSupportedVersions(controllerType, method, setting),
allowAnonymous
allowAnonymous,
implementFrom
)
);
@ -351,6 +361,19 @@ namespace Volo.Abp.AspNetCore.Mvc
return ModuleApiDescriptionModel.DefaultRemoteServiceName;
}
private string FindGroupName(Type controllerType)
{
var controllerNameAttribute =
controllerType.GetCustomAttributes().OfType<ControllerNameAttribute>().FirstOrDefault();
if (controllerNameAttribute?.Name != null)
{
return controllerNameAttribute.Name;
}
return null;
}
[CanBeNull]
private ConventionalControllerSetting FindSetting(Type controllerType)
{

13
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs

@ -45,7 +45,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
{
RemoveDuplicateControllers(application);
foreach (var controller in application.Controllers)
foreach (var controller in GetControllers(application))
{
var controllerType = controller.ControllerType.AsType();
@ -71,11 +71,16 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
}
}
protected virtual IList<ControllerModel> GetControllers(ApplicationModel application)
{
return application.Controllers;
}
protected virtual void RemoveDuplicateControllers(ApplicationModel application)
{
var controllerModelsToRemove = new List<ControllerModel>();
foreach (var controllerModel in application.Controllers)
foreach (var controllerModel in GetControllers(application))
{
if (!controllerModel.ControllerType.IsDefined(typeof(ExposeServicesAttribute), false))
{
@ -90,7 +95,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
var exposeServicesAttr = ReflectionHelper.GetSingleAttributeOrDefault<ExposeServicesAttribute>(controllerModel.ControllerType);
if (exposeServicesAttr.IncludeSelf)
{
var exposedControllerModels = application.Controllers
var exposedControllerModels = GetControllers(application)
.Where(cm => exposeServicesAttr.ServiceTypes.Contains(cm.ControllerType))
.ToArray();
@ -109,7 +114,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
continue;
}
var baseControllerModels = application.Controllers
var baseControllerModels = GetControllers(application)
.Where(cm => baseControllerTypes.Contains(cm.ControllerType))
.ToArray();

24
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs

@ -5,10 +5,12 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Http;
@ -55,17 +57,10 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{
//TODO: Trigger an AbpExceptionHandled event or something like that.
context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
context.HttpContext.Response.StatusCode = (int) context
.GetRequiredService<IHttpExceptionStatusCodeFinder>()
.GetStatusCode(context.HttpContext, context.Exception);
var exceptionHandlingOptions = context.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
var exceptionToErrorInfoConverter = context.GetRequiredService<IExceptionToErrorInfoConverter>();
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, exceptionHandlingOptions.SendExceptionsDetailsToClients);
context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo));
var logLevel = context.Exception.GetLogLevel();
var remoteServiceErrorInfoBuilder = new StringBuilder();
@ -80,6 +75,21 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
await context.GetRequiredService<IExceptionNotifier>().NotifyAsync(new ExceptionNotificationContext(context.Exception));
if (context.Exception is AbpAuthorizationException)
{
await context.HttpContext.RequestServices.GetRequiredService<IAbpAuthorizationExceptionHandler>()
.HandleAsync(context.Exception.As<AbpAuthorizationException>(), context.HttpContext);
}
else
{
context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
context.HttpContext.Response.StatusCode = (int) context
.GetRequiredService<IHttpExceptionStatusCodeFinder>()
.GetStatusCode(context.HttpContext, context.Exception);
context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo));
}
context.Exception = null; //Handled!
}
}

24
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs

@ -5,10 +5,12 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Http;
@ -67,17 +69,10 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{
//TODO: Trigger an AbpExceptionHandled event or something like that.
context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
context.HttpContext.Response.StatusCode = (int) context
.GetRequiredService<IHttpExceptionStatusCodeFinder>()
.GetStatusCode(context.HttpContext, context.Exception);
var exceptionHandlingOptions = context.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
var exceptionToErrorInfoConverter = context.GetRequiredService<IExceptionToErrorInfoConverter>();
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, exceptionHandlingOptions.SendExceptionsDetailsToClients);
context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo));
var logLevel = context.Exception.GetLogLevel();
var remoteServiceErrorInfoBuilder = new StringBuilder();
@ -91,6 +86,21 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
await context.GetRequiredService<IExceptionNotifier>().NotifyAsync(new ExceptionNotificationContext(context.Exception));
if (context.Exception is AbpAuthorizationException)
{
await context.HttpContext.RequestServices.GetRequiredService<IAbpAuthorizationExceptionHandler>()
.HandleAsync(context.Exception.As<AbpAuthorizationException>(), context.HttpContext);
}
else
{
context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
context.HttpContext.Response.StatusCode = (int) context
.GetRequiredService<IHttpExceptionStatusCodeFinder>()
.GetStatusCode(context.HttpContext, context.Exception);
context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo));
}
context.Exception = null; //Handled!
}
}

7
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Response/AbpNoContentActionFilter.cs

@ -18,8 +18,9 @@ namespace Volo.Abp.AspNetCore.Mvc.Response
await next();
if (context.HttpContext.Response.StatusCode == (int)HttpStatusCode.OK
&& context.Result == null)
if (!context.HttpContext.Response.HasStarted &&
context.HttpContext.Response.StatusCode == (int)HttpStatusCode.OK &&
context.Result == null)
{
var returnType = context.ActionDescriptor.GetReturnType();
if (returnType == typeof(Task) || returnType == typeof(void))
@ -29,4 +30,4 @@ namespace Volo.Abp.AspNetCore.Mvc.Response
}
}
}
}
}

2
framework/src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Serilog</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Serilog</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.SignalR/Volo.Abp.AspNetCore.SignalR.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.SignalR</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.SignalR</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.TestBase/Volo.Abp.AspNetCore.TestBase.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.TestBase</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.TestBase</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

6
framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/DynamicProxying/AspNetCoreTestDynamicProxyHttpClientFactory.cs → framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/DynamicProxying/AspNetCoreTestProxyHttpClientFactory.cs

@ -1,15 +1,17 @@
using System.Net.Http;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.DynamicProxying;
using Volo.Abp.Http.Client.Proxying;
namespace Volo.Abp.AspNetCore.TestBase.DynamicProxying
{
[Dependency(ReplaceServices = true)]
public class AspNetCoreTestDynamicProxyHttpClientFactory : IDynamicProxyHttpClientFactory, ITransientDependency
public class AspNetCoreTestProxyHttpClientFactory : IProxyHttpClientFactory, ITransientDependency
{
private readonly ITestServerAccessor _testServerAccessor;
public AspNetCoreTestDynamicProxyHttpClientFactory(
public AspNetCoreTestProxyHttpClientFactory(
ITestServerAccessor testServerAccessor)
{
_testServerAccessor = testServerAccessor;

17
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Http/AbpHttpRequestExtensions.cs

@ -1,23 +1,18 @@
using JetBrains.Annotations;
using System;
using JetBrains.Annotations;
using Microsoft.Net.Http.Headers;
using Volo.Abp;
namespace Microsoft.AspNetCore.Http
{
public static class AbpHttpRequestExtensions
{
private const string RequestedWithHeader = "X-Requested-With";
private const string XmlHttpRequest = "XMLHttpRequest";
public static bool IsAjax([NotNull]this HttpRequest request)
{
Check.NotNull(request, nameof(request));
if (request.Headers == null)
{
return false;
}
return request.Headers[RequestedWithHeader] == XmlHttpRequest;
return string.Equals(request.Query[HeaderNames.XRequestedWith], "XMLHttpRequest", StringComparison.Ordinal) ||
string.Equals(request.Headers[HeaderNames.XRequestedWith], "XMLHttpRequest", StringComparison.Ordinal);
}
public static bool CanAccept([NotNull]this HttpRequest request, [NotNull] string contentType)
@ -25,7 +20,7 @@ namespace Microsoft.AspNetCore.Http
Check.NotNull(request, nameof(request));
Check.NotNull(contentType, nameof(contentType));
return request.Headers["Accept"].ToString().Contains(contentType);
return request.Headers[HeaderNames.Accept].ToString().Contains(contentType, StringComparison.OrdinalIgnoreCase);
}
}
}

2
framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore</AssemblyName>
<PackageId>Volo.Abp.AspNetCore</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

7
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpAuthorizationExceptionHandlerOptions.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class AbpAuthorizationExceptionHandlerOptions
{
public string AuthenticationScheme { get; set; }
}
}

45
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs

@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Http;
@ -58,30 +59,38 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling
{
_logger.LogException(exception);
var errorInfoConverter = httpContext.RequestServices.GetRequiredService<IExceptionToErrorInfoConverter>();
var statusCodeFinder = httpContext.RequestServices.GetRequiredService<IHttpExceptionStatusCodeFinder>();
var jsonSerializer = httpContext.RequestServices.GetRequiredService<IJsonSerializer>();
var options = httpContext.RequestServices.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
httpContext.Response.Clear();
httpContext.Response.StatusCode = (int)statusCodeFinder.GetStatusCode(httpContext, exception);
httpContext.Response.OnStarting(_clearCacheHeadersDelegate, httpContext.Response);
httpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
await httpContext.Response.WriteAsync(
jsonSerializer.Serialize(
new RemoteServiceErrorResponse(
errorInfoConverter.Convert(exception, options.SendExceptionsDetailsToClients)
)
)
);
await httpContext
.RequestServices
.GetRequiredService<IExceptionNotifier>()
.NotifyAsync(
new ExceptionNotificationContext(exception)
);
if (exception is AbpAuthorizationException)
{
await httpContext.RequestServices.GetRequiredService<IAbpAuthorizationExceptionHandler>()
.HandleAsync(exception.As<AbpAuthorizationException>(), httpContext);
}
else
{
var errorInfoConverter = httpContext.RequestServices.GetRequiredService<IExceptionToErrorInfoConverter>();
var statusCodeFinder = httpContext.RequestServices.GetRequiredService<IHttpExceptionStatusCodeFinder>();
var jsonSerializer = httpContext.RequestServices.GetRequiredService<IJsonSerializer>();
var options = httpContext.RequestServices.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
httpContext.Response.Clear();
httpContext.Response.StatusCode = (int)statusCodeFinder.GetStatusCode(httpContext, exception);
httpContext.Response.OnStarting(_clearCacheHeadersDelegate, httpContext.Response);
httpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
await httpContext.Response.WriteAsync(
jsonSerializer.Serialize(
new RemoteServiceErrorResponse(
errorInfoConverter.Convert(exception, options.SendExceptionsDetailsToClients)
)
)
);
}
}
private Task ClearCacheHeaders(object state)

67
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultAbpAuthorizationExceptionHandler.cs

@ -0,0 +1,67 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class DefaultAbpAuthorizationExceptionHandler : IAbpAuthorizationExceptionHandler, ITransientDependency
{
public virtual async Task HandleAsync(AbpAuthorizationException exception, HttpContext httpContext)
{
var handlerOptions = httpContext.RequestServices.GetRequiredService<IOptions<AbpAuthorizationExceptionHandlerOptions>>().Value;
var isAuthenticated = httpContext.User.Identity?.IsAuthenticated ?? false;
var authenticationSchemeProvider = httpContext.RequestServices.GetRequiredService<IAuthenticationSchemeProvider>();
AuthenticationScheme scheme = null;
if (!handlerOptions.AuthenticationScheme.IsNullOrWhiteSpace())
{
scheme = await authenticationSchemeProvider.GetSchemeAsync(handlerOptions.AuthenticationScheme);
if (scheme == null)
{
throw new AbpException($"No authentication scheme named {handlerOptions.AuthenticationScheme} was found.");
}
}
else
{
if (isAuthenticated)
{
scheme = await authenticationSchemeProvider.GetDefaultForbidSchemeAsync();
if (scheme == null)
{
throw new AbpException($"There was no DefaultForbidScheme found.");
}
}
else
{
scheme = await authenticationSchemeProvider.GetDefaultChallengeSchemeAsync();
if (scheme == null)
{
throw new AbpException($"There was no DefaultChallengeScheme found.");
}
}
}
var handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
var handler = await handlers.GetHandlerAsync(httpContext, scheme.Name);
if (handler == null)
{
throw new AbpException($"No handler of {scheme.Name} was found.");
}
if (isAuthenticated)
{
await handler.ForbidAsync(null);
}
else
{
await handler.ChallengeAsync(null);
}
}
}
}

11
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IAbpAuthorizationExceptionHandler.cs

@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Volo.Abp.Authorization;
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public interface IAbpAuthorizationExceptionHandler
{
Task HandleAsync(AbpAuthorizationException exception, HttpContext httpContext);
}
}

3
framework/src/Volo.Abp.Auditing.Contracts/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
framework/src/Volo.Abp.Auditing.Contracts/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

21
framework/src/Volo.Abp.Auditing.Contracts/Volo.Abp.Auditing.Contracts.csproj

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.Auditing.Contracts</AssemblyName>
<PackageId>Volo.Abp.Auditing.Contracts</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
</ItemGroup>
</Project>

9
framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/AbpAuditingContractsModule.cs

@ -0,0 +1,9 @@
using Volo.Abp.Modularity;
namespace Volo.Abp.Auditing
{
public class AbpAuditingContractsModule : AbpModule
{
}
}

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditedAttribute.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/AuditedAttribute.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/DisableAuditingAttribute.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/DisableAuditingAttribute.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeType.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/EntityChangeType.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditedObject.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IAuditedObject.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingEnabled.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IAuditingEnabled.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/ICreationAuditedObject.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/ICreationAuditedObject.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IDeletionAuditedObject.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IDeletionAuditedObject.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IFullAuditedObject.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IFullAuditedObject.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IHasCreationTime.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasCreationTime.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IHasDeletionTime.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasDeletionTime.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IHasModificationTime.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasModificationTime.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMayHaveCreator.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IMayHaveCreator.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IModificationAuditedObject.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IModificationAuditedObject.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMustHaveCreator.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IMustHaveCreator.cs

1
framework/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj

@ -15,6 +15,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Auditing.Contracts\Volo.Abp.Auditing.Contracts.csproj" />
<ProjectReference Include="..\Volo.Abp.Data\Volo.Abp.Data.csproj" />
<ProjectReference Include="..\Volo.Abp.Json\Volo.Abp.Json.csproj" />
<ProjectReference Include="..\Volo.Abp.MultiTenancy\Volo.Abp.MultiTenancy.csproj" />

3
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs

@ -15,7 +15,8 @@ namespace Volo.Abp.Auditing
typeof(AbpTimingModule),
typeof(AbpSecurityModule),
typeof(AbpThreadingModule),
typeof(AbpMultiTenancyModule)
typeof(AbpMultiTenancyModule),
typeof(AbpAuditingContractsModule)
)]
public class AbpAuditingModule : AbpModule
{

4
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs

@ -136,8 +136,8 @@ namespace Volo.Abp.Auditing
UserName = CurrentUser.UserName,
ClientId = CurrentClient.Id,
CorrelationId = CorrelationIdProvider.Get(),
//ImpersonatorUserId = AbpSession.ImpersonatorUserId, //TODO: Impersonation system is not available yet!
//ImpersonatorTenantId = AbpSession.ImpersonatorTenantId,
ImpersonatorUserId = CurrentUser.FindImpersonatorUserId(),
ImpersonatorTenantId = CurrentUser.FindImpersonatorTenantId(),
ExecutionTime = Clock.Now
};

25
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/PermissionsRequirement.cs

@ -0,0 +1,25 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authorization;
namespace Volo.Abp.Authorization
{
public class PermissionsRequirement : IAuthorizationRequirement
{
public string[] PermissionNames { get; }
public bool RequiresAll { get; }
public PermissionsRequirement([NotNull]string[] permissionNames, bool requiresAll)
{
Check.NotNull(permissionNames, nameof(permissionNames));
PermissionNames = permissionNames;
RequiresAll = requiresAll;
}
public override string ToString()
{
return $"PermissionsRequirement: {string.Join(", ", PermissionNames)}";
}
}
}

31
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/PermissionsRequirementHandler.cs

@ -0,0 +1,31 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Authorization.Permissions;
namespace Volo.Abp.Authorization
{
public class PermissionsRequirementHandler : AuthorizationHandler<PermissionsRequirement>
{
private readonly IPermissionChecker _permissionChecker;
public PermissionsRequirementHandler(IPermissionChecker permissionChecker)
{
_permissionChecker = permissionChecker;
}
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
PermissionsRequirement requirement)
{
var multiplePermissionGrantResult = await _permissionChecker.IsGrantedAsync(context.User, requirement.PermissionNames);
if (requirement.RequiresAll ?
multiplePermissionGrantResult.AllGranted :
multiplePermissionGrantResult.Result.Any(x => x.Value == PermissionGrantResult.Granted))
{
context.Succeed(requirement);
}
}
}
}

1
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs

@ -31,6 +31,7 @@ namespace Volo.Abp.Authorization
context.Services.AddAuthorizationCore();
context.Services.AddSingleton<IAuthorizationHandler, PermissionRequirementHandler>();
context.Services.AddSingleton<IAuthorizationHandler, PermissionsRequirementHandler>();
context.Services.TryAddTransient<DefaultAuthorizationPolicyProvider>();

2
framework/src/Volo.Abp.Autofac.WebAssembly/Volo.Abp.Autofac.WebAssembly.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>

2
framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj

@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.1.0" />
<PackageReference Include="Autofac" Version="6.2.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.1.0" />
<PackageReference Include="Autofac.Extras.DynamicProxy" Version="6.0.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="$(MicrosoftPackageVersion)" />

6
framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/AbpRabbitMqBackgroundJobOptions.cs

@ -15,10 +15,16 @@ namespace Volo.Abp.BackgroundJobs.RabbitMQ
/// </summary>
public string DefaultQueueNamePrefix { get; set; }
/// <summary>
/// Default value: "AbpBackgroundJobsDelayed."
/// </summary>
public string DefaultDelayedQueueNamePrefix { get; set;}
public AbpRabbitMqBackgroundJobOptions()
{
JobQueues = new Dictionary<Type, JobQueueConfiguration>();
DefaultQueueNamePrefix = "AbpBackgroundJobs.";
DefaultDelayedQueueNamePrefix = "AbpBackgroundJobsDelayed.";
}
}
}

22
framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
@ -64,7 +65,8 @@ namespace Volo.Abp.BackgroundJobs.RabbitMQ
return AbpRabbitMqBackgroundJobOptions.JobQueues.GetOrDefault(typeof(TArgs)) ??
new JobQueueConfiguration(
typeof(TArgs),
AbpRabbitMqBackgroundJobOptions.DefaultQueueNamePrefix + JobConfiguration.JobName
AbpRabbitMqBackgroundJobOptions.DefaultQueueNamePrefix + JobConfiguration.JobName,
AbpRabbitMqBackgroundJobOptions.DefaultDelayedQueueNamePrefix + JobConfiguration.JobName
);
}
@ -133,6 +135,9 @@ namespace Volo.Abp.BackgroundJobs.RabbitMQ
var result = QueueConfiguration.Declare(ChannelAccessor.Channel);
Logger.LogDebug($"RabbitMQ Queue '{QueueConfiguration.QueueName}' has {result.MessageCount} messages and {result.ConsumerCount} consumers.");
// Declare delayed queue
QueueConfiguration.DeclareDelayed(ChannelAccessor.Channel);
if (AbpBackgroundJobOptions.IsJobExecutionEnabled)
{
Consumer = new AsyncEventingBasicConsumer(ChannelAccessor.Channel);
@ -154,12 +159,21 @@ namespace Volo.Abp.BackgroundJobs.RabbitMQ
BackgroundJobPriority priority = BackgroundJobPriority.Normal,
TimeSpan? delay = null)
{
//TODO: How to handle priority & delay?
//TODO: How to handle priority
var routingKey = QueueConfiguration.QueueName;
var basicProperties = CreateBasicPropertiesToPublish();
if (delay.HasValue)
{
routingKey = QueueConfiguration.DelayedQueueName;
basicProperties.Expiration = delay.Value.TotalMilliseconds.ToString();
}
ChannelAccessor.Channel.BasicPublish(
exchange: "",
routingKey: QueueConfiguration.QueueName,
basicProperties: CreateBasicPropertiesToPublish(),
routingKey: routingKey,
basicProperties: basicProperties,
body: Serializer.Serialize(args)
);

35
framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueueConfiguration.cs

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using RabbitMQ.Client;
using Volo.Abp.RabbitMQ;
namespace Volo.Abp.BackgroundJobs.RabbitMQ
@ -9,21 +11,42 @@ namespace Volo.Abp.BackgroundJobs.RabbitMQ
public string ConnectionName { get; set; }
public string DelayedQueueName { get; set; }
public JobQueueConfiguration(
Type jobArgsType,
string queueName,
Type jobArgsType,
string queueName,
string delayedQueueName,
string connectionName = null,
bool durable = true,
bool exclusive = false,
bool autoDelete = false)
: base(
queueName,
durable,
exclusive,
queueName,
durable,
exclusive,
autoDelete)
{
JobArgsType = jobArgsType;
ConnectionName = connectionName;
DelayedQueueName = delayedQueueName;
}
public virtual QueueDeclareOk DeclareDelayed(IModel channel)
{
var delayedArguments = new Dictionary<string, object>(Arguments)
{
["x-dead-letter-routing-key"] = QueueName,
["x-dead-letter-exchange"] = string.Empty
};
return channel.QueueDeclare(
queue: DelayedQueueName,
durable: Durable,
exclusive: Exclusive,
autoDelete: AutoDelete,
arguments: delayedArguments
);
}
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save