Browse Source

Merge remote-tracking branch 'abpframework/master' into Translate

pull/992/head
maliming 8 years ago
parent
commit
5ec6f33efc
  1. 16
      abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs
  2. 4
      abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/HomePageLayout.cshtml
  3. 4
      abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/Layout.cshtml
  4. 4
      abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/LayoutEmpty.cshtml
  5. 39
      abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/NugetReferenceReplaceStep.cs
  6. 1
      build-all.ps1
  7. 14
      docs/en/Multi-Tenancy.md
  8. 26
      docs/en/Nightly-Builds.md
  9. 26
      docs/en/Samples/Microservice-Demo.md
  10. 4
      docs/en/docs-nav.json
  11. BIN
      docs/en/images/microservice-sample-update-database-authserver.png
  12. BIN
      docs/en/images/microservice-sample-update-database-products.png
  13. BIN
      docs/en/images/night-build-add-nuget-package.png
  14. BIN
      docs/en/images/night-build-add-nuget-source.png
  15. 14
      docs/zh-Hans/Multi-Tenancy.md
  16. 5
      framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs
  17. 2
      framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyOptionsExtensions.cs
  18. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pt-BR.json
  19. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs
  20. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml
  21. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml
  22. 34
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
  23. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml
  24. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml
  25. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj
  26. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js
  27. 30
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js
  28. 1
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs
  29. 6
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs
  30. 7
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs
  31. 15
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs
  32. 12
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs
  33. 13
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs
  34. 6
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs
  35. 14
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs
  36. 2
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptor.cs
  37. 23
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs
  38. 1
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RolePermissionValueProvider.cs
  39. 1
      framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj
  40. 9
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs
  41. 56
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
  42. 9
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCacheSerializer.cs
  43. 26
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Utf8JsonDistributedCacheSerializer.cs
  44. 2
      framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj
  45. 62
      framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpInterceptorAdapter.cs
  46. 10
      framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs
  47. 4
      framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
  48. 97
      framework/src/Volo.Abp.Core/Volo/Abp/Check.cs
  49. 12
      framework/src/Volo.Abp.Core/Volo/Abp/RandomHelper.cs
  50. 2
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AsyncCrudAppService.cs
  51. 11
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs
  52. 25
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderConfiguration.cs
  53. 4
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs
  54. 14
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSenderConfiguration.cs
  55. 3
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSender.cs
  56. 18
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSenderConfiguration.cs
  57. 32
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs
  58. 42
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSenderConfiguration.cs
  59. 5
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs
  60. 1
      framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj
  61. 4
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs
  62. 13
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
  63. 4
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs
  64. 14
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs
  65. 25
      framework/src/Volo.Abp.MultiTenancy/System/Security/Principal/AbpClaimsIdentityExtensions.cs
  66. 28
      framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentClaimsPrincipalTenantResolveContributor.cs
  67. 7
      framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantExtensions.cs
  68. 24
      framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentUserTenantResolveContributor.cs
  69. 5
      framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantResolveResultAccessor.cs
  70. 13
      framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantResolveResultAccessor.cs
  71. 2
      framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolveOptions.cs
  72. 9
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs
  73. 4
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlOptions.cs
  74. 80
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs
  75. 5
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs
  76. 4
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json
  77. 16
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json
  78. 6
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json
  79. 1
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json
  80. 16
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs
  81. 3
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/pt-BR.json
  82. 4
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/_Layout.cshtml
  83. 2
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/CachedTestObject.cs
  84. 2
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/SimpleAsyncInterceptor.cs
  85. 3
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/en.json
  86. 3
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/pt-BR.json
  87. 22
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs
  88. 2
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
  89. 2
      modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs
  90. 14
      modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json
  91. 2
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs
  92. 41
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs
  93. 4
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLogAction.cs
  94. 23
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityChange.cs
  95. 8
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityPropertyChange.cs
  96. 11
      modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AuditLogRepository_Tests.cs
  97. 11
      modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditLogRepository_Tests.cs
  98. 232
      modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogRepository_Tests.cs
  99. 13
      modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogging_Repository_Resolve_Tests.cs
  100. 1
      modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditStore_Basic_Tests.cs

16
abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs

@ -1,5 +1,4 @@
using System.IO; using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -14,7 +13,6 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theming; using Volo.Abp.AspNetCore.Mvc.UI.Theming;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Autofac; using Volo.Abp.Autofac;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
@ -182,18 +180,8 @@ namespace Volo.AbpWebSite
AsyncHelper.RunSync(async () => AsyncHelper.RunSync(async () =>
{ {
await scope.ServiceProvider await scope.ServiceProvider
.GetRequiredService<IIdentityDataSeeder>() .GetRequiredService<IDataSeeder>()
.SeedAsync( .SeedAsync();
"1q2w3E*"
);
await scope.ServiceProvider
.GetRequiredService<IPermissionDataSeeder>()
.SeedAsync(
RolePermissionValueProvider.ProviderName,
"admin",
IdentityPermissions.GetAll().Union(BloggingPermissions.GetAll())
);
}); });
} }
} }

4
abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/HomePageLayout.cshtml

@ -17,7 +17,7 @@
<title>@(ViewBag.Title == null ? "abp.io" : ViewBag.Title)</title> <title>@(ViewBag.Title == null ? "abp.io" : ViewBag.Title)</title>
@await Component.InvokeAsync(typeof(StandardMetaViewComponent)) @await Component.InvokeAsync(typeof(StandardMetaViewComponent))
<abp-style-bundle name="@AbpIoBundles.Styles.Global" /> <abp-style-bundle name="@AbpIoBundles.Styles.Global" />
@RenderSection("styles", false) @await RenderSectionAsync("styles", false)
</head> </head>
<body> <body>
@ -47,7 +47,7 @@
gtag('config', 'UA-49982725-4'); gtag('config', 'UA-49982725-4');
</script> </script>
@RenderSection("scripts", false) @await RenderSectionAsync("scripts", false)
</body> </body>
</html> </html>

4
abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/Layout.cshtml

@ -17,7 +17,7 @@
<title>@(ViewBag.Title == null ? "abp.io" : ViewBag.Title)</title> <title>@(ViewBag.Title == null ? "abp.io" : ViewBag.Title)</title>
@await Component.InvokeAsync(typeof(StandardMetaViewComponent)) @await Component.InvokeAsync(typeof(StandardMetaViewComponent))
<abp-style-bundle name="@AbpIoBundles.Styles.Global" /> <abp-style-bundle name="@AbpIoBundles.Styles.Global" />
@RenderSection("styles", false) @await RenderSectionAsync("styles", false)
</head> </head>
<body> <body>
@ -58,7 +58,7 @@
gtag('config', 'UA-49982725-4'); gtag('config', 'UA-49982725-4');
</script> </script>
@RenderSection("scripts", false) @await RenderSectionAsync("scripts", false)
</body> </body>
</html> </html>

4
abp_io/src/Volo.AbpWebSite.Web/Pages/Shared/LayoutEmpty.cshtml

@ -18,7 +18,7 @@
<title>@(ViewBag.Title == null ? "abp.io" : ViewBag.Title)</title> <title>@(ViewBag.Title == null ? "abp.io" : ViewBag.Title)</title>
@await Component.InvokeAsync(typeof(StandardMetaViewComponent)) @await Component.InvokeAsync(typeof(StandardMetaViewComponent))
<abp-style-bundle name="@StandardBundles.Styles.Global" /> <abp-style-bundle name="@StandardBundles.Styles.Global" />
@RenderSection("styles", false) @await RenderSectionAsync("styles", false)
</head> </head>
<body class="abp-empty-layout"> <body class="abp-empty-layout">
@ -41,7 +41,7 @@
gtag('config', 'UA-49982725-4'); gtag('config', 'UA-49982725-4');
</script> </script>
@RenderSection("scripts", false) @await RenderSectionAsync("scripts", false)
</body> </body>
</html> </html>

39
abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/NugetReferenceReplaceStep.cs

@ -16,7 +16,8 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps
new NugetReferenceReplacer( new NugetReferenceReplacer(
context.Files, context.Files,
"MyCompanyName", "MyCompanyName",
"MyProjectName" "MyProjectName",
context.Template.Version
).Run(); ).Run();
} }
@ -27,12 +28,12 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps
private readonly string _projectNamePlaceHolder; private readonly string _projectNamePlaceHolder;
private readonly string _latestNugetPackageVersion; private readonly string _latestNugetPackageVersion;
public NugetReferenceReplacer(List<FileEntry> entries, string companyNamePlaceHolder, string projectNamePlaceHolder) public NugetReferenceReplacer(List<FileEntry> entries, string companyNamePlaceHolder, string projectNamePlaceHolder, string latestNugetPackageVersion)
{ {
_entries = entries; _entries = entries;
_companyNamePlaceHolder = companyNamePlaceHolder; _companyNamePlaceHolder = companyNamePlaceHolder;
_projectNamePlaceHolder = projectNamePlaceHolder; _projectNamePlaceHolder = projectNamePlaceHolder;
_latestNugetPackageVersion = GetLatestNugetPackageVersion(); _latestNugetPackageVersion = latestNugetPackageVersion;
} }
public void Run() public void Run()
@ -110,38 +111,6 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps
stream.Position = 0; stream.Position = 0;
return stream; return stream;
} }
private string GetLatestNugetPackageVersion()
{
//TODO: This should get it from the related release! Not always from the master!
var commonPropsUrl = "https://raw.githubusercontent.com/abpframework/abp/master/common.props";
var content = "";
using (var webClient = new WebClient())
{
try
{
content = webClient.DownloadString(commonPropsUrl);
}
catch (Exception)
{
throw new Exception("The Common.pros doesn't exist on github or removed to anywhere else.");
}
}
var doc = new HtmlDocument();
doc.Load(GenerateStreamFromString(content));
try
{
return doc.DocumentNode.SelectNodes("//version").FirstOrDefault().InnerHtml.Trim();
}
catch (Exception e)
{
return "";
}
}
} }
} }
} }

1
build-all.ps1

@ -18,6 +18,7 @@ $solutionPaths = (
"modules/blogging", "modules/blogging",
"modules/audit-logging", "modules/audit-logging",
"modules/background-jobs", "modules/background-jobs",
"modules/client-simulation",
"abp_io", "abp_io",
"templates/module", "templates/module",
"templates/service", "templates/service",

14
docs/en/Multi-Tenancy.md

@ -6,17 +6,17 @@ Wikipedia [defines](https://en.wikipedia.org/wiki/Multitenancy) multi-tenancy as
> Software **Multi-tenancy** refers to a software **architecture** in which a **single instance** of a software runs on a server and serves **multiple tenants**. A tenant is a group of users who share a common access with specific privileges to the software instance. With a multitenant architecture, a software application is designed to provide every tenant a **dedicated share of the instance including its data**, configuration, user management, tenant individual functionality and non-functional properties. Multi-tenancy contrasts with multi-instance architectures, where separate software instances operate on behalf of different tenants. > Software **Multi-tenancy** refers to a software **architecture** in which a **single instance** of a software runs on a server and serves **multiple tenants**. A tenant is a group of users who share a common access with specific privileges to the software instance. With a multitenant architecture, a software application is designed to provide every tenant a **dedicated share of the instance including its data**, configuration, user management, tenant individual functionality and non-functional properties. Multi-tenancy contrasts with multi-instance architectures, where separate software instances operate on behalf of different tenants.
### Volo.Abp.MultiTenancy.Abstractions Package ### Volo.Abp.MultiTenancy Package
Volo.Abp.MultiTenancy.Abstractions package defines fundamental interfaces to make your code "multi-tenancy ready". So, install it to your project using the package manager console (PMC): Volo.Abp.MultiTenancy package defines fundamental interfaces to make your code "multi-tenancy ready". So, install it to your project using the package manager console (PMC):
```` ````
Install-Package Volo.Abp.MultiTenancy.Abstractions Install-Package Volo.Abp.MultiTenancy
```` ````
> This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually. > This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually.
Then you can add **AbpMultiTenancyAbstractionsModule** dependency to your module: Then you can add **AbpMultiTenancyModule** dependency to your module:
````C# ````C#
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
@ -24,7 +24,7 @@ using Volo.Abp.MultiTenancy;
namespace MyCompany.MyProject namespace MyCompany.MyProject
{ {
[DependsOn(typeof(AbpMultiTenancyAbstractionsModule))] [DependsOn(typeof(AbpMultiTenancyModule))]
public class MyModule : AbpModule public class MyModule : AbpModule
{ {
//... //...
@ -98,7 +98,7 @@ Volo.Abp.MultiTenancy is the actual package that makes your application multi-te
Install-Package Volo.Abp.MultiTenancy Install-Package Volo.Abp.MultiTenancy
```` ````
Then you can add **AbpMultiTenancyAbstractionsModule** dependency to your module: Then you can add **AbpMultiTenancyModule** dependency to your module:
````C# ````C#
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
@ -114,7 +114,7 @@ namespace MyCompany.MyProject
} }
```` ````
> If you add AbpMultiTenancyModule dependency to your module, then you don't need to add AbpMultiTenancyAbstractionsModule dependency separately since AbpMultiTenancyModule already depends on it. > If you add AbpMultiTenancyModule dependency to your module, then you don't need to add AbpMultiTenancyModule dependency separately since AbpMultiTenancyModule already depends on it.
#### Determining Current Tenant #### Determining Current Tenant

26
docs/en/Nightly-Builds.md

@ -0,0 +1,26 @@
# Nightly Builds
All framework & module packages are deployed to MyGet every night in weekdays. So, you can use or test the latest code without waiting the next release.
## Configure Visual Studio
> Requires Visual Studio 2017+
1. Go to `Tools > Options > NuGet Package Manager > Package Source`.
2. Click the green `+` icon.
3. Set `ABP Nightly` as *Name* and `https://www.myget.org/F/abp-nightly/api/v3/index.json` as the *Source* as shown below:
![night-build-add-nuget-source](images/night-build-add-nuget-source.png)
4. Click the `Update` button.
5. Click the `OK` button to save changes.
## Install Package
Now, you can install preview / nightly packages to your project from Nuget Browser or Package Manager Console.
![night-build-add-nuget-package](images/night-build-add-nuget-package.png)
1. In the nuget browser, select "Include prereleases".
2. Change package source to "All".
3. Search a package. You will see prereleases of the package formatted as `(VERSION)-preview(DATE)` (like *v0.16.0-preview20190401* in this sample).
4. You can click to the `Install` button to add package to your project.

26
docs/en/Samples/Microservice-Demo.md

@ -116,11 +116,29 @@ To be able to run the solution from source code, following tools should be insta
* Run `dotnet restore` from the command line inside the `samples\MicroserviceDemo` folder. * Run `dotnet restore` from the command line inside the `samples\MicroserviceDemo` folder.
* Build the solution in Visual Studio. * Build the solution in Visual Studio.
#### Restore Databases #### Create Databases
Open `MsDemo_Identity.zip` and `MsDemo_ProductManagement.zip` inside the `samples\MicroserviceDemo\databases` folder and restore to the SQL Server. MongoDB database is created dynamically, however you need to create database schemas for SQL server databases. The solution is configured to use Entity Core Code First migrations, so you can easily create databases.
> Notice that: These databases have EF Core migrations in the solution, however they don't have seed data, especially required for IdentityServer4 configuration. So, restoring the databases is much more easier. There are two SQL server databases in this solution.
##### MsDemo_Identity Database
* Right click to the `AuthServer.Host` project and click to the `Set as startup project`.
* Open the **Package Manager Console** (Tools -> Nuget Package Manager -> Package Manager Console)
* Select `AuthServer.Host` as the **Default project**.
* Run `Update-Database` command.
![microservice-sample-update-database-authserver](../images/microservice-sample-update-database-authserver.png)
##### MsDemo_ProductManagement
- Right click to the `ProductService.Host` project and click to the `Set as startup project`.
- Open the **Package Manager Console** (Tools -> Nuget Package Manager -> Package Manager Console)
- Select `ProductService.Host` as the **Default project**.
- Run `Update-Database` command.
![microservice-sample-update-database-products](../images/microservice-sample-update-database-products.png)
#### Run Projects #### Run Projects
@ -136,6 +154,8 @@ Run the projects with the following order (right click to each project, set as s
* BackendAdminApp.Host * BackendAdminApp.Host
* PublicWebSite.Host * PublicWebSite.Host
When you run projects, they will add some initial demo data to their databases.
## A Brief Overview of the Solution ## A Brief Overview of the Solution
The Visual Studio solution consists of multiple projects each have different roles in the system: The Visual Studio solution consists of multiple projects each have different roles in the system:

4
docs/en/docs-nav.json

@ -276,6 +276,10 @@
{ {
"text": "Testing" "text": "Testing"
}, },
{
"text": "Nightly Builds",
"path": "Nightly-Builds.md"
},
{ {
"text": "Contribution Guide", "text": "Contribution Guide",
"path": "Contribution/Index.md" "path": "Contribution/Index.md"

BIN
docs/en/images/microservice-sample-update-database-authserver.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
docs/en/images/microservice-sample-update-database-products.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
docs/en/images/night-build-add-nuget-package.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
docs/en/images/night-build-add-nuget-source.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

14
docs/zh-Hans/Multi-Tenancy.md

@ -6,17 +6,17 @@ ABP的多租户模块提供了创建多租户应用程序的基本功能.
> 软件多租户技术指的是一种软件架构,这种架构可以使用软件的单实例运行并为多个租户提供服务.租户是通过软件实例的特定权限共享通用访问的一组用户.使用多租户架构,软件应用为每个租户提供实例的专用共享,包括实例的数据、配置、用户管理、租户的私有功能和非功能属性.多租户与多实例架构形成对比,将软件实例的行为根据不同的租户分割开来. > 软件多租户技术指的是一种软件架构,这种架构可以使用软件的单实例运行并为多个租户提供服务.租户是通过软件实例的特定权限共享通用访问的一组用户.使用多租户架构,软件应用为每个租户提供实例的专用共享,包括实例的数据、配置、用户管理、租户的私有功能和非功能属性.多租户与多实例架构形成对比,将软件实例的行为根据不同的租户分割开来.
### Volo.Abp.MultiTenancy.Abstractions ### Volo.Abp.MultiTenancy
Volo.Abp.MultiTenancy.Abstractions定义了一些基础接口让你的代码"multi-tenancy ready",使用包管理器控制台(PMC)将它安装到你的项目中: Volo.Abp.MultiTenancy"multi-tenancy ready",使用包管理器控制台(PMC)将它安装到你的项目中:
```` ````
Install-Package Volo.Abp.MultiTenancy.Abstractions Install-Package Volo.Abp.MultiTenancy
```` ````
> 这个包默认安装在了快速启动模板中.所以,大多数情况下,你不需要手动安装它. > 这个包默认安装在了快速启动模板中.所以,大多数情况下,你不需要手动安装它.
然后你可以添加 **AbpMultiTenancyAbstractionsModule** 依赖到你的模块: 然后你可以添加 **AbpMultiTenancyModule** 依赖到你的模块:
````C# ````C#
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
@ -24,7 +24,7 @@ using Volo.Abp.MultiTenancy;
namespace MyCompany.MyProject namespace MyCompany.MyProject
{ {
[DependsOn(typeof(AbpMultiTenancyAbstractionsModule))] [DependsOn(typeof(AbpMultiTenancyModule))]
public class MyModule : AbpModule public class MyModule : AbpModule
{ {
//... //...
@ -98,7 +98,7 @@ Volo.Abp.MultiTenancy 才是让你的程序实现多租户的真正的包.使用
Install-Package Volo.Abp.MultiTenancy Install-Package Volo.Abp.MultiTenancy
```` ````
然后添加 **AbpMultiTenancyAbstractionsModule** 依赖到你的模块中: 然后添加 **AbpMultiTenancyModule** 依赖到你的模块中:
````C# ````C#
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
@ -114,7 +114,7 @@ namespace MyCompany.MyProject
} }
```` ````
> 如果你添加了AbpMultiTenancyModule依赖,就不需要再另外添加AbpMultiTenancyAbstractionsModule依赖了,因为AbpMultiTenancyModule已经依赖它了. > 如果你添加了AbpMultiTenancyModule依赖,就不需要再另外添加AbpMultiTenancyModule依赖了,因为AbpMultiTenancyModule已经依赖它了.
#### 确定当前租户 #### 确定当前租户

5
framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs

@ -1,13 +1,12 @@
using JetBrains.Annotations; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
namespace Volo.Abp.AspNetCore.MultiTenancy namespace Volo.Abp.AspNetCore.MultiTenancy
{ {
[Dependency(ReplaceServices = true)]
public class HttpContextTenantResolveResultAccessor : ITenantResolveResultAccessor, ITransientDependency public class HttpContextTenantResolveResultAccessor : ITenantResolveResultAccessor, ITransientDependency
{ {
[CanBeNull]
public TenantResolveResult Result public TenantResolveResult Result
{ {
get get

2
framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyOptionsExtensions.cs

@ -8,7 +8,7 @@ namespace Volo.Abp.MultiTenancy
public static void AddDomainTenantResolver(this TenantResolveOptions options, string domainFormat) public static void AddDomainTenantResolver(this TenantResolveOptions options, string domainFormat)
{ {
options.TenantResolvers.InsertAfter( options.TenantResolvers.InsertAfter(
r => r is CurrentClaimsPrincipalTenantResolveContributor, r => r is CurrentUserTenantResolveContributor,
new DomainTenantResolveContributor(domainFormat) new DomainTenantResolveContributor(domainFormat)
); );
} }

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pt-BR.json

@ -1,9 +1,11 @@
{ {
"culture": "pt-BR", "culture": "pt-BR",
"texts": { "texts": {
"GivenTenantIsNotAvailable": "Tenant não está disponível: {0}", "GivenTenantIsNotAvailable": "Inquilino não está disponível: {0}",
"SwitchTenant": "Trocar tenant", "Tenant": "Inquilino",
"Switch": "trocar",
"Name": "Nome", "Name": "Nome",
"SwitchTenantHint": "Deixe o campo de nome em branco para alternar para o lado do host." "SwitchTenantHint": "Deixe o campo de nome em branco para alternar para o lado do host.",
"NotSelected": "Não selecionado"
} }
} }

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs

@ -1,4 +1,5 @@
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Toolbars; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Toolbars;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
@ -11,7 +12,8 @@ using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic
{ {
[DependsOn( [DependsOn(
typeof(AbpAspNetCoreMvcUiThemeSharedModule) typeof(AbpAspNetCoreMvcUiThemeSharedModule),
typeof(AbpAspNetCoreMvcUiMultiTenancyModule)
)] )]
public class AbpAspNetCoreMvcUiBasicThemeModule : AbpModule public class AbpAspNetCoreMvcUiBasicThemeModule : AbpModule
{ {

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml

@ -7,7 +7,7 @@
var disabled = menuItem.IsDisabled ? "disabled" : string.Empty; var disabled = menuItem.IsDisabled ? "disabled" : string.Empty;
if (menuItem.IsLeaf) if (menuItem.IsLeaf)
{ {
@if (menuItem.Url != null) if (menuItem.Url != null)
{ {
<li class="nav-item @cssClass @disabled" @elementId> <li class="nav-item @cssClass @disabled" @elementId>
<a class="nav-link" href="@(menuItem.Url ?? "#")"> <a class="nav-link" href="@(menuItem.Url ?? "#")">

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml

@ -7,7 +7,7 @@
} }
@if (Model.IsLeaf) @if (Model.IsLeaf)
{ {
@if (Model.Url != null) if (Model.Url != null)
{ {
<a class="dropdown-item @cssClass @disabled" href="@(Model.Url ?? "#")" @Html.Raw(elementId)> <a class="dropdown-item @cssClass @disabled" href="@(Model.Url ?? "#")" @Html.Raw(elementId)>
@Model.DisplayName @Model.DisplayName

34
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml

@ -1,10 +1,20 @@
@using Volo.Abp.AspNetCore.Mvc.AntiForgery @using Microsoft.Extensions.Localization
@using Microsoft.Extensions.Options
@using Volo.Abp.AspNetCore.MultiTenancy
@using Volo.Abp.AspNetCore.Mvc.AntiForgery
@using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.MainNavbar @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.MainNavbar
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.PageAlerts @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.PageAlerts
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components
@using Volo.Abp.MultiTenancy
@inject IAbpAntiForgeryManager AbpAntiForgeryManager @inject IAbpAntiForgeryManager AbpAntiForgeryManager
@inject IBrandingProvider BrandingProvider @inject IBrandingProvider BrandingProvider
@inject IOptions<MultiTenancyOptions> MultiTenancyOptions
@inject ICurrentTenant CurrentTenant
@inject IStringLocalizer<AbpUiMultiTenancyResource> MultiTenancyStringLocalizer
@inject ITenantResolveResultAccessor TenantResolveResultAccessor
@{ @{
Layout = null; Layout = null;
AbpAntiForgeryManager.SetCookie(); AbpAntiForgeryManager.SetCookie();
@ -23,7 +33,7 @@
<abp-style-bundle name="@BasicThemeBundles.Styles.Global" /> <abp-style-bundle name="@BasicThemeBundles.Styles.Global" />
@RenderSection("styles", false) @await RenderSectionAsync("styles", false)
</head> </head>
<body class="abp-account-layout"> <body class="abp-account-layout">
@ -32,6 +42,24 @@
<div class="@containerClass"> <div class="@containerClass">
<abp-row> <abp-row>
<abp-column size-md="_4" offset-md="_4"> <abp-column size-md="_4" offset-md="_4">
@if (MultiTenancyOptions.Value.IsEnabled &&
(TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(CookieTenantResolveContributor.ContributorName) == true))
{
<div class="tenant-switch-box" style="background-color: #eee; margin-bottom: 20px; color: #000; padding: 10px; text-align: center;">
<span style="color: #666;">@MultiTenancyStringLocalizer["Tenant"]: </span>
<strong>
@if (CurrentTenant.Id == null)
{
<i>@MultiTenancyStringLocalizer["NotSelected"]</i>
}
else
{
@(CurrentTenant.Name ?? CurrentTenant.Id.Value.ToString())
}
</strong>
(<a id="AbpTenantSwitchLink" style="color: #333; cursor: pointer">@MultiTenancyStringLocalizer["Switch"]</a>)
</div>
}
@(await Component.InvokeAsync<PageAlertsViewComponent>()) @(await Component.InvokeAsync<PageAlertsViewComponent>())
@RenderBody() @RenderBody()
</abp-column> </abp-column>
@ -43,7 +71,7 @@
<script type="text/javascript" src="~/Abp/ApplicationConfigurationScript"></script> <script type="text/javascript" src="~/Abp/ApplicationConfigurationScript"></script>
<script type="text/javascript" src="~/Abp/ServiceProxyScript"></script> <script type="text/javascript" src="~/Abp/ServiceProxyScript"></script>
@RenderSection("scripts", false) @await RenderSectionAsync("scripts", false)
</body> </body>
</html> </html>

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml

@ -23,7 +23,7 @@
<abp-style-bundle name="@BasicThemeBundles.Styles.Global" /> <abp-style-bundle name="@BasicThemeBundles.Styles.Global" />
@RenderSection("styles", false) @await RenderSectionAsync("styles", false)
</head> </head>
<body class="abp-application-layout"> <body class="abp-application-layout">
@ -39,7 +39,7 @@
<script type="text/javascript" src="~/Abp/ApplicationConfigurationScript"></script> <script type="text/javascript" src="~/Abp/ApplicationConfigurationScript"></script>
<script type="text/javascript" src="~/Abp/ServiceProxyScript"></script> <script type="text/javascript" src="~/Abp/ServiceProxyScript"></script>
@RenderSection("scripts", false) @await RenderSectionAsync("scripts", false)
</body> </body>
</html> </html>

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml

@ -22,7 +22,7 @@
<abp-style-bundle name="@BasicThemeBundles.Styles.Global" /> <abp-style-bundle name="@BasicThemeBundles.Styles.Global" />
@RenderSection("styles", false) @await RenderSectionAsync("styles", false)
</head> </head>
<body class="abp-empty-layout"> <body class="abp-empty-layout">
@ -36,7 +36,7 @@
<script type="text/javascript" src="~/Abp/ApplicationConfigurationScript"></script> <script type="text/javascript" src="~/Abp/ApplicationConfigurationScript"></script>
<script type="text/javascript" src="~/Abp/ServiceProxyScript"></script> <script type="text/javascript" src="~/Abp/ServiceProxyScript"></script>
@RenderSection("scripts", false) @await RenderSectionAsync("scripts", false)
</body> </body>
</html> </html>

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj

@ -31,6 +31,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy\Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" /> <ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
</ItemGroup> </ItemGroup>

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js

@ -71,6 +71,10 @@ $.validator.defaults.ignore = ''; //TODO: Would be better if we can apply only f
_$form.abpAjaxForm(); _$form.abpAjaxForm();
} }
if (_$form.attr('data-check-form-on-close') === undefined || _$form.attr('data-check-form-on-close') != 'false') {
_$form.needConfirmationOnUnsavedClose(_$modal);
}
_$form.on('abp-ajax-success', _$form.on('abp-ajax-success',
function () { function () {
_publicApi.setResult.apply(_publicApi, arguments); _publicApi.setResult.apply(_publicApi, arguments);

30
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js

@ -3,6 +3,10 @@
return; return;
} }
var localize = function (key) {
return abp.localization.getResource('AbpUi')(key);
};
/* A simple jQuery plug-in to make a button busy. */ /* A simple jQuery plug-in to make a button busy. */
$.fn.buttonBusy = function (isBusy) { $.fn.buttonBusy = function (isBusy) {
return $(this).each(function () { return $(this).each(function () {
@ -127,4 +131,30 @@
}); });
}; };
$.fn.needConfirmationOnUnsavedClose = function ($modal) {
var $form = $(this);
var formSaved = false;
var unEditedForm = JSON.stringify($form.serializeFormToObject());
$modal.on("hide.bs.modal", function (e) {
var currentForm = JSON.stringify($form.serializeFormToObject());
var thereAreUnsavedChanges = currentForm !== unEditedForm;
if (!formSaved && thereAreUnsavedChanges) {
e.preventDefault();
abp.message.confirm(localize('AreYouSureYouWantToCancelEditingWarningMessage'),
function (result) {
if (result) {
formSaved = true;
$modal.modal('hide');
}
});
}
});
$(this).on('abp-ajax-success',function () {
formSaved = true;
});
};
})(jQuery); })(jQuery);

1
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs

@ -92,6 +92,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Auditing
auditLog = auditLogScope.Log; auditLog = auditLogScope.Log;
auditLogAction = _auditingHelper.CreateAuditLogAction( auditLogAction = _auditingHelper.CreateAuditLogAction(
auditLog,
context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType(), context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType(),
context.ActionDescriptor.AsControllerActionDescriptor().MethodInfo, context.ActionDescriptor.AsControllerActionDescriptor().MethodInfo,
context.ActionArguments context.ActionArguments

6
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs

@ -1,14 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Auditing namespace Volo.Abp.Auditing
{ {
public class AuditLogActionInfo : IMultiTenant, IHasExtraProperties [Serializable]
public class AuditLogActionInfo : IHasExtraProperties
{ {
public Guid? TenantId { get; set; }
public string ServiceName { get; set; } public string ServiceName { get; set; }
public string MethodName { get; set; } public string MethodName { get; set; }

7
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs

@ -3,12 +3,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Auditing namespace Volo.Abp.Auditing
{ {
//TODO: Make serializable! [Serializable]
public class AuditLogInfo : IMultiTenant, IHasExtraProperties public class AuditLogInfo : IHasExtraProperties
{ {
public string ApplicationName { get; set; } public string ApplicationName { get; set; }
@ -18,6 +17,8 @@ namespace Volo.Abp.Auditing
public Guid? TenantId { get; set; } public Guid? TenantId { get; set; }
public string TenantName { get; set; }
public Guid? ImpersonatorUserId { get; set; } public Guid? ImpersonatorUserId { get; set; }
public Guid? ImpersonatorTenantId { get; set; } public Guid? ImpersonatorTenantId { get; set; }

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

@ -92,6 +92,7 @@ namespace Volo.Abp.Auditing
{ {
ApplicationName = Options.ApplicationName, ApplicationName = Options.ApplicationName,
TenantId = CurrentTenant.Id, TenantId = CurrentTenant.Id,
TenantName = CurrentTenant.Name,
UserId = CurrentUser.Id, UserId = CurrentUser.Id,
UserName = CurrentUser.UserName, UserName = CurrentUser.UserName,
ClientId = CurrentClient.Id, ClientId = CurrentClient.Id,
@ -106,12 +107,20 @@ namespace Volo.Abp.Auditing
return auditInfo; return auditInfo;
} }
public virtual AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, object[] arguments) public virtual AuditLogActionInfo CreateAuditLogAction(
AuditLogInfo auditLog,
Type type,
MethodInfo method,
object[] arguments)
{ {
return CreateAuditLogAction(type, method, CreateArgumentsDictionary(method, arguments)); return CreateAuditLogAction(auditLog, type, method, CreateArgumentsDictionary(method, arguments));
} }
public virtual AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, IDictionary<string, object> arguments) public virtual AuditLogActionInfo CreateAuditLogAction(
AuditLogInfo auditLog,
Type type,
MethodInfo method,
IDictionary<string, object> arguments)
{ {
var actionInfo = new AuditLogActionInfo var actionInfo = new AuditLogActionInfo
{ {

12
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs

@ -49,7 +49,7 @@ namespace Volo.Abp.Auditing
{ {
if (!ShouldIntercept(invocation, out var auditLog, out var auditLogAction)) if (!ShouldIntercept(invocation, out var auditLog, out var auditLogAction))
{ {
invocation.Proceed(); await invocation.ProceedAsync();
return; return;
} }
@ -72,7 +72,10 @@ namespace Volo.Abp.Auditing
} }
} }
protected virtual bool ShouldIntercept(IAbpMethodInvocation invocation, out AuditLogInfo auditLog, out AuditLogActionInfo auditLogAction) protected virtual bool ShouldIntercept(
IAbpMethodInvocation invocation,
out AuditLogInfo auditLog,
out AuditLogActionInfo auditLogAction)
{ {
auditLog = null; auditLog = null;
auditLogAction = null; auditLogAction = null;
@ -95,7 +98,10 @@ namespace Volo.Abp.Auditing
auditLog = auditLogScope.Log; auditLog = auditLogScope.Log;
auditLogAction = _auditingHelper.CreateAuditLogAction( auditLogAction = _auditingHelper.CreateAuditLogAction(
invocation.TargetObject.GetType(), invocation.Method, invocation.Arguments auditLog,
invocation.TargetObject.GetType(),
invocation.Method,
invocation.Arguments
); );
return true; return true;

13
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs

@ -2,22 +2,27 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Auditing namespace Volo.Abp.Auditing
{ {
public class EntityChangeInfo : IMultiTenant, IHasExtraProperties [Serializable]
public class EntityChangeInfo : IHasExtraProperties
{ {
public DateTime ChangeTime { get; set; } public DateTime ChangeTime { get; set; }
public EntityChangeType ChangeType { get; set; } public EntityChangeType ChangeType { get; set; }
/// <summary>
/// TenantId of the related entity.
/// This is not the TenantId of the audit log entry.
/// There can be multiple tenant data changes in a single audit log entry.
/// </summary>
public Guid? EntityTenantId { get; set; }
public string EntityId { get; set; } public string EntityId { get; set; }
public string EntityTypeFullName { get; set; } public string EntityTypeFullName { get; set; }
public Guid? TenantId { get; set; }
public List<EntityPropertyChangeInfo> PropertyChanges { get; set; } public List<EntityPropertyChangeInfo> PropertyChanges { get; set; }
public Dictionary<string, object> ExtraProperties { get; } public Dictionary<string, object> ExtraProperties { get; }

6
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs

@ -1,9 +1,9 @@
using System; using System;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Auditing namespace Volo.Abp.Auditing
{ {
public class EntityPropertyChangeInfo : IMultiTenant [Serializable]
public class EntityPropertyChangeInfo
{ {
/// <summary> /// <summary>
/// Maximum length of <see cref="PropertyName"/> property. /// Maximum length of <see cref="PropertyName"/> property.
@ -23,8 +23,6 @@ namespace Volo.Abp.Auditing
/// </summary> /// </summary>
public const int MaxPropertyTypeFullNameLength = 192; public const int MaxPropertyTypeFullNameLength = 192;
public Guid? TenantId { get; set; }
public virtual string NewValue { get; set; } public virtual string NewValue { get; set; }
public virtual string OriginalValue { get; set; } public virtual string OriginalValue { get; set; }

14
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs

@ -11,8 +11,18 @@ namespace Volo.Abp.Auditing
AuditLogInfo CreateAuditLogInfo(); AuditLogInfo CreateAuditLogInfo();
AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, object[] arguments); AuditLogActionInfo CreateAuditLogAction(
AuditLogInfo auditLog,
Type type,
MethodInfo method,
object[] arguments
);
AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, IDictionary<string, object> arguments); AuditLogActionInfo CreateAuditLogAction(
AuditLogInfo auditLog,
Type type,
MethodInfo method,
IDictionary<string, object> arguments
);
} }
} }

2
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptor.cs

@ -35,7 +35,7 @@ namespace Volo.Abp.Authorization
return; return;
} }
AsyncHelper.RunSync(() => AuthorizeAsync(invocation)); await AuthorizeAsync(invocation);
await invocation.ProceedAsync(); await invocation.ProceedAsync();
} }

23
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs

@ -4,8 +4,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Security.Claims; using Volo.Abp.Security.Claims;
namespace Volo.Abp.Authorization.Permissions namespace Volo.Abp.Authorization.Permissions
@ -18,6 +20,8 @@ namespace Volo.Abp.Authorization.Permissions
protected ICurrentPrincipalAccessor PrincipalAccessor { get; } protected ICurrentPrincipalAccessor PrincipalAccessor { get; }
protected ICurrentTenant CurrentTenant { get; }
protected PermissionOptions Options { get; } protected PermissionOptions Options { get; }
private readonly Lazy<List<IPermissionValueProvider>> _lazyProviders; private readonly Lazy<List<IPermissionValueProvider>> _lazyProviders;
@ -26,10 +30,12 @@ namespace Volo.Abp.Authorization.Permissions
IOptions<PermissionOptions> options, IOptions<PermissionOptions> options,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
ICurrentPrincipalAccessor principalAccessor, ICurrentPrincipalAccessor principalAccessor,
IPermissionDefinitionManager permissionDefinitionManager) IPermissionDefinitionManager permissionDefinitionManager,
ICurrentTenant currentTenant)
{ {
PrincipalAccessor = principalAccessor; PrincipalAccessor = principalAccessor;
PermissionDefinitionManager = permissionDefinitionManager; PermissionDefinitionManager = permissionDefinitionManager;
CurrentTenant = currentTenant;
Options = options.Value; Options = options.Value;
_lazyProviders = new Lazy<List<IPermissionValueProvider>>( _lazyProviders = new Lazy<List<IPermissionValueProvider>>(
@ -50,13 +56,18 @@ namespace Volo.Abp.Authorization.Permissions
{ {
Check.NotNull(name, nameof(name)); Check.NotNull(name, nameof(name));
var context = new PermissionValueCheckContext( var permission = PermissionDefinitionManager.Get(name);
PermissionDefinitionManager.Get(name),
claimsPrincipal
);
var isGranted = false; var multiTenancySide = claimsPrincipal?.GetMultiTenancySide()
?? CurrentTenant.GetMultiTenancySide();
if (!permission.MultiTenancySide.HasFlag(multiTenancySide))
{
return false;
}
var isGranted = false;
var context = new PermissionValueCheckContext(permission, claimsPrincipal);
foreach (var provider in ValueProviders) foreach (var provider in ValueProviders)
{ {
if (context.Permission.Providers.Any() && if (context.Permission.Providers.Any() &&

1
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RolePermissionValueProvider.cs

@ -19,6 +19,7 @@ namespace Volo.Abp.Authorization.Permissions
public override async Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context) public override async Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context)
{ {
var roles = context.Principal?.FindAll(AbpClaimTypes.Role).Select(c => c.Value).ToArray(); var roles = context.Principal?.FindAll(AbpClaimTypes.Role).Select(c => c.Value).ToArray();
if (roles == null || !roles.Any()) if (roles == null || !roles.Any())
{ {
return PermissionGrantResult.Undefined; return PermissionGrantResult.Undefined;

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

@ -18,6 +18,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Abp.Json\Volo.Abp.Json.csproj" />
<ProjectReference Include="..\Volo.Abp.MultiTenancy\Volo.Abp.MultiTenancy.csproj" /> <ProjectReference Include="..\Volo.Abp.MultiTenancy\Volo.Abp.MultiTenancy.csproj" />
<ProjectReference Include="..\Volo.Abp.Serialization\Volo.Abp.Serialization.csproj" /> <ProjectReference Include="..\Volo.Abp.Serialization\Volo.Abp.Serialization.csproj" />
<ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" /> <ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" />

9
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs

@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using Volo.Abp.Json;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Serialization; using Volo.Abp.Serialization;
@ -7,9 +8,11 @@ using Volo.Abp.Threading;
namespace Volo.Abp.Caching namespace Volo.Abp.Caching
{ {
[DependsOn(typeof(AbpThreadingModule))] [DependsOn(
[DependsOn(typeof(AbpSerializationModule))] typeof(AbpThreadingModule),
[DependsOn(typeof(AbpMultiTenancyModule))] typeof(AbpSerializationModule),
typeof(AbpMultiTenancyModule),
typeof(AbpJsonModule))]
public class AbpCachingModule : AbpModule public class AbpCachingModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)

56
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs

@ -25,7 +25,8 @@ namespace Volo.Abp.Caching
protected ICancellationTokenProvider CancellationTokenProvider { get; } protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected IObjectSerializer ObjectSerializer { get; } //TODO: Create IDistributedCacheSerializer
protected IDistributedCacheSerializer Serializer { get; }
protected ICurrentTenant CurrentTenant { get; } protected ICurrentTenant CurrentTenant { get; }
@ -42,8 +43,7 @@ namespace Volo.Abp.Caching
IOptions<DistributedCacheOptions> distributedCacheOption, IOptions<DistributedCacheOptions> distributedCacheOption,
IDistributedCache cache, IDistributedCache cache,
ICancellationTokenProvider cancellationTokenProvider, ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheSerializer serializer,
IObjectSerializer objectSerializer,
ICurrentTenant currentTenant) ICurrentTenant currentTenant)
{ {
_distributedCacheOption = distributedCacheOption.Value; _distributedCacheOption = distributedCacheOption.Value;
@ -51,13 +51,15 @@ namespace Volo.Abp.Caching
Cache = cache; Cache = cache;
CancellationTokenProvider = cancellationTokenProvider; CancellationTokenProvider = cancellationTokenProvider;
Logger = NullLogger<DistributedCache<TCacheItem>>.Instance; Logger = NullLogger<DistributedCache<TCacheItem>>.Instance;
ObjectSerializer = objectSerializer; Serializer = serializer;
CurrentTenant = currentTenant; CurrentTenant = currentTenant;
SetDefaultOptions(); SetDefaultOptions();
} }
public virtual TCacheItem Get(string key, bool? hideErrors = null) public virtual TCacheItem Get(
string key,
bool? hideErrors = null)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -83,10 +85,13 @@ namespace Volo.Abp.Caching
return null; return null;
} }
return ObjectSerializer.Deserialize<TCacheItem>(cachedBytes); return Serializer.Deserialize<TCacheItem>(cachedBytes);
} }
public virtual async Task<TCacheItem> GetAsync(string key, bool? hideErrors = null, CancellationToken token = default) public virtual async Task<TCacheItem> GetAsync(
string key,
bool? hideErrors = null,
CancellationToken token = default)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -115,7 +120,7 @@ namespace Volo.Abp.Caching
return null; return null;
} }
return ObjectSerializer.Deserialize<TCacheItem>(cachedBytes); return Serializer.Deserialize<TCacheItem>(cachedBytes);
} }
public TCacheItem GetOrAdd( public TCacheItem GetOrAdd(
@ -174,7 +179,11 @@ namespace Volo.Abp.Caching
return value; return value;
} }
public virtual void Set(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null) public virtual void Set(
string key,
TCacheItem value,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -182,7 +191,7 @@ namespace Volo.Abp.Caching
{ {
Cache.Set( Cache.Set(
NormalizeKey(key), NormalizeKey(key),
ObjectSerializer.Serialize(value), Serializer.Serialize(value),
options ?? DefaultCacheOptions options ?? DefaultCacheOptions
); );
} }
@ -198,7 +207,12 @@ namespace Volo.Abp.Caching
} }
} }
public virtual async Task SetAsync(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null, CancellationToken token = default) public virtual async Task SetAsync(
string key,
TCacheItem value,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
CancellationToken token = default)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -206,7 +220,7 @@ namespace Volo.Abp.Caching
{ {
await Cache.SetAsync( await Cache.SetAsync(
NormalizeKey(key), NormalizeKey(key),
ObjectSerializer.Serialize(value), Serializer.Serialize(value),
options ?? DefaultCacheOptions, options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token) CancellationTokenProvider.FallbackToProvider(token)
); );
@ -223,7 +237,9 @@ namespace Volo.Abp.Caching
} }
} }
public virtual void Refresh(string key, bool? hideErrors = null) public virtual void Refresh(
string key,
bool? hideErrors = null)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -243,7 +259,10 @@ namespace Volo.Abp.Caching
} }
} }
public virtual async Task RefreshAsync(string key, bool? hideErrors = null, CancellationToken token = default) public virtual async Task RefreshAsync(
string key,
bool? hideErrors = null,
CancellationToken token = default)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -263,7 +282,9 @@ namespace Volo.Abp.Caching
} }
} }
public virtual void Remove(string key, bool? hideErrors = null) public virtual void Remove(
string key,
bool? hideErrors = null)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -282,7 +303,10 @@ namespace Volo.Abp.Caching
} }
} }
public virtual async Task RemoveAsync(string key, bool? hideErrors = null, CancellationToken token = default) public virtual async Task RemoveAsync(
string key,
bool? hideErrors = null,
CancellationToken token = default)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;

9
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCacheSerializer.cs

@ -0,0 +1,9 @@
namespace Volo.Abp.Caching
{
public interface IDistributedCacheSerializer
{
byte[] Serialize<T>(T obj);
T Deserialize<T>(byte[] bytes);
}
}

26
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Utf8JsonDistributedCacheSerializer.cs

@ -0,0 +1,26 @@
using System.Text;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace Volo.Abp.Caching
{
public class Utf8JsonDistributedCacheSerializer : IDistributedCacheSerializer, ITransientDependency
{
protected IJsonSerializer JsonSerializer { get; }
public Utf8JsonDistributedCacheSerializer(IJsonSerializer jsonSerializer)
{
JsonSerializer = jsonSerializer;
}
public byte[] Serialize<T>(T obj)
{
return Encoding.UTF8.GetBytes(JsonSerializer.Serialize(obj));
}
public T Deserialize<T>(byte[] bytes)
{
return (T)JsonSerializer.Deserialize(typeof(T), Encoding.UTF8.GetString(bytes));
}
}
}

2
framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj

@ -14,7 +14,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Castle.Core" Version="4.3.1" /> <PackageReference Include="Castle.Core" Version="4.4.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

62
framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpInterceptorAdapter.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks; using System.Reflection;
using System.Threading.Tasks;
using Castle.DynamicProxy; using Castle.DynamicProxy;
using Volo.Abp.DynamicProxy; using Volo.Abp.DynamicProxy;
using Volo.Abp.Threading; using Volo.Abp.Threading;
@ -8,6 +9,20 @@ namespace Volo.Abp.Castle.DynamicProxy
public class CastleAbpInterceptorAdapter<TInterceptor> : IInterceptor public class CastleAbpInterceptorAdapter<TInterceptor> : IInterceptor
where TInterceptor : IAbpInterceptor where TInterceptor : IAbpInterceptor
{ {
private static readonly MethodInfo MethodExecuteWithoutReturnValueAsync =
typeof(CastleAbpInterceptorAdapter<TInterceptor>)
.GetMethod(
nameof(ExecuteWithoutReturnValueAsync),
BindingFlags.NonPublic | BindingFlags.Instance
);
private static readonly MethodInfo MethodExecuteWithReturnValueAsync =
typeof(CastleAbpInterceptorAdapter<TInterceptor>)
.GetMethod(
nameof(ExecuteWithReturnValueAsync),
BindingFlags.NonPublic | BindingFlags.Instance
);
private readonly TInterceptor _abpInterceptor; private readonly TInterceptor _abpInterceptor;
public CastleAbpInterceptorAdapter(TInterceptor abpInterceptor) public CastleAbpInterceptorAdapter(TInterceptor abpInterceptor)
@ -17,39 +32,58 @@ namespace Volo.Abp.Castle.DynamicProxy
public void Intercept(IInvocation invocation) public void Intercept(IInvocation invocation)
{ {
var proceedInfo = invocation.CaptureProceedInfo();
var method = invocation.MethodInvocationTarget ?? invocation.Method; var method = invocation.MethodInvocationTarget ?? invocation.Method;
if (method.IsAsync()) if (method.IsAsync())
{ {
InterceptAsyncMethod(invocation); InterceptAsyncMethod(invocation, proceedInfo);
} }
else else
{ {
InterceptSyncMethod(invocation); InterceptSyncMethod(invocation, proceedInfo);
} }
} }
private void InterceptAsyncMethod(IInvocation invocation) private void InterceptSyncMethod(IInvocation invocation, IInvocationProceedInfo proceedInfo)
{
_abpInterceptor.Intercept(new CastleAbpMethodInvocationAdapter(invocation, proceedInfo));
}
private void InterceptAsyncMethod(IInvocation invocation, IInvocationProceedInfo proceedInfo)
{ {
if (invocation.Method.ReturnType == typeof(Task)) if (invocation.Method.ReturnType == typeof(Task))
{ {
invocation.ReturnValue = _abpInterceptor.InterceptAsync(new CastleAbpMethodInvocationAdapter(invocation)); invocation.ReturnValue = MethodExecuteWithoutReturnValueAsync
.Invoke(this, new object[] { invocation, proceedInfo });
} }
else else
{ {
var interceptResult = _abpInterceptor.InterceptAsync(new CastleAbpMethodInvocationAdapter(invocation)); invocation.ReturnValue = MethodExecuteWithReturnValueAsync
var actualReturnValue = invocation.ReturnValue; .MakeGenericMethod(invocation.Method.ReturnType.GenericTypeArguments[0])
invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPreActionAndPostActionAndFinallyAndGetResult( .Invoke(this, new object[] {invocation, proceedInfo});
invocation.Method.ReturnType.GenericTypeArguments[0],
() => actualReturnValue,
() => interceptResult
);
} }
} }
private void InterceptSyncMethod(IInvocation invocation) private async Task ExecuteWithoutReturnValueAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo)
{ {
_abpInterceptor.Intercept(new CastleAbpMethodInvocationAdapter(invocation)); await Task.Yield();
await _abpInterceptor.InterceptAsync(
new CastleAbpMethodInvocationAdapter(invocation, proceedInfo)
);
}
private async Task<T> ExecuteWithReturnValueAsync<T>(IInvocation invocation, IInvocationProceedInfo proceedInfo)
{
await Task.Yield();
await _abpInterceptor.InterceptAsync(
new CastleAbpMethodInvocationAdapter(invocation, proceedInfo)
);
return await (Task<T>)invocation.ReturnValue;
} }
} }
} }

10
framework/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs

@ -30,17 +30,19 @@ namespace Volo.Abp.Castle.DynamicProxy
private object _actualReturnValue; private object _actualReturnValue;
protected IInvocation Invocation { get; } protected IInvocation Invocation { get; }
protected IInvocationProceedInfo ProceedInfo { get; }
public CastleAbpMethodInvocationAdapter(IInvocation invocation) public CastleAbpMethodInvocationAdapter(IInvocation invocation, IInvocationProceedInfo proceedInfo)
{ {
Invocation = invocation; Invocation = invocation;
ProceedInfo = proceedInfo;
_lazyArgumentsDictionary = new Lazy<IReadOnlyDictionary<string, object>>(GetArgumentsDictionary); _lazyArgumentsDictionary = new Lazy<IReadOnlyDictionary<string, object>>(GetArgumentsDictionary);
} }
public void Proceed() public void Proceed()
{ {
Invocation.Proceed(); ProceedInfo.Invoke();
if (Invocation.Method.IsAsync()) if (Invocation.Method.IsAsync())
{ {
@ -50,8 +52,10 @@ namespace Volo.Abp.Castle.DynamicProxy
public Task ProceedAsync() public Task ProceedAsync()
{ {
Invocation.Proceed(); ProceedInfo.Invoke();
_actualReturnValue = Invocation.ReturnValue; _actualReturnValue = Invocation.ReturnValue;
return Invocation.Method.IsAsync() return Invocation.Method.IsAsync()
? (Task)_actualReturnValue ? (Task)_actualReturnValue
: Task.FromResult(_actualReturnValue); : Task.FromResult(_actualReturnValue);

4
framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj

@ -24,7 +24,7 @@
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="JetBrains.Annotations" Version="2018.3.0" /> <PackageReference Include="JetBrains.Annotations" Version="2018.3.0" />
<PackageReference Include="Nito.AsyncEx.Coordination" Version="1.0.2" /> <PackageReference Include="Nito.AsyncEx.Coordination" Version="5.0.0" />
<PackageReference Include="Nito.AsyncEx.Context" Version="1.1.0" /> <PackageReference Include="Nito.AsyncEx.Context" Version="5.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

97
framework/src/Volo.Abp.Core/Volo/Abp/Check.cs

@ -1,7 +1,7 @@
using System; using JetBrains.Annotations;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using JetBrains.Annotations;
namespace Volo.Abp namespace Volo.Abp
{ {
@ -9,7 +9,9 @@ namespace Volo.Abp
public static class Check public static class Check
{ {
[ContractAnnotation("value:null => halt")] [ContractAnnotation("value:null => halt")]
public static T NotNull<T>(T value, [InvokerParameterName] [NotNull] string parameterName) public static T NotNull<T>(
T value,
[InvokerParameterName] [NotNull] string parameterName)
{ {
if (value == null) if (value == null)
{ {
@ -20,7 +22,10 @@ namespace Volo.Abp
} }
[ContractAnnotation("value:null => halt")] [ContractAnnotation("value:null => halt")]
public static T NotNull<T>(T value, [InvokerParameterName] [NotNull] string parameterName, string message) public static T NotNull<T>(
T value,
[InvokerParameterName] [NotNull] string parameterName,
string message)
{ {
if (value == null) if (value == null)
{ {
@ -31,24 +36,77 @@ namespace Volo.Abp
} }
[ContractAnnotation("value:null => halt")] [ContractAnnotation("value:null => halt")]
public static string NotNullOrWhiteSpace(string value, [InvokerParameterName] [NotNull] string parameterName) public static string NotNull(
string value,
[InvokerParameterName] [NotNull] string parameterName,
int maxLength = int.MaxValue,
int minLength = 0)
{
if (value == null)
{
throw new ArgumentException($"{parameterName} can not be null!", parameterName);
}
if (value.Length > maxLength)
{
throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName);
}
if (minLength > 0 && value.Length < minLength)
{
throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName);
}
return value;
}
[ContractAnnotation("value:null => halt")]
public static string NotNullOrWhiteSpace(
string value,
[InvokerParameterName] [NotNull] string parameterName,
int maxLength = int.MaxValue,
int minLength = 0)
{ {
if (value.IsNullOrWhiteSpace()) if (value.IsNullOrWhiteSpace())
{ {
throw new ArgumentException($"{parameterName} can not be null, empty or white space!", parameterName); throw new ArgumentException($"{parameterName} can not be null, empty or white space!", parameterName);
} }
if (value.Length > maxLength)
{
throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName);
}
if (minLength > 0 && value.Length < minLength)
{
throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName);
}
return value; return value;
} }
[ContractAnnotation("value:null => halt")] [ContractAnnotation("value:null => halt")]
public static string NotNullOrEmpty(string value, [InvokerParameterName] [NotNull] string parameterName) public static string NotNullOrEmpty(
string value,
[InvokerParameterName] [NotNull] string parameterName,
int maxLength = int.MaxValue,
int minLength = 0)
{ {
if (value.IsNullOrEmpty()) if (value.IsNullOrEmpty())
{ {
throw new ArgumentException($"{parameterName} can not be null or empty!", parameterName); throw new ArgumentException($"{parameterName} can not be null or empty!", parameterName);
} }
if (value.Length > maxLength)
{
throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName);
}
if (minLength > 0 && value.Length < minLength)
{
throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName);
}
return value; return value;
} }
@ -62,5 +120,32 @@ namespace Volo.Abp
return value; return value;
} }
public static string Length(
string value,
[InvokerParameterName] [NotNull] string parameterName,
int maxLength,
int minLength = 0)
{
if (minLength > 0)
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException(parameterName + " can not be null or empty!", parameterName);
}
if (value.Length < minLength)
{
throw new ArgumentException($"{parameterName} length must be equal to or bigger than {minLength}!", parameterName);
}
}
if (value != null && value.Length > maxLength)
{
throw new ArgumentException($"{parameterName} length must be equal to or lower than {maxLength}!", parameterName);
}
return value;
}
} }
} }

12
framework/src/Volo.Abp.Core/Volo/Abp/RandomHelper.cs

@ -72,6 +72,18 @@ namespace Volo.Abp
return objs[GetRandom(0, objs.Length)]; return objs[GetRandom(0, objs.Length)];
} }
/// <summary>
/// Gets random item from the given list.
/// </summary>
/// <typeparam name="T">Type of the objects</typeparam>
/// <param name="list">List of object to select a random one</param>
public static T GetRandomOfList<T>([NotNull] IList<T> list)
{
Check.NotNullOrEmpty(list, nameof(list));
return list[GetRandom(0, list.Count)];
}
/// <summary> /// <summary>
/// Generates a randomized list from given enumerable. /// Generates a randomized list from given enumerable.
/// </summary> /// </summary>

2
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AsyncCrudAppService.cs

@ -34,10 +34,8 @@ namespace Volo.Abp.Application.Services
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput> public abstract class AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput>
: AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TCreateInput> : AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TCreateInput>
where TGetAllInput : IPagedAndSortedResultRequest
where TEntity : class, IEntity<TKey> where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey> where TEntityDto : IEntityDto<TKey>
where TCreateInput : IEntityDto<TKey>
{ {
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository) protected AsyncCrudAppService(IRepository<TEntity, TKey> repository)
: base(repository) : base(repository)

11
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs

@ -3,6 +3,7 @@ using System.Net.Mail;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs; using Volo.Abp.BackgroundJobs;
using Volo.Abp.Threading;
namespace Volo.Abp.Emailing namespace Volo.Abp.Emailing
{ {
@ -60,7 +61,7 @@ namespace Volo.Abp.Emailing
{ {
if (normalize) if (normalize)
{ {
NormalizeMail(mail); await NormalizeMailAsync(mail);
} }
await SendEmailAsync(mail); await SendEmailAsync(mail);
@ -89,7 +90,7 @@ namespace Volo.Abp.Emailing
{ {
if (normalize) if (normalize)
{ {
NormalizeMail(mail); AsyncHelper.RunSync(() => NormalizeMailAsync(mail));
} }
SendEmail(mail); SendEmail(mail);
@ -113,13 +114,13 @@ namespace Volo.Abp.Emailing
/// Sets encodings to UTF8 if they are not set before. /// Sets encodings to UTF8 if they are not set before.
/// </summary> /// </summary>
/// <param name="mail">Mail to be normalized</param> /// <param name="mail">Mail to be normalized</param>
protected virtual void NormalizeMail(MailMessage mail) protected virtual async Task NormalizeMailAsync(MailMessage mail)
{ {
if (mail.From == null || mail.From.Address.IsNullOrEmpty()) if (mail.From == null || mail.From.Address.IsNullOrEmpty())
{ {
mail.From = new MailAddress( mail.From = new MailAddress(
Configuration.DefaultFromAddress, await Configuration.GetDefaultFromAddressAsync(),
Configuration.DefaultFromDisplayName, await Configuration.GetDefaultFromDisplayNameAsync(),
Encoding.UTF8 Encoding.UTF8
); );
} }

25
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderConfiguration.cs

@ -1,19 +1,16 @@
using System; using System;
using System.Threading.Tasks;
using Volo.Abp.Settings; using Volo.Abp.Settings;
namespace Volo.Abp.Emailing namespace Volo.Abp.Emailing
{ {
/// <summary> /// <summary>
/// Implementation of <see cref="IEmailSenderConfiguration"/> that reads settings /// Base implementation of <see cref="IEmailSenderConfiguration"/> that reads settings
/// from <see cref="ISettingManager"/>. /// from <see cref="ISettingProvider"/>.
/// </summary> /// </summary>
public abstract class EmailSenderConfiguration : IEmailSenderConfiguration public abstract class EmailSenderConfiguration : IEmailSenderConfiguration
{ {
public virtual string DefaultFromAddress => GetNotEmptySettingValue(EmailSettingNames.DefaultFromAddress); protected ISettingProvider SettingProvider { get; }
public virtual string DefaultFromDisplayName => SettingProvider.GetOrNull(EmailSettingNames.DefaultFromDisplayName);
protected readonly ISettingProvider SettingProvider;
/// <summary> /// <summary>
/// Creates a new <see cref="EmailSenderConfiguration"/>. /// Creates a new <see cref="EmailSenderConfiguration"/>.
@ -23,14 +20,24 @@ namespace Volo.Abp.Emailing
SettingProvider = settingProvider; SettingProvider = settingProvider;
} }
public Task<string> GetDefaultFromAddressAsync()
{
return GetNotEmptySettingValueAsync(EmailSettingNames.DefaultFromAddress);
}
public Task<string> GetDefaultFromDisplayNameAsync()
{
return GetNotEmptySettingValueAsync(EmailSettingNames.DefaultFromDisplayName);
}
/// <summary> /// <summary>
/// Gets a setting value by checking. Throws <see cref="AbpException"/> if it's null or empty. /// Gets a setting value by checking. Throws <see cref="AbpException"/> if it's null or empty.
/// </summary> /// </summary>
/// <param name="name">Name of the setting</param> /// <param name="name">Name of the setting</param>
/// <returns>Value of the setting</returns> /// <returns>Value of the setting</returns>
protected string GetNotEmptySettingValue(string name) protected async Task<string> GetNotEmptySettingValueAsync(string name)
{ {
var value = SettingProvider.GetOrNull(name); var value = await SettingProvider.GetOrNullAsync(name);
if (value.IsNullOrEmpty()) if (value.IsNullOrEmpty())
{ {

4
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs

@ -18,8 +18,8 @@ namespace Volo.Abp.Emailing
new SettingDefinition(EmailSettingNames.Smtp.Domain), new SettingDefinition(EmailSettingNames.Smtp.Domain),
new SettingDefinition(EmailSettingNames.Smtp.EnableSsl, "false"), new SettingDefinition(EmailSettingNames.Smtp.EnableSsl, "false"),
new SettingDefinition(EmailSettingNames.Smtp.UseDefaultCredentials, "true"), new SettingDefinition(EmailSettingNames.Smtp.UseDefaultCredentials, "true"),
new SettingDefinition(EmailSettingNames.DefaultFromAddress), new SettingDefinition(EmailSettingNames.DefaultFromAddress, "noreply@abp.io"),
new SettingDefinition(EmailSettingNames.DefaultFromDisplayName) new SettingDefinition(EmailSettingNames.DefaultFromDisplayName, "ABP application")
); );
} }
} }

14
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSenderConfiguration.cs

@ -1,18 +1,14 @@
namespace Volo.Abp.Emailing using System.Threading.Tasks;
namespace Volo.Abp.Emailing
{ {
/// <summary> /// <summary>
/// Defines configurations used while sending emails. /// Defines configurations used while sending emails.
/// </summary> /// </summary>
public interface IEmailSenderConfiguration public interface IEmailSenderConfiguration
{ {
/// <summary> Task<string> GetDefaultFromAddressAsync();
/// Default from address.
/// </summary>
string DefaultFromAddress { get; }
/// <summary> Task<string> GetDefaultFromDisplayNameAsync();
/// Default display name.
/// </summary>
string DefaultFromDisplayName { get; }
} }
} }

3
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSender.cs

@ -1,4 +1,5 @@
using System.Net.Mail; using System.Net.Mail;
using System.Threading.Tasks;
namespace Volo.Abp.Emailing.Smtp namespace Volo.Abp.Emailing.Smtp
{ {
@ -13,6 +14,6 @@ namespace Volo.Abp.Emailing.Smtp
/// <returns> /// <returns>
/// An <see cref="SmtpClient"/> object that is ready to send emails. /// An <see cref="SmtpClient"/> object that is ready to send emails.
/// </returns> /// </returns>
SmtpClient BuildClient(); Task<SmtpClient> BuildClientAsync();
} }
} }

18
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSenderConfiguration.cs

@ -1,4 +1,6 @@
namespace Volo.Abp.Emailing.Smtp using System.Threading.Tasks;
namespace Volo.Abp.Emailing.Smtp
{ {
/// <summary> /// <summary>
/// Defines configurations to used by SmtpClient object. /// Defines configurations to used by SmtpClient object.
@ -8,36 +10,36 @@
/// <summary> /// <summary>
/// SMTP Host name/IP. /// SMTP Host name/IP.
/// </summary> /// </summary>
string Host { get; } Task<string> GetHostAsync();
/// <summary> /// <summary>
/// SMTP Port. /// SMTP Port.
/// </summary> /// </summary>
int Port { get; } Task<int> GetPortAsync();
/// <summary> /// <summary>
/// User name to login to SMTP server. /// User name to login to SMTP server.
/// </summary> /// </summary>
string UserName { get; } Task<string> GetUserNameAsync();
/// <summary> /// <summary>
/// Password to login to SMTP server. /// Password to login to SMTP server.
/// </summary> /// </summary>
string Password { get; } Task<string> GetPasswordAsync();
/// <summary> /// <summary>
/// Domain name to login to SMTP server. /// Domain name to login to SMTP server.
/// </summary> /// </summary>
string Domain { get; } Task<string> GetDomainAsync();
/// <summary> /// <summary>
/// Is SSL enabled? /// Is SSL enabled?
/// </summary> /// </summary>
bool EnableSsl { get; } Task<bool> GetEnableSslAsync();
/// <summary> /// <summary>
/// Use default credentials? /// Use default credentials?
/// </summary> /// </summary>
bool UseDefaultCredentials { get; } Task<bool> GetUseDefaultCredentialsAsync();
} }
} }

32
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs

@ -4,6 +4,7 @@ using System.Net.Mail;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs; using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace Volo.Abp.Emailing.Smtp namespace Volo.Abp.Emailing.Smtp
{ {
@ -12,31 +13,34 @@ namespace Volo.Abp.Emailing.Smtp
/// </summary> /// </summary>
public class SmtpEmailSender : EmailSenderBase, ISmtpEmailSender, ITransientDependency public class SmtpEmailSender : EmailSenderBase, ISmtpEmailSender, ITransientDependency
{ {
private readonly ISmtpEmailSenderConfiguration _configuration; protected ISmtpEmailSenderConfiguration SmtpConfiguration { get; }
/// <summary> /// <summary>
/// Creates a new <see cref="SmtpEmailSender"/>. /// Creates a new <see cref="SmtpEmailSender"/>.
/// </summary> /// </summary>
public SmtpEmailSender(ISmtpEmailSenderConfiguration configuration, IBackgroundJobManager backgroundJobManager) public SmtpEmailSender(
: base(configuration, backgroundJobManager) ISmtpEmailSenderConfiguration smtpConfiguration,
IBackgroundJobManager backgroundJobManager)
: base(smtpConfiguration, backgroundJobManager)
{ {
_configuration = configuration; SmtpConfiguration = smtpConfiguration;
} }
public SmtpClient BuildClient() public async Task<SmtpClient> BuildClientAsync()
{ {
var host = _configuration.Host; var host = await SmtpConfiguration.GetHostAsync();
var port = _configuration.Port; var port = await SmtpConfiguration.GetPortAsync();
var smtpClient = new SmtpClient(host, port); var smtpClient = new SmtpClient(host, port);
try try
{ {
if (_configuration.EnableSsl) if (await SmtpConfiguration.GetEnableSslAsync())
{ {
smtpClient.EnableSsl = true; smtpClient.EnableSsl = true;
} }
if (_configuration.UseDefaultCredentials) if (await SmtpConfiguration.GetUseDefaultCredentialsAsync())
{ {
smtpClient.UseDefaultCredentials = true; smtpClient.UseDefaultCredentials = true;
} }
@ -44,11 +48,11 @@ namespace Volo.Abp.Emailing.Smtp
{ {
smtpClient.UseDefaultCredentials = false; smtpClient.UseDefaultCredentials = false;
var userName = _configuration.UserName; var userName = await SmtpConfiguration.GetUserNameAsync();
if (!userName.IsNullOrEmpty()) if (!userName.IsNullOrEmpty())
{ {
var password = _configuration.Password; var password = await SmtpConfiguration.GetPasswordAsync();
var domain = _configuration.Domain; var domain = await SmtpConfiguration.GetDomainAsync();
smtpClient.Credentials = !domain.IsNullOrEmpty() smtpClient.Credentials = !domain.IsNullOrEmpty()
? new NetworkCredential(userName, password, domain) ? new NetworkCredential(userName, password, domain)
: new NetworkCredential(userName, password); : new NetworkCredential(userName, password);
@ -66,7 +70,7 @@ namespace Volo.Abp.Emailing.Smtp
protected override async Task SendEmailAsync(MailMessage mail) protected override async Task SendEmailAsync(MailMessage mail)
{ {
using (var smtpClient = BuildClient()) using (var smtpClient = await BuildClientAsync())
{ {
await smtpClient.SendMailAsync(mail); await smtpClient.SendMailAsync(mail);
} }
@ -74,7 +78,7 @@ namespace Volo.Abp.Emailing.Smtp
protected override void SendEmail(MailMessage mail) protected override void SendEmail(MailMessage mail)
{ {
using (var smtpClient = BuildClient()) using (var smtpClient = AsyncHelper.RunSync(BuildClientAsync))
{ {
smtpClient.Send(mail); smtpClient.Send(mail);
} }

42
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSenderConfiguration.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Settings; using Volo.Abp.Settings;
@ -6,28 +7,49 @@ namespace Volo.Abp.Emailing.Smtp
{ {
/// <summary> /// <summary>
/// Implementation of <see cref="ISmtpEmailSenderConfiguration"/> that reads settings /// Implementation of <see cref="ISmtpEmailSenderConfiguration"/> that reads settings
/// from <see cref="ISettingManager"/>. /// from <see cref="ISettingProvider"/>.
/// </summary> /// </summary>
public class SmtpEmailSenderConfiguration : EmailSenderConfiguration, ISmtpEmailSenderConfiguration, ITransientDependency public class SmtpEmailSenderConfiguration : EmailSenderConfiguration, ISmtpEmailSenderConfiguration, ITransientDependency
{ {
public virtual string Host => GetNotEmptySettingValue(EmailSettingNames.Smtp.Host); public SmtpEmailSenderConfiguration(ISettingProvider settingProvider)
: base(settingProvider)
{
public virtual int Port => GetNotEmptySettingValue(EmailSettingNames.Smtp.Port).To<int>(); }
public virtual string UserName => GetNotEmptySettingValue(EmailSettingNames.Smtp.UserName); public Task<string> GetHostAsync()
{
return GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.Host);
}
public virtual string Password => GetNotEmptySettingValue(EmailSettingNames.Smtp.Password); public async Task<int> GetPortAsync()
{
return (await GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.Port)).To<int>();
}
public virtual string Domain => SettingProvider.GetOrNull(EmailSettingNames.Smtp.Domain); public Task<string> GetUserNameAsync()
{
return GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.UserName);
}
public virtual bool EnableSsl => SettingProvider.GetOrNull(EmailSettingNames.Smtp.EnableSsl).To<bool>(); public Task<string> GetPasswordAsync()
{
return GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.Password);
}
public virtual bool UseDefaultCredentials => SettingProvider.GetOrNull(EmailSettingNames.Smtp.UseDefaultCredentials).To<bool>(); public Task<string> GetDomainAsync()
{
return SettingProvider.GetOrNullAsync(EmailSettingNames.Smtp.Domain);
}
public SmtpEmailSenderConfiguration(ISettingProvider settingProvider) public async Task<bool> GetEnableSslAsync()
: base(settingProvider)
{ {
return (await GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.EnableSsl)).To<bool>();
}
public async Task<bool> GetUseDefaultCredentialsAsync()
{
return (await GetNotEmptySettingValueAsync(EmailSettingNames.Smtp.UseDefaultCredentials)).To<bool>();
} }
} }
} }

5
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs

@ -103,7 +103,7 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory
EntityId = entityId, EntityId = entityId,
EntityTypeFullName = entityType.FullName, EntityTypeFullName = entityType.FullName,
PropertyChanges = GetPropertyChanges(entityEntry), PropertyChanges = GetPropertyChanges(entityEntry),
TenantId = GetTenantId(entity) EntityTenantId = GetTenantId(entity)
}; };
return entityChange; return entityChange;
@ -171,8 +171,7 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory
NewValue = isDeleted ? null : JsonSerializer.Serialize(propertyEntry.CurrentValue).TruncateWithPostfix(EntityPropertyChangeInfo.MaxValueLength), NewValue = isDeleted ? null : JsonSerializer.Serialize(propertyEntry.CurrentValue).TruncateWithPostfix(EntityPropertyChangeInfo.MaxValueLength),
OriginalValue = isCreated ? null : JsonSerializer.Serialize(propertyEntry.OriginalValue).TruncateWithPostfix(EntityPropertyChangeInfo.MaxValueLength), OriginalValue = isCreated ? null : JsonSerializer.Serialize(propertyEntry.OriginalValue).TruncateWithPostfix(EntityPropertyChangeInfo.MaxValueLength),
PropertyName = property.Name, PropertyName = property.Name,
PropertyTypeFullName = property.ClrType.GetFirstGenericArgumentIfNullable().FullName, PropertyTypeFullName = property.ClrType.GetFirstGenericArgumentIfNullable().FullName
TenantId = GetTenantId(entityEntry.Entity)
}); });
} }
} }

1
framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj

@ -20,6 +20,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Abp.Castle.Core\Volo.Abp.Castle.Core.csproj" /> <ProjectReference Include="..\Volo.Abp.Castle.Core\Volo.Abp.Castle.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Http\Volo.Abp.Http.csproj" /> <ProjectReference Include="..\Volo.Abp.Http\Volo.Abp.Http.csproj" />
<ProjectReference Include="..\Volo.Abp.MultiTenancy\Volo.Abp.MultiTenancy.csproj" />
<ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" /> <ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" />
</ItemGroup> </ItemGroup>

4
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs

@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Castle; using Volo.Abp.Castle;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace Volo.Abp.Http.Client namespace Volo.Abp.Http.Client
@ -8,7 +9,8 @@ namespace Volo.Abp.Http.Client
[DependsOn( [DependsOn(
typeof(AbpHttpModule), typeof(AbpHttpModule),
typeof(AbpCastleCoreModule), typeof(AbpCastleCoreModule),
typeof(AbpThreadingModule) typeof(AbpThreadingModule),
typeof(AbpMultiTenancyModule)
)] )]
public class AbpHttpClientModule : AbpModule public class AbpHttpClientModule : AbpModule
{ {

13
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs

@ -16,6 +16,7 @@ using Volo.Abp.Http.Client.Authentication;
using Volo.Abp.Http.Modeling; using Volo.Abp.Http.Modeling;
using Volo.Abp.Http.ProxyScripting.Generators; using Volo.Abp.Http.ProxyScripting.Generators;
using Volo.Abp.Json; using Volo.Abp.Json;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Reflection; using Volo.Abp.Reflection;
using Volo.Abp.Threading; using Volo.Abp.Threading;
using Volo.Abp.Tracing; using Volo.Abp.Tracing;
@ -29,6 +30,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying
protected ICancellationTokenProvider CancellationTokenProvider { get; } protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected ICorrelationIdProvider CorrelationIdProvider { get; } protected ICorrelationIdProvider CorrelationIdProvider { get; }
protected ICurrentTenant CurrentTenant { get; }
protected CorrelationIdOptions CorrelationIdOptions { get; } protected CorrelationIdOptions CorrelationIdOptions { get; }
protected IDynamicProxyHttpClientFactory HttpClientFactory { get; } protected IDynamicProxyHttpClientFactory HttpClientFactory { get; }
protected IApiDescriptionFinder ApiDescriptionFinder { get; } protected IApiDescriptionFinder ApiDescriptionFinder { get; }
@ -55,10 +57,12 @@ namespace Volo.Abp.Http.Client.DynamicProxying
IRemoteServiceHttpClientAuthenticator clientAuthenticator, IRemoteServiceHttpClientAuthenticator clientAuthenticator,
ICancellationTokenProvider cancellationTokenProvider, ICancellationTokenProvider cancellationTokenProvider,
ICorrelationIdProvider correlationIdProvider, ICorrelationIdProvider correlationIdProvider,
IOptions<CorrelationIdOptions> correlationIdOptions) IOptions<CorrelationIdOptions> correlationIdOptions,
ICurrentTenant currentTenant)
{ {
CancellationTokenProvider = cancellationTokenProvider; CancellationTokenProvider = cancellationTokenProvider;
CorrelationIdProvider = correlationIdProvider; CorrelationIdProvider = correlationIdProvider;
CurrentTenant = currentTenant;
CorrelationIdOptions = correlationIdOptions.Value; CorrelationIdOptions = correlationIdOptions.Value;
HttpClientFactory = httpClientFactory; HttpClientFactory = httpClientFactory;
ApiDescriptionFinder = apiDescriptionFinder; ApiDescriptionFinder = apiDescriptionFinder;
@ -213,6 +217,13 @@ namespace Volo.Abp.Http.Client.DynamicProxying
//CorrelationId //CorrelationId
requestMessage.Headers.Add(CorrelationIdOptions.HttpHeaderName, CorrelationIdProvider.Get()); requestMessage.Headers.Add(CorrelationIdOptions.HttpHeaderName, CorrelationIdProvider.Get());
//TenantId
if (CurrentTenant.Id.HasValue)
{
//TODO: Use AspNetCoreMultiTenancyOptions to get the key
requestMessage.Headers.Add(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.Value.ToString());
}
//TODO: Is that the way we want? Couldn't send the culture (not ui culture) //TODO: Is that the way we want? Couldn't send the culture (not ui culture)
requestMessage.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(CultureInfo.CurrentUICulture.Name)); requestMessage.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(CultureInfo.CurrentUICulture.Name));
} }

4
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs

@ -10,7 +10,7 @@ namespace Volo.Abp.Http.Client
public string BaseUrl public string BaseUrl
{ {
get => this.GetOrDefault(nameof(BaseUrl)); get => this.GetOrDefault(nameof(BaseUrl));
set => this[BaseUrl] = value; set => this[nameof(BaseUrl)] = value;
} }
/// <summary> /// <summary>
@ -19,7 +19,7 @@ namespace Volo.Abp.Http.Client
public string Version public string Version
{ {
get => this.GetOrDefault(nameof(Version)); get => this.GetOrDefault(nameof(Version));
set => this[Version] = value; set => this[nameof(Version)] = value;
} }
public RemoteServiceConfiguration() public RemoteServiceConfiguration()

14
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs

@ -12,7 +12,7 @@ namespace Volo.Abp.IdentityModel
public string GrantType public string GrantType
{ {
get => this.GetOrDefault(nameof(GrantType)); get => this.GetOrDefault(nameof(GrantType));
set => this[GrantType] = value; set => this[nameof(GrantType)] = value;
} }
/// <summary> /// <summary>
@ -21,7 +21,7 @@ namespace Volo.Abp.IdentityModel
public string ClientId public string ClientId
{ {
get => this.GetOrDefault(nameof(ClientId)); get => this.GetOrDefault(nameof(ClientId));
set => this[ClientId] = value; set => this[nameof(ClientId)] = value;
} }
/// <summary> /// <summary>
@ -30,7 +30,7 @@ namespace Volo.Abp.IdentityModel
public string ClientSecret public string ClientSecret
{ {
get => this.GetOrDefault(nameof(ClientSecret)); get => this.GetOrDefault(nameof(ClientSecret));
set => this[ClientSecret] = value; set => this[nameof(ClientSecret)] = value;
} }
/// <summary> /// <summary>
@ -40,7 +40,7 @@ namespace Volo.Abp.IdentityModel
public string UserName public string UserName
{ {
get => this.GetOrDefault(nameof(UserName)); get => this.GetOrDefault(nameof(UserName));
set => this[UserName] = value; set => this[nameof(UserName)] = value;
} }
/// <summary> /// <summary>
@ -50,7 +50,7 @@ namespace Volo.Abp.IdentityModel
public string UserPassword public string UserPassword
{ {
get => this.GetOrDefault(nameof(UserPassword)); get => this.GetOrDefault(nameof(UserPassword));
set => this[UserPassword] = value; set => this[nameof(UserPassword)] = value;
} }
/// <summary> /// <summary>
@ -59,7 +59,7 @@ namespace Volo.Abp.IdentityModel
public string Authority public string Authority
{ {
get => this.GetOrDefault(nameof(Authority)); get => this.GetOrDefault(nameof(Authority));
set => this[Authority] = value; set => this[nameof(Authority)] = value;
} }
/// <summary> /// <summary>
@ -68,7 +68,7 @@ namespace Volo.Abp.IdentityModel
public string Scope public string Scope
{ {
get => this.GetOrDefault(nameof(Scope)); get => this.GetOrDefault(nameof(Scope));
set => this[Scope] = value; set => this[nameof(Scope)] = value;
} }
public IdentityClientConfiguration() public IdentityClientConfiguration()

25
framework/src/Volo.Abp.MultiTenancy/System/Security/Principal/AbpClaimsIdentityExtensions.cs

@ -0,0 +1,25 @@
using System.Security.Claims;
using JetBrains.Annotations;
using Volo.Abp.MultiTenancy;
namespace System.Security.Principal
{
public static class AbpMultiTenancyClaimsIdentityExtensions
{
public static MultiTenancySides GetMultiTenancySide([NotNull] this IIdentity identity)
{
var tenantId = identity.FindTenantId();
return tenantId.HasValue
? MultiTenancySides.Tenant
: MultiTenancySides.Host;
}
public static MultiTenancySides GetMultiTenancySide([NotNull] this ClaimsPrincipal principal)
{
var tenantId = principal.FindTenantId();
return tenantId.HasValue
? MultiTenancySides.Tenant
: MultiTenancySides.Host;
}
}
}

28
framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentClaimsPrincipalTenantResolveContributor.cs

@ -1,28 +0,0 @@
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.MultiTenancy
{
public class CurrentClaimsPrincipalTenantResolveContributor : TenantResolveContributorBase
{
public const string ContributorName = "CurrentClaims";
public override string Name => ContributorName;
public override void Resolve(ITenantResolveContext context)
{
var principal = context.ServiceProvider.GetRequiredService<ICurrentPrincipalAccessor>().Principal;
if (principal?.Identity?.IsAuthenticated != true)
{
return;
}
context.Handled = true;
context.TenantIdOrName = principal
.Claims
.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId)
?.Value;
}
}
}

7
framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantExtensions.cs

@ -16,5 +16,12 @@ namespace Volo.Abp.MultiTenancy
return currentTenant.Id.Value; return currentTenant.Id.Value;
} }
public static MultiTenancySides GetMultiTenancySide(this ICurrentTenant currentTenant)
{
return currentTenant.Id.HasValue
? MultiTenancySides.Tenant
: MultiTenancySides.Host;
}
} }
} }

24
framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentUserTenantResolveContributor.cs

@ -0,0 +1,24 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Users;
namespace Volo.Abp.MultiTenancy
{
public class CurrentUserTenantResolveContributor : TenantResolveContributorBase
{
public const string ContributorName = "CurrentUser";
public override string Name => ContributorName;
public override void Resolve(ITenantResolveContext context)
{
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
if (currentUser.IsAuthenticated != true)
{
return;
}
context.Handled = true;
context.TenantIdOrName = currentUser.TenantId?.ToString();
}
}
}

5
framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/ITenantResolveResultAccessor.cs → framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantResolveResultAccessor.cs

@ -1,9 +1,10 @@
using Volo.Abp.MultiTenancy; using JetBrains.Annotations;
namespace Volo.Abp.AspNetCore.MultiTenancy namespace Volo.Abp.MultiTenancy
{ {
public interface ITenantResolveResultAccessor public interface ITenantResolveResultAccessor
{ {
[CanBeNull]
TenantResolveResult Result { get; set; } TenantResolveResult Result { get; set; }
} }
} }

13
framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantResolveResultAccessor.cs

@ -0,0 +1,13 @@
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.MultiTenancy
{
public class NullTenantResolveResultAccessor : ITenantResolveResultAccessor, ISingletonDependency
{
public TenantResolveResult Result
{
get => null;
set { }
}
}
}

2
framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolveOptions.cs

@ -12,7 +12,7 @@ namespace Volo.Abp.MultiTenancy
{ {
TenantResolvers = new List<ITenantResolveContributor> TenantResolvers = new List<ITenantResolveContributor>
{ {
new CurrentClaimsPrincipalTenantResolveContributor() new CurrentUserTenantResolveContributor()
}; };
} }
} }

9
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs

@ -39,6 +39,15 @@ namespace Volo.Abp.UI.Navigation
return menuWithItems.Items.FirstOrDefault(mi => mi.Name == menuItemName); return menuWithItems.Items.FirstOrDefault(mi => mi.Name == menuItemName);
} }
public static bool TryRemoveMenuItem(
[NotNull] this IHasMenuItems menuWithItems,
string menuItemName)
{
Check.NotNull(menuWithItems, nameof(menuWithItems));
return menuWithItems.Items.RemoveAll(item => item.Name == menuItemName) > 0;
}
[NotNull] [NotNull]
public static IHasMenuItems SetSubItemOrder( public static IHasMenuItems SetSubItemOrder(
[NotNull] this IHasMenuItems menuWithItems, [NotNull] this IHasMenuItems menuWithItems,

4
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlOptions.cs

@ -1,6 +1,4 @@
using Volo.Abp.Ui.Navigation.Urls; namespace Volo.Abp.Ui.Navigation.Urls
namespace Volo.Abp.Ui.Navigation.Urls
{ {
public class AppUrlOptions public class AppUrlOptions
{ {

80
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs

@ -1,20 +1,42 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Ui.Navigation.Urls namespace Volo.Abp.Ui.Navigation.Urls
{ {
public class AppUrlProvider : IAppUrlProvider, ITransientDependency public class AppUrlProvider : IAppUrlProvider, ITransientDependency
{ {
public const string TenantIdPlaceHolder = "{{tenantId}}";
public const string TenantNamePlaceHolder = "{{tenantName}}";
protected AppUrlOptions Options { get; } protected AppUrlOptions Options { get; }
protected ICurrentTenant CurrentTenant { get; }
protected ITenantStore TenantStore { get; }
public AppUrlProvider(IOptions<AppUrlOptions> options) public AppUrlProvider(
IOptions<AppUrlOptions> options,
ICurrentTenant currentTenant,
ITenantStore tenantStore)
{ {
CurrentTenant = currentTenant;
TenantStore = tenantStore;
Options = options.Value; Options = options.Value;
} }
public virtual string GetUrl(string appName, string urlName = null) public virtual async Task<string> GetUrlAsync(string appName, string urlName = null)
{
return await ReplacePlaceHoldersAsync(
await GetConfiguredUrl(
appName,
urlName
)
);
}
protected virtual Task<string> GetConfiguredUrl(string appName, string urlName)
{ {
var app = Options.Applications[appName]; var app = Options.Applications[appName];
@ -22,24 +44,70 @@ namespace Volo.Abp.Ui.Navigation.Urls
{ {
if (app.RootUrl.IsNullOrEmpty()) if (app.RootUrl.IsNullOrEmpty())
{ {
throw new AbpException($"RootUrl for the application '{appName}' was not configured. Use {nameof(AppUrlOptions)} to configure it!"); throw new AbpException(
$"RootUrl for the application '{appName}' was not configured. Use {nameof(AppUrlOptions)} to configure it!"
);
} }
return app.RootUrl; return Task.FromResult(app.RootUrl);
} }
var url = app.Urls.GetOrDefault(urlName); var url = app.Urls.GetOrDefault(urlName);
if (url.IsNullOrEmpty()) if (url.IsNullOrEmpty())
{ {
throw new AbpException($"Url, named '{urlName}', for the application '{appName}' was not configured. Use {nameof(AppUrlOptions)} to configure it!"); throw new AbpException(
$"Url, named '{urlName}', for the application '{appName}' was not configured. Use {nameof(AppUrlOptions)} to configure it!"
);
} }
if (app.RootUrl == null) if (app.RootUrl == null)
{
return Task.FromResult(url);
}
return Task.FromResult(app.RootUrl.EnsureEndsWith('/') + url);
}
protected virtual async Task<string> ReplacePlaceHoldersAsync(string url)
{
url = url.Replace(
TenantIdPlaceHolder,
CurrentTenant.Id.HasValue ? CurrentTenant.Id.Value.ToString() : ""
);
if (!url.Contains(TenantNamePlaceHolder))
{ {
return url; return url;
} }
return app.RootUrl.EnsureEndsWith('/') + url; var tenantNamePlaceHolder = TenantNamePlaceHolder;
if (url.Contains(TenantNamePlaceHolder + '.'))
{
tenantNamePlaceHolder = TenantNamePlaceHolder + '.';
}
if (CurrentTenant.Id.HasValue)
{
url = url.Replace(tenantNamePlaceHolder, await GetCurrentTenantNameAsync());
}
else
{
url = url.Replace(tenantNamePlaceHolder, "");
}
return url;
}
private async Task<string> GetCurrentTenantNameAsync()
{
if (CurrentTenant.Id.HasValue && CurrentTenant.Name.IsNullOrEmpty())
{
var tenantConfiguration = await TenantStore.FindAsync(CurrentTenant.Id.Value);
return tenantConfiguration.Name;
}
return CurrentTenant.Name;
} }
} }
} }

5
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs

@ -1,9 +1,10 @@
using JetBrains.Annotations; using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.Ui.Navigation.Urls namespace Volo.Abp.Ui.Navigation.Urls
{ {
public interface IAppUrlProvider public interface IAppUrlProvider
{ {
string GetUrl([NotNull] string appName, [CanBeNull] string urlName = null); Task<string> GetUrlAsync([NotNull] string appName, [CanBeNull] string urlName = null);
} }
} }

4
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json

@ -24,6 +24,7 @@
"Actions": "Actions", "Actions": "Actions",
"Delete": "Delete", "Delete": "Delete",
"Edit": "Edit", "Edit": "Edit",
"Refresh": "Refresh",
"ProcessingWithThreeDot": "Processing...", "ProcessingWithThreeDot": "Processing...",
"LoadingWithThreeDot": "Loading...", "LoadingWithThreeDot": "Loading...",
"Welcome": "Welcome", "Welcome": "Welcome",
@ -44,6 +45,7 @@
"PagerShowMenuEntries": "Show _MENU_ entries", "PagerShowMenuEntries": "Show _MENU_ entries",
"DatatableActionDropdownDefaultText": "Actions", "DatatableActionDropdownDefaultText": "Actions",
"ChangePassword": "Change password", "ChangePassword": "Change password",
"PersonalInfo": "My profile" "PersonalInfo": "My profile",
"AreYouSureYouWantToCancelEditingWarningMessage": "You have unsaved changes."
} }
} }

16
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json

@ -24,17 +24,27 @@
"Actions": "Ações", "Actions": "Ações",
"Delete": "Excluir", "Delete": "Excluir",
"Edit": "Editar", "Edit": "Editar",
"Refresh": "Refrescar",
"ProcessingWithThreeDot": "Processando...", "ProcessingWithThreeDot": "Processando...",
"LoadingWithThreeDot": "Carregando...",
"Welcome": "Bem Vindo",
"Login": "Entrar", "Login": "Entrar",
"Register": "cadastrar", "Register": "Registrar-se",
"Logout": "Sair", "Logout": "Sair",
"Submit": "Enviar", "Submit": "Enviar",
"Back": "Voltar", "Back": "Voltar",
"PagerSearch": "Pesquisar",
"PagerNext": "Próximo", "PagerNext": "Próximo",
"PagerPrevious": "Anterior", "PagerPrevious": "Anterior",
"PagerInfo": "Mostrando _START_ até _END_ de _TOTAL_ registros.", "PagerFirst": "Primeira",
"PagerLast": "Última",
"PagerInfo": "Mostrando _START_ até _END_ de _TOTAL_ registros",
"PagerInfoEmpty": "Mostrando 0 até 0 de 0 registros",
"PagerInfoFiltered": "(filtrado de um total de _MAX_ registros)",
"NoDataAvailableInDatatable": "Nenhum dado disponível",
"PagerShowMenuEntries": "Mostrando _MENU_ registros",
"DatatableActionDropdownDefaultText": "Ações", "DatatableActionDropdownDefaultText": "Ações",
"ChangePassword": "Alterar senha", "ChangePassword": "Alterar Senha",
"PersonalInfo": "Perfil" "PersonalInfo": "Perfil"
} }
} }

6
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json

@ -24,6 +24,7 @@
"Actions": "İşlemler", "Actions": "İşlemler",
"Delete": "Sil", "Delete": "Sil",
"Edit": "Düzenle", "Edit": "Düzenle",
"Refresh": "Yenile",
"ProcessingWithThreeDot": "İşleniyor...", "ProcessingWithThreeDot": "İşleniyor...",
"LoadingWithThreeDot": "Yükleniyor...", "LoadingWithThreeDot": "Yükleniyor...",
"Welcome": "Hoşgeldiniz", "Welcome": "Hoşgeldiniz",
@ -40,10 +41,11 @@
"PagerInfo": "_TOTAL_ kayıttan _START_ ile _END_ arası gösteriliyor.", "PagerInfo": "_TOTAL_ kayıttan _START_ ile _END_ arası gösteriliyor.",
"PagerInfoEmpty": "0 kayıttan 0 ile 0 arası gösteriliyor.", "PagerInfoEmpty": "0 kayıttan 0 ile 0 arası gösteriliyor.",
"PagerInfoFiltered": "(_MAX_ kayıt arasından filtrelendi)", "PagerInfoFiltered": "(_MAX_ kayıt arasından filtrelendi)",
"NoDataAvailableInDatatable": "Tabloda kayır mevcut değil.", "NoDataAvailableInDatatable": "Tabloda kayıt mevcut değil.",
"PagerShowMenuEntries": "Sayfada _MENU_ kayıt göster.", "PagerShowMenuEntries": "Sayfada _MENU_ kayıt göster.",
"DatatableActionDropdownDefaultText": "İşlemler", "DatatableActionDropdownDefaultText": "İşlemler",
"ChangePassword": "Şifre değiştir", "ChangePassword": "Şifre değiştir",
"PersonalInfo": "Profilim" "PersonalInfo": "Profilim",
"AreYouSureYouWantToCancelEditingWarningMessage": "Kaydedilmemiş değişiklikler var."
} }
} }

1
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json

@ -24,6 +24,7 @@
"Actions": "操作", "Actions": "操作",
"Delete": "删除", "Delete": "删除",
"Edit": "修改", "Edit": "修改",
"Refresh": "刷新",
"ProcessingWithThreeDot": "处理中...", "ProcessingWithThreeDot": "处理中...",
"LoadingWithThreeDot": "加载中...", "LoadingWithThreeDot": "加载中...",
"Welcome": "欢迎", "Welcome": "欢迎",

16
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs

@ -1,4 +1,5 @@
using Volo.Abp.Aspects; using System.Threading.Tasks;
using Volo.Abp.Aspects;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy; using Volo.Abp.DynamicProxy;
@ -26,6 +27,19 @@ namespace Volo.Abp.Validation
invocation.Proceed(); invocation.Proceed();
} }
public override async Task InterceptAsync(IAbpMethodInvocation invocation)
{
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Validation))
{
await invocation.ProceedAsync();
return;
}
Validate(invocation);
await invocation.ProceedAsync();
}
protected virtual void Validate(IAbpMethodInvocation invocation) protected virtual void Validate(IAbpMethodInvocation invocation)
{ {
_validator.Validate( _validator.Validate(

3
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/pt-BR.json

@ -1,6 +1,7 @@
{ {
"culture": "pt-BR", "culture": "pt-BR",
"texts": { "texts": {
"BirthDate": "Aniversário" "BirthDate": "Aniversário",
"Value1": "Valor Um"
} }
} }

4
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/_Layout.cshtml

@ -20,7 +20,7 @@
<abp-style-bundle name="@StandardBundles.Styles.Global" /> <abp-style-bundle name="@StandardBundles.Styles.Global" />
<link href="~/css/demo.min.css" rel="stylesheet" /> <link href="~/css/demo.min.css" rel="stylesheet" />
@RenderSection("styles", false) @await RenderSectionAsync("styles", false)
</head> </head>
<body> <body>
@ -30,7 +30,7 @@
<abp-script-bundle name="@StandardBundles.Scripts.Global" /> <abp-script-bundle name="@StandardBundles.Scripts.Global" />
@RenderSection("scripts", false) @await RenderSectionAsync("scripts", false)
</body> </body>
</html> </html>

2
framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/CachedTestObject.cs

@ -1,3 +1,4 @@
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Volo.Abp.DynamicProxy namespace Volo.Abp.DynamicProxy
@ -6,6 +7,7 @@ namespace Volo.Abp.DynamicProxy
{ {
public virtual int GetValue(int v) public virtual int GetValue(int v)
{ {
Thread.Sleep(5);
return v; return v;
} }

2
framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/SimpleAsyncInterceptor.cs

@ -14,7 +14,7 @@ namespace Volo.Abp.DynamicProxy
public override async Task InterceptAsync(IAbpMethodInvocation invocation) public override async Task InterceptAsync(IAbpMethodInvocation invocation)
{ {
//await Task.Delay(5); CAN NOT USE await before method execution! This is a restriction of Castle DynamicProxy await Task.Delay(5);
(invocation.TargetObject as ICanLogOnObject)?.Logs?.Add($"{GetType().Name}_InterceptAsync_BeforeInvocation"); (invocation.TargetObject as ICanLogOnObject)?.Logs?.Add($"{GetType().Name}_InterceptAsync_BeforeInvocation");
await invocation.ProceedAsync(); await invocation.ProceedAsync();
(invocation.TargetObject as ICanLogOnObject)?.Logs?.Add($"{GetType().Name}_InterceptAsync_AfterInvocation"); (invocation.TargetObject as ICanLogOnObject)?.Logs?.Add($"{GetType().Name}_InterceptAsync_AfterInvocation");

3
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/en.json

@ -1,6 +1,7 @@
{ {
"culture": "en", "culture": "en",
"texts": { "texts": {
"USA": "United States of America" "USA": "United States of America",
"Brazil": "Brasil"
} }
} }

3
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/pt-BR.json

@ -5,6 +5,7 @@
"Car": "Carro", "Car": "Carro",
"CarPlural": "Carros", "CarPlural": "Carros",
"MaxLenghtErrorMessage": "O comprimento deste campo pode ser no máximo de '{0}' caracteres", "MaxLenghtErrorMessage": "O comprimento deste campo pode ser no máximo de '{0}' caracteres",
"Universe": "Universo" "Universe": "Universo",
"FortyTwo": "Quarenta e Dois"
} }
} }

22
modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs

@ -1,4 +1,7 @@
using Volo.Abp.IdentityServer; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Identity.AspNetCore;
using Volo.Abp.IdentityServer;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;
@ -10,12 +13,29 @@ namespace Volo.Abp.Account.Web
)] )]
public class AbpAccountWebIdentityServerModule : AbpModule public class AbpAccountWebIdentityServerModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.PreConfigure<AbpIdentityAspNetCoreOptions>(options =>
{
options.ConfigureAuthentication = false;
});
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
Configure<VirtualFileSystemOptions>(options => Configure<VirtualFileSystemOptions>(options =>
{ {
options.FileSets.AddEmbedded<AbpAccountWebIdentityServerModule>("Volo.Abp.Account.Web"); options.FileSets.AddEmbedded<AbpAccountWebIdentityServerModule>("Volo.Abp.Account.Web");
}); });
//TODO: Try to reuse from AbpIdentityAspNetCoreModule
context.Services
.AddAuthentication(o =>
{
o.DefaultScheme = IdentityConstants.ApplicationScheme;
o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies();
} }
} }
} }

2
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs

@ -35,11 +35,9 @@ namespace Volo.Abp.Account.Web.Pages.Account
schemeProvider, schemeProvider,
accountOptions) accountOptions)
{ {
_schemeProvider = schemeProvider;
Interaction = interaction; Interaction = interaction;
ClientStore = clientStore; ClientStore = clientStore;
IdentityServerEvents = identityServerEvents; IdentityServerEvents = identityServerEvents;
_accountOptions = accountOptions.Value;
} }
public override async Task OnGetAsync() public override async Task OnGetAsync()

2
modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs

@ -1,7 +1,6 @@
using Localization.Resources.AbpUi; using Localization.Resources.AbpUi;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Account.Web.Localization; using Volo.Abp.Account.Web.Localization;
using Volo.Abp.Account.Web.Settings;
using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars;
@ -9,7 +8,6 @@ using Volo.Abp.Identity.AspNetCore;
using Volo.Abp.Localization; using Volo.Abp.Localization;
using Volo.Abp.Localization.Resources.AbpValidation; using Volo.Abp.Localization.Resources.AbpValidation;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Settings;
using Volo.Abp.UI.Navigation; using Volo.Abp.UI.Navigation;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;

14
modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json

@ -11,12 +11,12 @@
"InvalidUserNameOrPassword": "Kullanıcı adı ya da şifre geçersiz!", "InvalidUserNameOrPassword": "Kullanıcı adı ya da şifre geçersiz!",
"LoginIsNotAllowed": "Giriş yapamazsınız! E-posta adresinizi ya da telefon numaranızı doğrulamanız gerekiyor.", "LoginIsNotAllowed": "Giriş yapamazsınız! E-posta adresinizi ya da telefon numaranızı doğrulamanız gerekiyor.",
"SelfRegistrationDisabledMessage": "Bu uygulama için kullanıcıların kendi kendilerine kaydolmaları engellenmiştir. Yeni bir kullanıcı kaydetmek için lütfen uygulama yöneticisi ile iletişime geçin.", "SelfRegistrationDisabledMessage": "Bu uygulama için kullanıcıların kendi kendilerine kaydolmaları engellenmiştir. Yeni bir kullanıcı kaydetmek için lütfen uygulama yöneticisi ile iletişime geçin.",
"Login": "Login", "Login": "Giriş yap",
"Cancel": "Cancel", "Cancel": "İptal",
"Register": "Register", "Register": "Kayıt ol",
"UseAnotherServiceToLogIn.": "Use another service to log in.", "UseAnotherServiceToLogIn.": "Başka bir servisle giriş yap.",
"InvalidLoginRequest": "Invalid login request", "InvalidLoginRequest": "Başarısız giriş isteği",
"ThereAreNoLoginSchemesConfiguredForThisClient": "There are no login schemes configured for this client.", "ThereAreNoLoginSchemesConfiguredForThisClient": "Bu client için konfigüre edilmiş giriş şeması bulunamadı.",
"LogInUsingYourProviderAccount": "Log in using your {0} account" "LogInUsingYourProviderAccount": "{0} hesabınızla giriş yapın."
} }
} }

2
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs

@ -23,5 +23,7 @@
public const int MaxHttpMethodLength = 16; public const int MaxHttpMethodLength = 16;
public const int MaxUserNameLength = 256; public const int MaxUserNameLength = 256;
public const int MaxTenantNameLength = 64;
} }
} }

41
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs

@ -19,6 +19,8 @@ namespace Volo.Abp.AuditLogging
public virtual Guid? TenantId { get; protected set; } public virtual Guid? TenantId { get; protected set; }
public virtual string TenantName { get; protected set; }
public virtual Guid? ImpersonatorUserId { get; protected set; } public virtual Guid? ImpersonatorUserId { get; protected set; }
public virtual Guid? ImpersonatorTenantId { get; protected set; } public virtual Guid? ImpersonatorTenantId { get; protected set; }
@ -31,9 +33,9 @@ namespace Volo.Abp.AuditLogging
public virtual string ClientName { get; protected set; } public virtual string ClientName { get; protected set; }
public string ClientId { get; set; } public virtual string ClientId { get; set; }
public string CorrelationId { get; set; } public virtual string CorrelationId { get; set; }
public virtual string BrowserInfo { get; protected set; } public virtual string BrowserInfo { get; protected set; }
@ -59,8 +61,9 @@ namespace Volo.Abp.AuditLogging
public AuditLog(IGuidGenerator guidGenerator, AuditLogInfo auditInfo) public AuditLog(IGuidGenerator guidGenerator, AuditLogInfo auditInfo)
{ {
Id = guidGenerator.Create(); Id = guidGenerator.Create();
ApplicationName = auditInfo.ApplicationName; ApplicationName = auditInfo.ApplicationName.Truncate(AuditLogConsts.MaxApplicationNameLength);
TenantId = auditInfo.TenantId; TenantId = auditInfo.TenantId;
TenantName = auditInfo.TenantName.Truncate(AuditLogConsts.MaxTenantNameLength);
UserId = auditInfo.UserId; UserId = auditInfo.UserId;
UserName = auditInfo.UserName.Truncate(AuditLogConsts.MaxUserNameLength); UserName = auditInfo.UserName.Truncate(AuditLogConsts.MaxUserNameLength);
ExecutionTime = auditInfo.ExecutionTime; ExecutionTime = auditInfo.ExecutionTime;
@ -75,11 +78,33 @@ namespace Volo.Abp.AuditLogging
HttpStatusCode = auditInfo.HttpStatusCode; HttpStatusCode = auditInfo.HttpStatusCode;
ImpersonatorUserId = auditInfo.ImpersonatorUserId; ImpersonatorUserId = auditInfo.ImpersonatorUserId;
ImpersonatorTenantId = auditInfo.ImpersonatorTenantId; ImpersonatorTenantId = auditInfo.ImpersonatorTenantId;
ExtraProperties = auditInfo.ExtraProperties.ToDictionary(pair => pair.Key, pair => pair.Value);
EntityChanges = auditInfo.EntityChanges.Select(e => new EntityChange(guidGenerator, Id, e)).ToList(); ExtraProperties = auditInfo
Actions = auditInfo.Actions.Select(e => new AuditLogAction(guidGenerator.Create(), Id, e)).ToList(); .ExtraProperties?
Exceptions = auditInfo.Exceptions.JoinAsString(Environment.NewLine).Truncate(AuditLogConsts.MaxExceptionsLength); .ToDictionary(pair => pair.Key, pair => pair.Value)
Comments = auditInfo.Comments.JoinAsString(Environment.NewLine).Truncate(AuditLogConsts.MaxCommentsLength); ?? new Dictionary<string, object>();
EntityChanges = auditInfo
.EntityChanges?
.Select(entityChangeInfo => new EntityChange(guidGenerator, Id, entityChangeInfo, tenantId: auditInfo.TenantId))
.ToList()
?? new List<EntityChange>();
Actions = auditInfo
.Actions?
.Select(auditLogActionInfo => new AuditLogAction(guidGenerator.Create(), Id, auditLogActionInfo, tenantId: auditInfo.TenantId))
.ToList()
?? new List<AuditLogAction>();
Exceptions = auditInfo
.Exceptions?
.JoinAsString(Environment.NewLine)
.Truncate(AuditLogConsts.MaxExceptionsLength);
Comments = auditInfo
.Comments?
.JoinAsString(Environment.NewLine)
.Truncate(AuditLogConsts.MaxCommentsLength);
} }
} }
} }

4
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLogAction.cs

@ -32,11 +32,11 @@ namespace Volo.Abp.AuditLogging
ExtraProperties = new Dictionary<string, object>(); ExtraProperties = new Dictionary<string, object>();
} }
public AuditLogAction(Guid id, Guid auditLogId, AuditLogActionInfo actionInfo) public AuditLogAction(Guid id, Guid auditLogId, AuditLogActionInfo actionInfo, Guid? tenantId = null)
{ {
Id = id; Id = id;
TenantId = actionInfo.TenantId; TenantId = tenantId;
AuditLogId = auditLogId; AuditLogId = auditLogId;
ExecutionTime = actionInfo.ExecutionTime; ExecutionTime = actionInfo.ExecutionTime;
ExecutionDuration = actionInfo.ExecutionDuration; ExecutionDuration = actionInfo.ExecutionDuration;

23
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityChange.cs

@ -20,6 +20,8 @@ namespace Volo.Abp.AuditLogging
public virtual EntityChangeType ChangeType { get; protected set; } public virtual EntityChangeType ChangeType { get; protected set; }
public virtual Guid? EntityTenantId { get; protected set; }
public virtual string EntityId { get; protected set; } public virtual string EntityId { get; protected set; }
public virtual string EntityTypeFullName { get; protected set; } public virtual string EntityTypeFullName { get; protected set; }
@ -33,17 +35,30 @@ namespace Volo.Abp.AuditLogging
ExtraProperties = new Dictionary<string, object>(); ExtraProperties = new Dictionary<string, object>();
} }
public EntityChange(IGuidGenerator guidGenerator, Guid auditLogId, EntityChangeInfo entityChangeInfo) public EntityChange(
IGuidGenerator guidGenerator,
Guid auditLogId,
EntityChangeInfo entityChangeInfo,
Guid? tenantId = null)
{ {
Id = guidGenerator.Create(); Id = guidGenerator.Create();
AuditLogId = auditLogId; AuditLogId = auditLogId;
TenantId = entityChangeInfo.TenantId; TenantId = tenantId;
ChangeTime = entityChangeInfo.ChangeTime; ChangeTime = entityChangeInfo.ChangeTime;
ChangeType = entityChangeInfo.ChangeType; ChangeType = entityChangeInfo.ChangeType;
EntityId = entityChangeInfo.EntityId.Truncate(EntityChangeConsts.MaxEntityTypeFullNameLength); EntityId = entityChangeInfo.EntityId.Truncate(EntityChangeConsts.MaxEntityTypeFullNameLength);
EntityTypeFullName = entityChangeInfo.EntityTypeFullName.TruncateFromBeginning(EntityChangeConsts.MaxEntityTypeFullNameLength); EntityTypeFullName = entityChangeInfo.EntityTypeFullName.TruncateFromBeginning(EntityChangeConsts.MaxEntityTypeFullNameLength);
PropertyChanges = entityChangeInfo.PropertyChanges.Select(p => new EntityPropertyChange(guidGenerator, Id, p)).ToList();
ExtraProperties = entityChangeInfo.ExtraProperties.ToDictionary(pair => pair.Key, pair => pair.Value); PropertyChanges = entityChangeInfo
.PropertyChanges?
.Select(p => new EntityPropertyChange(guidGenerator, Id, p, tenantId))
.ToList()
?? new List<EntityPropertyChange>();
ExtraProperties = entityChangeInfo
.ExtraProperties?
.ToDictionary(pair => pair.Key, pair => pair.Value)
?? new Dictionary<string, object>();
} }
} }
} }

8
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityPropertyChange.cs

@ -26,10 +26,14 @@ namespace Volo.Abp.AuditLogging
} }
public EntityPropertyChange(IGuidGenerator guidGenerator, Guid entityChangeId, EntityPropertyChangeInfo entityChangeInfo) public EntityPropertyChange(
IGuidGenerator guidGenerator,
Guid entityChangeId,
EntityPropertyChangeInfo entityChangeInfo,
Guid? tenantId = null)
{ {
Id = guidGenerator.Create(); Id = guidGenerator.Create();
TenantId = entityChangeInfo.TenantId; TenantId = tenantId;
EntityChangeId = entityChangeId; EntityChangeId = entityChangeId;
NewValue = entityChangeInfo.NewValue.Truncate(EntityPropertyChangeConsts.MaxNewValueLength); NewValue = entityChangeInfo.NewValue.Truncate(EntityPropertyChangeConsts.MaxNewValueLength);
OriginalValue = entityChangeInfo.OriginalValue.Truncate(EntityPropertyChangeConsts.MaxOriginalValueLength); OriginalValue = entityChangeInfo.OriginalValue.Truncate(EntityPropertyChangeConsts.MaxOriginalValueLength);

11
modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AuditLogRepository_Tests.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Volo.Abp.AuditLogging.EntityFrameworkCore
{
public class AuditLogRepository_Tests : AuditLogRepository_Tests<AbpAuditLoggingEntityFrameworkCoreTestModule>
{
}
}

11
modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditLogRepository_Tests.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Volo.Abp.AuditLogging.MongoDB
{
public class AuditLogRepository_Tests : AuditLogRepository_Tests<AbpAuditLoggingMongoDbTestModule>
{
}
}

232
modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogRepository_Tests.cs

@ -0,0 +1,232 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Auditing;
using Volo.Abp.Guids;
using Volo.Abp.Modularity;
using Xunit;
namespace Volo.Abp.AuditLogging
{
public abstract class AuditLogRepository_Tests<TStartupModule> : AuditLoggingTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
protected IAuditLogRepository AuditLogRepository { get; }
protected IGuidGenerator GuidGenerator { get; }
protected AuditLogRepository_Tests()
{
AuditLogRepository = GetRequiredService<IAuditLogRepository>();
GuidGenerator = GetRequiredService<IGuidGenerator>();
}
[Fact]
public async Task GetListAsync()
{
// Arrange
var userId = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06");
var userId2 = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06");
var ipAddress = "153.1.7.61";
var firstComment = "first Comment";
var log1 = new AuditLogInfo
{
UserId = userId,
ImpersonatorUserId = Guid.NewGuid(),
ImpersonatorTenantId = Guid.NewGuid(),
ExecutionTime = DateTime.Today,
ExecutionDuration = 42,
ClientIpAddress = ipAddress,
ClientName = "MyDesktop",
BrowserInfo = "Chrome",
Comments = new List<string> { firstComment, "Second Comment" },
UserName = "Douglas",
EntityChanges = {
new EntityChangeInfo
{
EntityId = Guid.NewGuid().ToString(),
EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity",
ChangeType = EntityChangeType.Created,
ChangeTime = DateTime.Now,
PropertyChanges = new List<EntityPropertyChangeInfo>
{
new EntityPropertyChangeInfo
{
PropertyTypeFullName = typeof(string).FullName,
PropertyName = "Name",
NewValue = "New value",
OriginalValue = null
}
}
},
new EntityChangeInfo
{
EntityId = Guid.NewGuid().ToString(),
EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity",
ChangeType = EntityChangeType.Created,
ChangeTime = DateTime.Now,
PropertyChanges = new List<EntityPropertyChangeInfo>
{
new EntityPropertyChangeInfo
{
PropertyTypeFullName = typeof(string).FullName,
PropertyName = "Name",
NewValue = "New value",
OriginalValue = null
}
}
}
}
};
var log2 = new AuditLogInfo
{
UserId = userId2,
ImpersonatorUserId = Guid.NewGuid(),
ImpersonatorTenantId = Guid.NewGuid(),
ExecutionTime = DateTime.Today,
ExecutionDuration = 42,
ClientIpAddress = ipAddress,
ClientName = "MyDesktop",
BrowserInfo = "Chrome",
Comments = new List<string> { firstComment, "Second Comment" },
HttpStatusCode = (int?)HttpStatusCode.BadGateway,
EntityChanges = {
new EntityChangeInfo
{
EntityId = Guid.NewGuid().ToString(),
EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity",
ChangeType = EntityChangeType.Created,
ChangeTime = DateTime.Now,
PropertyChanges = new List<EntityPropertyChangeInfo>
{
new EntityPropertyChangeInfo
{
PropertyTypeFullName = typeof(string).FullName,
PropertyName = "Name",
NewValue = "New value",
OriginalValue = null
}
}
}
}
};
AuditLogRepository.Insert(new AuditLog(GuidGenerator, log1));
AuditLogRepository.Insert(new AuditLog(GuidGenerator, log2));
//Assert
var logs = await AuditLogRepository.GetListAsync();
logs.ShouldNotBeNull();
logs.ShouldContain(x => x.UserId == userId);
logs.ShouldContain(x => x.UserId == userId2);
}
[Fact]
public async Task GetCountAsync()
{
// Arrange
var userId = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06");
var userId2 = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06");
var ipAddress = "153.1.7.61";
var firstComment = "first Comment";
var log1 = new AuditLogInfo
{
UserId = userId,
ImpersonatorUserId = Guid.NewGuid(),
ImpersonatorTenantId = Guid.NewGuid(),
ExecutionTime = DateTime.Today,
ExecutionDuration = 42,
ClientIpAddress = ipAddress,
ClientName = "MyDesktop",
BrowserInfo = "Chrome",
Comments = new List<string> { firstComment, "Second Comment" },
UserName = "Douglas",
EntityChanges = {
new EntityChangeInfo
{
EntityId = Guid.NewGuid().ToString(),
EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity",
ChangeType = EntityChangeType.Created,
ChangeTime = DateTime.Now,
PropertyChanges = new List<EntityPropertyChangeInfo>
{
new EntityPropertyChangeInfo
{
PropertyTypeFullName = typeof(string).FullName,
PropertyName = "Name",
NewValue = "New value",
OriginalValue = null
}
}
},
new EntityChangeInfo
{
EntityId = Guid.NewGuid().ToString(),
EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity",
ChangeType = EntityChangeType.Created,
ChangeTime = DateTime.Now,
PropertyChanges = new List<EntityPropertyChangeInfo>
{
new EntityPropertyChangeInfo
{
PropertyTypeFullName = typeof(string).FullName,
PropertyName = "Name",
NewValue = "New value",
OriginalValue = null
}
}
}
}
};
var log2 = new AuditLogInfo
{
UserId = userId2,
ImpersonatorUserId = Guid.NewGuid(),
ImpersonatorTenantId = Guid.NewGuid(),
ExecutionTime = DateTime.Today,
ExecutionDuration = 42,
ClientIpAddress = ipAddress,
ClientName = "MyDesktop",
BrowserInfo = "Chrome",
Comments = new List<string> { firstComment, "Second Comment" },
HttpStatusCode = (int?)HttpStatusCode.BadGateway,
EntityChanges = {
new EntityChangeInfo
{
EntityId = Guid.NewGuid().ToString(),
EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity",
ChangeType = EntityChangeType.Created,
ChangeTime = DateTime.Now,
PropertyChanges = new List<EntityPropertyChangeInfo>
{
new EntityPropertyChangeInfo
{
PropertyTypeFullName = typeof(string).FullName,
PropertyName = "Name",
NewValue = "New value",
OriginalValue = null
}
}
}
}
};
AuditLogRepository.Insert(new AuditLog(GuidGenerator, log1));
AuditLogRepository.Insert(new AuditLog(GuidGenerator, log2));
//Assert
var logs = await AuditLogRepository.GetCountAsync();
logs.ShouldBe(2);
}
}
}

13
modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogging_Repository_Resolve_Tests.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp.Modularity;
namespace Volo.Abp.AuditLogging
{
public class AuditLogging_Repository_Resolve_Tests<TStartupModule> : AuditLoggingTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
}
}

1
modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditStore_Basic_Tests.cs

@ -78,7 +78,6 @@ namespace Volo.Abp.AuditLogging
insertedLog.EntityChanges.First().PropertyChanges.Count.ShouldBeGreaterThan(0); insertedLog.EntityChanges.First().PropertyChanges.Count.ShouldBeGreaterThan(0);
} }
[Fact] [Fact]
public async Task Should_Get_List_Of_Audit_Logs() public async Task Should_Get_List_Of_Audit_Logs()
{ {

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

Loading…
Cancel
Save