Browse Source

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

pull/771/head
梁士伟 8 years ago
parent
commit
f0cec1e091
  1. 9
      .gitignore
  2. 61
      abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs
  3. 4
      abp_io/src/Volo.AbpWebSite.Web/Volo.AbpWebSite.Web.csproj
  4. 3
      build-all.ps1
  5. 2
      common.props
  6. 3
      docs/en/Event-Bus.md
  7. 30
      docs/en/Microservice-Architecture.md
  8. 40
      docs/en/Samples/Microservice-Demo.md
  9. 6
      docs/en/Virtual-File-System.md
  10. 13
      docs/en/docs-nav.json
  11. BIN
      docs/en/images/microservice-sample-diagram.png
  12. 6
      docs/zh-Hans/Virtual-File-System.md
  13. 3
      docs/zh-Hans/docs-nav.json
  14. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClient.cs
  15. 32
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperLocalizer.cs
  16. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormControlSize.cs
  17. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
  18. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelper.cs
  19. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs
  20. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs
  21. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DisabledInput.cs
  22. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/FormControlSize.cs
  23. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/InputInfoText.cs
  24. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/Placeholder.cs
  25. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/ReadOnlyInput.cs
  26. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/TextArea.cs
  27. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/IAbpTagHelperLocalizer.cs
  28. 14
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs
  29. 38
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollection.cs
  30. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs
  31. 36
      framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Builder/AbpAspNetCoreMvcApplicationBuilderExtensions.cs
  32. 8
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
  33. 5
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalControllerFeatureProvider.cs
  34. 39
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs
  35. 26
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/GlobalizationHelper.cs
  36. 2
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/ApplicationInitializationContextExtensions.cs
  37. 6
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs
  38. 1
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheNameAttribute.cs
  39. 24
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheOptions.cs
  40. 228
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
  41. 10
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCacheOptions.cs
  42. 20
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs
  43. 10
      framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionCommonExtensions.cs
  44. 4
      framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
  45. 13
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/AbpEventBusRabbitMqModule.cs
  46. 19
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpEventBusRabbitMqModule.cs
  47. 28
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs
  48. 4
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqEventBusOptions.cs
  49. 8
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IocEventHandlerFactory.cs
  50. 1
      framework/src/Volo.Abp.Http.Client.IdentityModel/Volo.Abp.Http.Client.IdentityModel.csproj
  51. 2
      framework/src/Volo.Abp.Http.Client.IdentityModel/Volo/Abp/Http/Client/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs
  52. 6
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
  53. 2
      framework/src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj
  54. 2
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IIdentityModelHttpClientAuthenticator.cs
  55. 10
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticateContext.cs
  56. 27
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticatorExtensions.cs
  57. 14
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs
  58. 1
      framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj
  59. 5
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs
  60. 6
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqModule.cs
  61. 4
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqOptions.cs
  62. 2
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs
  63. 2
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetListExtensions.cs
  64. 1
      framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs
  65. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj
  66. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs
  67. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/AbpAspNetCoreMvcUiBootstrapDemoModule.cs
  68. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml
  69. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs
  70. 22
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/AbpCachingTestModule.cs
  71. 51
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_ConfigureOptions_Test.cs
  72. 52
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs
  73. 20
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/PersonCacheItem.cs
  74. 7
      modules/account/Volo.Abp.Account.sln
  75. 14
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs
  76. 239
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
  77. 2
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/_ViewImports.cshtml
  78. 27
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Properties/launchSettings.json
  79. 33
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj
  80. 6
      modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs
  81. 73
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml
  82. 156
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
  83. 1
      modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj
  84. 4
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Volo.BloggingTestApp.EntityFrameworkCore.csproj
  85. 48
      modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs
  86. 2
      modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj
  87. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/ICommentAppService.cs
  88. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Tagging/Dtos/GetPopularTagsInput.cs
  89. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Tagging/ITagAppService.cs
  90. 2
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs
  91. 8
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAppService.cs
  92. 4
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Tagging/TagAppService.cs
  93. 11
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogsController.cs
  94. 53
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/CommentsController.cs
  95. 65
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/PostsController.cs
  96. 32
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/TagsController.cs
  97. 2
      modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs
  98. 4
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Create.cshtml.cs
  99. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml.cs
  100. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml.cs

9
.gitignore

@ -285,3 +285,12 @@ framework/test/Volo\.Abp\.AspNetCore\.Mvc\.UI\.Bootstrap\.Demo/package-lock\.jso
modules/blogging/app/Volo\.BloggingTestApp/package-lock\.json modules/blogging/app/Volo\.BloggingTestApp/package-lock\.json
templates/mvc/src/MyCompanyName\.MyProjectName\.Web/package-lock\.json templates/mvc/src/MyCompanyName\.MyProjectName\.Web/package-lock\.json
samples/MicroserviceDemo/applications/AuthServer.Host/Logs/logs.txt
samples/MicroserviceDemo/microservices/IdentityService.Host/Logs/logs.txt
samples/MicroserviceDemo/applications/BackendAdminApp.Host/Logs/logs.txt
samples/MicroserviceDemo/microservices/ProductService.Host/Logs/logs.txt
samples/MicroserviceDemo/gateways/InternalGateway.Host/Logs/logs.txt
samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/Logs/logs.txt
samples/MicroserviceDemo/applications/PublicWebSite.Host/Logs/logs.txt
samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/Logs/logs.txt
samples/MicroserviceDemo/microservices/BloggingService.Host/Logs/logs.txt

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

@ -8,13 +8,13 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Account.Web; using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI; using Volo.Abp.AspNetCore.Mvc.UI;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; 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;
@ -22,6 +22,8 @@ using Volo.Abp.Identity;
using Volo.Abp.Identity.Web; using Volo.Abp.Identity.Web;
using Volo.Abp.Localization; using Volo.Abp.Localization;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.Threading; using Volo.Abp.Threading;
using Volo.Abp.UI; using Volo.Abp.UI;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;
@ -41,6 +43,8 @@ namespace Volo.AbpWebSite
typeof(AbpAccountWebModule), typeof(AbpAccountWebModule),
typeof(AbpIdentityApplicationModule), typeof(AbpIdentityApplicationModule),
typeof(AbpIdentityWebModule), typeof(AbpIdentityWebModule),
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpPermissionManagementDomainIdentityModule),
typeof(BloggingApplicationModule), typeof(BloggingApplicationModule),
typeof(BloggingWebModule) typeof(BloggingWebModule)
)] )]
@ -110,14 +114,14 @@ namespace Volo.AbpWebSite
{ {
services.Configure<VirtualFileSystemOptions>(options => services.Configure<VirtualFileSystemOptions>(options =>
{ {
options.FileSets.ReplaceEmbeddedByPyhsical<AbpUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.UI", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<AbpUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiBootstrapModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Bootstrap", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiBootstrapModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Bootstrap", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiThemeSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiThemeSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<DocsDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}docs{0}src{0}Volo.Docs.Domain", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<DocsDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}docs{0}src{0}Volo.Docs.Domain", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<DocsWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}docs{0}src{0}Volo.Docs.Web", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<DocsWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}docs{0}src{0}Volo.Docs.Web", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<BloggingWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}blogging{0}src{0}Volo.Blogging.Web", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<BloggingWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}blogging{0}src{0}Volo.Blogging.Web", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAccountWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}account{0}src{0}Volo.Abp.Account.Web", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<AbpAccountWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}account{0}src{0}Volo.Abp.Account.Web", Path.DirectorySeparatorChar)));
}); });
} }
} }
@ -158,29 +162,28 @@ namespace Volo.AbpWebSite
app.UseVirtualFiles(); app.UseVirtualFiles();
app.UseAuthentication(); app.UseAuthentication();
//TODO: Create an extension method!
app.UseMvc(routes =>
{
routes.MapRoute(
name: "defaultWithArea",
template: "{area}/{controller=Home}/{action=Index}/{id?}");
routes.MapRoute( app.UseMvcWithDefaultRouteAndArea();
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
AsyncHelper.RunSync(async () => using (var scope = context.ServiceProvider.CreateScope())
{ {
await context.ServiceProvider AsyncHelper.RunSync(async () =>
.GetRequiredService<IIdentityDataSeeder>() {
.SeedAsync( await scope.ServiceProvider
"1q2w3E*", .GetRequiredService<IIdentityDataSeeder>()
IdentityPermissions.GetAll() .SeedAsync(
.Union(BloggingPermissions.GetAll()) "1q2w3E*"
); );
});
await scope.ServiceProvider
.GetRequiredService<IPermissionDataSeeder>()
.SeedAsync(
RolePermissionValueProvider.ProviderName,
"admin",
IdentityPermissions.GetAll().Union(BloggingPermissions.GetAll())
);
});
}
} }
} }
} }

4
abp_io/src/Volo.AbpWebSite.Web/Volo.AbpWebSite.Web.csproj

@ -31,6 +31,8 @@
<ProjectReference Include="..\..\..\modules\blogging\src\Volo.Blogging.Application\Volo.Blogging.Application.csproj" /> <ProjectReference Include="..\..\..\modules\blogging\src\Volo.Blogging.Application\Volo.Blogging.Application.csproj" />
<ProjectReference Include="..\..\..\modules\identity\src\Volo.Abp.Identity.Application\Volo.Abp.Identity.Application.csproj" /> <ProjectReference Include="..\..\..\modules\identity\src\Volo.Abp.Identity.Application\Volo.Abp.Identity.Application.csproj" />
<ProjectReference Include="..\..\..\modules\identity\src\Volo.Abp.Identity.Web\Volo.Abp.Identity.Web.csproj" /> <ProjectReference Include="..\..\..\modules\identity\src\Volo.Abp.Identity.Web\Volo.Abp.Identity.Web.csproj" />
<ProjectReference Include="..\..\..\modules\identity\src\Volo.Abp.PermissionManagement.Domain.Identity\Volo.Abp.PermissionManagement.Domain.Identity.csproj" />
<ProjectReference Include="..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.Application\Volo.Abp.PermissionManagement.Application.csproj" />
<ProjectReference Include="..\..\..\modules\account\src\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" /> <ProjectReference Include="..\..\..\modules\account\src\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" />
</ItemGroup> </ItemGroup>
@ -38,7 +40,7 @@
<Content Include=".well-known/README.md"> <Content Include=".well-known/README.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Pages\*.js" > <Content Include="Pages\*.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Pages\*.css" /> <Content Include="Pages\*.css" />

3
build-all.ps1

@ -20,7 +20,8 @@ $solutionPaths = (
"abp_io", "abp_io",
"templates/module", "templates/module",
"templates/service", "templates/service",
"templates/mvc" "templates/mvc",
"samples/MicroserviceDemo"
) )
# Build all solutions # Build all solutions

2
common.props

@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Version>0.12.0</Version> <Version>0.13.0</Version>
<NoWarn>$(NoWarn);CS1591</NoWarn> <NoWarn>$(NoWarn);CS1591</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl> <PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io</PackageProjectUrl> <PackageProjectUrl>https://abp.io</PackageProjectUrl>

3
docs/en/Event-Bus.md

@ -0,0 +1,3 @@
# Event Bus
TODO

30
docs/en/Microservice-Architecture.md

@ -0,0 +1,30 @@
# Microservice Architecture
*"Microservices are a software development technique—a variant of the **service-oriented architecture** (SOA) architectural style that structures an application as a collection of **loosely coupled services**. In a microservices architecture, services are **fine-grained** and the protocols are **lightweight**. The benefit of decomposing an application into different smaller services is that it improves **modularity**. This makes the application easier to understand, develop, test, and become more resilient to architecture erosion. It **parallelizes development** by enabling small autonomous teams to **develop, deploy and scale** their respective services independently. It also allows the architecture of an individual service to emerge through **continuous refactoring**. Microservices-based architectures enable **continuous delivery and deployment**."*
— [Wikipedia](https://en.wikipedia.org/wiki/Microservices)
## Introduction
One of the major goals of the ABP framework is to provide a convenient infrastructure to create microservice solutions. To make this possible,
* Provides a [module system](Module-Development-Basics.md) that allows you to split your application into modules where each module may have its own database, entities, services, APIs, UI components/pages... etc.
* Offers an [architectural model](Best-Practices/Module-Architecture.md) to develop your modules to be compatible to microservice development and deployment.
* Provides [best practices guide](Best-Practices/Index.md) to develop your module standards-compliance.
* Provides base infrastructure to implement [Domain Driven Design](Domain-Driven-Design.md) in your microservices.
* Provide services to [automatically create REST-style APIs](AspNetCore/Auto-API-Controllers.md) from your application services.
* Provide services to [automatically create C# API clients](AspNetCore/Dynamic-CSharp-API-Clients.md) that makes easy to consume your services from another service/application.
* Provides a [distributed event bus](Event-Bus.md) to communicate your services.
* Provides many other services to make your daily development easier.
## Microservice for New Applications
One common advise to start a new solution is **always to start with a monolith**, keep it modular and split into microservices once the monolith becomes a problem. This makes your progress fast in the beginning especially if your team is small and you don't want to deal with challanges of the microservice architecture.
However, developing such a well-modular application can be a problem since it is **hard to keep modules isolated** from each other as you would do it for microservices (see [Stefan Tilkov's article](https://martinfowler.com/articles/dont-start-monolith.html) about that). Microservice architecture naturally forces you to develop well isolated services, but in a modular monolithic application it's easy to tight couple modules to each other and design **weak module boundaries** and API contracts.
ABP can help you in that point by oferring a **microservice-compatible, strict module architecture** where your module is splitted into multiple layers/projects and developed in its own VS solution completely isolated and independent from other modules. Such a developed module is a natural microservice yet it can be easily plugged-in a monolithic application. See the [module development best practice guide](Best-Practices/Index.md) that offers a **microservice-first module design**. All [standard ABP modules](https://github.com/abpframework/abp/tree/master/modules) are developed based on this guide. So, you can use these modules by embedding into your monolithic solution or deploy them separately and use via remote APIs. They can share a single database or can have their own database based on your simple configuration.
## Microservice Demo Solution
The [sample microservice solution](Samples/Microservice-Demo.md) demonstrates a complete microservice solution based on the ABP framework.

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

@ -0,0 +1,40 @@
# Microservice Demo Solution
*"Microservices are a software development technique—a variant of the **service-oriented architecture** (SOA) architectural style that structures an application as a collection of **loosely coupled services**. In a microservices architecture, services are **fine-grained** and the protocols are **lightweight**. The benefit of decomposing an application into different smaller services is that it improves **modularity**. This makes the application easier to understand, develop, test, and become more resilient to architecture erosion. It **parallelizes development** by enabling small autonomous teams to **develop, deploy and scale** their respective services independently. It also allows the architecture of an individual service to emerge through **continuous refactoring**. Microservices-based architectures enable **continuous delivery and deployment**."*
— [Wikipedia](https://en.wikipedia.org/wiki/Microservices)
## Introduction
One of the major goals of the ABP framework is to provide a [convenient infrastructure to create microservice solutions](Microservice-Architecture.md).
This sample aims to demonstrate a simple yet complete microservice solution;
* Has multiple, independent, self-deployable **microservices**.
* Multiple **web applications**, each uses a different API gateway.
* Has multiple **gateways** / BFFs (Backend for Frontends) developed using the [Ocelot](https://github.com/ThreeMammals/Ocelot) library.
* Has an **authentication service** developed using the [IdentityServer](https://identityserver.io/) framework. It's also a SSO (Single Sign On) application with necessary UIs.
* Has **multiple databases**. Some microservices has their own database while some services/applications shares a database (to demonstrate different use cases).
* Has different types of databases: **SQL Server** (with **Entity Framework Core** ORM) and **MongoDB**.
* Has a **console application** to show the simplest way of using a service by authenticating.
* Uses [Redis](https://redis.io/) for **distributed caching**.
* Uses [RabbitMQ](https://www.rabbitmq.com/) for service-to-service **messaging**.
* Uses [Kubernates](https://kubernetes.io/) to **deploy** & run all services and applications.
The diagram below shows the system:
![microservice-sample-diagram](../images/microservice-sample-diagram.png)
### Source Code
You can get the source code from [the GitHub repository](https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo).
### Status
This sample is still in development, not completed yet.
## Microservices
### Identity Service
...

6
docs/en/Virtual-File-System.md

@ -108,7 +108,7 @@ Embedding a file into a module assembly and being able to use it from another pr
Let's assume that you're developing a module that contains an embedded JavaScript file. Whenever you change this file you must re-compile the project, re-start the application and refresh the browser page to take the change. Obviously, this is very time consuming and tedious. Let's assume that you're developing a module that contains an embedded JavaScript file. Whenever you change this file you must re-compile the project, re-start the application and refresh the browser page to take the change. Obviously, this is very time consuming and tedious.
What is needed is the ability for the application to directly use the physical file at development time and a have a browser refresh reflect any change made in the JavaScript file. The `ReplaceEmbeddedByPyhsical` method makes all this possible. What is needed is the ability for the application to directly use the physical file at development time and a have a browser refresh reflect any change made in the JavaScript file. The `ReplaceEmbeddedByPhysical` method makes all this possible.
The example below shows an application that depends on a module (`MyModule`) that itself contains embedded files. The application can reach the source code of the module at development time. The example below shows an application that depends on a module (`MyModule`) that itself contains embedded files. The application can reach the source code of the module at development time.
@ -124,8 +124,8 @@ public class MyWebAppModule : AbpModule
{ {
Configure<VirtualFileSystemOptions>(options => Configure<VirtualFileSystemOptions>(options =>
{ {
//ReplaceEmbeddedByPyhsical gets the root folder of the MyModule project //ReplaceEmbeddedByPhysical gets the root folder of the MyModule project
options.FileSets.ReplaceEmbeddedByPyhsical<MyModule>( options.FileSets.ReplaceEmbeddedByPhysical<MyModule>(
Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}MyModuleProject", Path.DirectorySeparatorChar)) Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}MyModuleProject", Path.DirectorySeparatorChar))
); );
}); });

13
docs/en/docs-nav.json

@ -256,10 +256,23 @@
} }
] ]
}, },
{
"text": "Samples",
"items": [
{
"text": "Microservice Demo",
"path": "Samples/Microservice-Demo.md"
}
]
},
{ {
"text": "Application Modules", "text": "Application Modules",
"path": "Modules/Index.md" "path": "Modules/Index.md"
}, },
{
"text": "Microservice Architecture",
"path": "Microservice-Architecture.md"
},
{ {
"text": "Testing" "text": "Testing"
}, },

BIN
docs/en/images/microservice-sample-diagram.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

6
docs/zh-Hans/Virtual-File-System.md

@ -109,7 +109,7 @@ public class MyService
假设你正在开发一个包含嵌入式JavaScript文件的模块. 当你更改文件时, 你必须重新编译项目, 重新启动应用程序并刷新浏览器页面以进行更改. 显然, 这是非常耗时和乏味的. 假设你正在开发一个包含嵌入式JavaScript文件的模块. 当你更改文件时, 你必须重新编译项目, 重新启动应用程序并刷新浏览器页面以进行更改. 显然, 这是非常耗时和乏味的.
我们需要的是应用程序在开发时直接使用物理文件的能力, 让浏览器刷新时同步JavaScript文件的任何更改. `ReplaceEmbeddedByPyhsical` 方法使其成为可能. 我们需要的是应用程序在开发时直接使用物理文件的能力, 让浏览器刷新时同步JavaScript文件的任何更改. `ReplaceEmbeddedByPhysical` 方法使其成为可能.
下面的示例展示了应用程序依赖于包含嵌入文件的模块("MyModule"), 并且应用程序可以在开发过程中直接使用模块的源代码. 下面的示例展示了应用程序依赖于包含嵌入文件的模块("MyModule"), 并且应用程序可以在开发过程中直接使用模块的源代码.
@ -125,8 +125,8 @@ public class MyWebAppModule : AbpModule
{ {
Configure<VirtualFileSystemOptions>(options => Configure<VirtualFileSystemOptions>(options =>
{ {
//ReplaceEmbeddedByPyhsical gets the root folder of the MyModule project //ReplaceEmbeddedByPhysical gets the root folder of the MyModule project
options.FileSets.ReplaceEmbeddedByPyhsical<MyModule>( options.FileSets.ReplaceEmbeddedByPhysical<MyModule>(
Path.Combine(hostingEnvironment.ContentRootPath, "..\\MyModuleProject") Path.Combine(hostingEnvironment.ContentRootPath, "..\\MyModuleProject")
); );
}); });

3
docs/zh-Hans/docs-nav.json

@ -171,7 +171,8 @@
"text": "应用服务层", "text": "应用服务层",
"items": [ "items": [
{ {
"text": "应用服务" "text": "应用服务",
"path": "Application-Services.md"
}, },
{ {
"text": "数据传输对象(DTO)" "text": "数据传输对象(DTO)"

2
framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClient.cs

@ -45,7 +45,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Client
async () => await Proxy.Service.GetAsync(), async () => await Proxy.Service.GetAsync(),
() => new DistributedCacheEntryOptions () => new DistributedCacheEntryOptions
{ {
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5)
} }
); );

32
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperLocalizer.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -20,27 +21,38 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers
public string GetLocalizedText(string text, ModelExplorer explorer) public string GetLocalizedText(string text, ModelExplorer explorer)
{ {
var localizer = GetStringLocalizer(explorer); var resourceType = GetResourceTypeFromModelExplorer(explorer);
var localizer = GetStringLocalizer(resourceType);
return localizer == null ? text : localizer[text].Value; return localizer == null ? text : localizer[text].Value;
} }
public IStringLocalizer GetLocalizer(ModelExplorer explorer) public IStringLocalizer GetLocalizer(ModelExplorer explorer)
{ {
return GetStringLocalizer(explorer); var resourceType = GetResourceTypeFromModelExplorer(explorer);
return GetStringLocalizer(resourceType);
} }
private IStringLocalizer GetStringLocalizer(ModelExplorer explorer) public IStringLocalizer GetLocalizer(Assembly assembly)
{ {
IStringLocalizer localizer = null; var resourceType = _options.AssemblyResources.GetOrDefault(assembly);
var resourceType = _options.AssemblyResources.GetOrDefault(explorer.Container.ModelType.Assembly); return GetStringLocalizer(resourceType);
}
if (resourceType != null) public IStringLocalizer GetLocalizer(Type resourceType)
{ {
localizer = _stringLocalizerFactory.Create(resourceType); return GetStringLocalizer(resourceType);
} }
return localizer; private IStringLocalizer GetStringLocalizer(Type resourceType)
{
return resourceType == null ? null : _stringLocalizerFactory.Create(resourceType);
}
private Type GetResourceTypeFromModelExplorer(ModelExplorer explorer)
{
var assembly = explorer.Container.ModelType.Assembly;
return _options.AssemblyResources.GetOrDefault(assembly);
} }
} }
} }

7
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormControlSize.cs

@ -1,9 +1,4 @@
using System; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{ {
public enum AbpFormControlSize public enum AbpFormControlSize
{ {

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

@ -1,14 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Mvc.TagHelpers; using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelper.cs

@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.AspNetCore.Razor.TagHelpers;

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs

@ -7,8 +7,6 @@ using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Localization;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{ {

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs

@ -1,5 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
@ -10,8 +9,6 @@ using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DisabledInput.cs

@ -1,7 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{ {

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/FormControlSize.cs

@ -1,7 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{ {

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/InputInfoText.cs

@ -1,7 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{ {

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/Placeholder.cs

@ -1,7 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{ {

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/ReadOnlyInput.cs

@ -1,7 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{ {

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/TextArea.cs

@ -1,7 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{ {

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/IAbpTagHelperLocalizer.cs

@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Mvc.ViewFeatures; using System;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -9,5 +11,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers
string GetLocalizedText(string text, ModelExplorer explorer); string GetLocalizedText(string text, ModelExplorer explorer);
IStringLocalizer GetLocalizer(ModelExplorer explorer); IStringLocalizer GetLocalizer(ModelExplorer explorer);
IStringLocalizer GetLocalizer(Assembly assembly);
IStringLocalizer GetLocalizer(Type resourceType);
} }
} }

14
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs

@ -13,13 +13,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination
{ {
private readonly IHtmlGenerator _generator; private readonly IHtmlGenerator _generator;
private readonly HtmlEncoder _encoder; private readonly HtmlEncoder _encoder;
private readonly IStringLocalizer<AbpUiResource> _localizer; private readonly IAbpTagHelperLocalizer _tagHelperLocalizer;
public AbpPaginationTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IStringLocalizer<AbpUiResource> localizer) public AbpPaginationTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IAbpTagHelperLocalizer tagHelperLocalizer)
{ {
_generator = generator; _generator = generator;
_encoder = encoder; _encoder = encoder;
_localizer = localizer; _tagHelperLocalizer = tagHelperLocalizer;
} }
public override void Process(TagHelperContext context, TagHelperOutput output) public override void Process(TagHelperContext context, TagHelperOutput output)
@ -117,11 +117,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination
protected virtual string RenderAnchorTagHelperLinkHtml(TagHelperContext context, TagHelperOutput output, string currentPage, string localizationKey) protected virtual string RenderAnchorTagHelperLinkHtml(TagHelperContext context, TagHelperOutput output, string currentPage, string localizationKey)
{ {
var localizer = _tagHelperLocalizer.GetLocalizer(typeof(AbpUiResource));
var anchorTagHelper = GetAnchorTagHelper(currentPage, out var attributeList); var anchorTagHelper = GetAnchorTagHelper(currentPage, out var attributeList);
var tagHelperOutput = GetInnerTagHelper(attributeList, context, anchorTagHelper, "a", TagMode.StartTagAndEndTag); var tagHelperOutput = GetInnerTagHelper(attributeList, context, anchorTagHelper, "a", TagMode.StartTagAndEndTag);
tagHelperOutput.Content.SetHtmlContent(_localizer[localizationKey]); tagHelperOutput.Content.SetHtmlContent(localizer[localizationKey]);
var renderedHtml = RenderTagHelperOutput(tagHelperOutput, _encoder); var renderedHtml = RenderTagHelperOutput(tagHelperOutput, _encoder);
@ -150,8 +152,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination
protected virtual string GetOpeningTags(TagHelperContext context, TagHelperOutput output) protected virtual string GetOpeningTags(TagHelperContext context, TagHelperOutput output)
{ {
var localizer = _tagHelperLocalizer.GetLocalizer(typeof(AbpUiResource));
var pagerInfo = (TagHelper.ShowInfo ?? false) ? var pagerInfo = (TagHelper.ShowInfo ?? false) ?
" <div class=\"col-sm-12 col-md-5\"> " + _localizer["PagerInfo", TagHelper.Model.ShowingFrom, TagHelper.Model.ShowingTo, TagHelper.Model.TotalItemsCount] + "</div>\r\n" " <div class=\"col-sm-12 col-md-5\"> " + localizer["PagerInfo", TagHelper.Model.ShowingFrom, TagHelper.Model.ShowingTo, TagHelper.Model.TotalItemsCount] + "</div>\r\n"
: ""; : "";
return return

38
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollection.cs

@ -1,8 +1,8 @@
using System; using JetBrains.Annotations;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using JetBrains.Annotations;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
@ -18,6 +18,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
public void Add(BundleContributor contributor) public void Add(BundleContributor contributor)
{ {
foreach (var dependedType in GetDirectDependencies(contributor.GetType()))
{
AddWithDependencies(dependedType);
}
_contributors.Add(contributor); _contributors.Add(contributor);
} }
@ -39,11 +44,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
return _contributors.ToImmutableList(); return _contributors.ToImmutableList();
} }
private bool IsAlreadyAdded(Type contributorType)
{
return _contributors.Any(c => c.GetType() == contributorType);
}
private void AddWithDependencies(Type contributorType) private void AddWithDependencies(Type contributorType)
{ {
if (IsAlreadyAdded(contributorType)) if (IsAlreadyAdded(contributorType))
@ -51,20 +51,28 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
return; return;
} }
foreach (var dependedType in GetDirectDependencies(contributorType))
{
AddWithDependencies(dependedType); //Recursive call
}
AddInstanceToContributors(contributorType);
}
private IEnumerable<Type> GetDirectDependencies(Type contributorType)
{
var dependsOnAttributes = contributorType var dependsOnAttributes = contributorType
.GetCustomAttributes(true) .GetCustomAttributes(true)
.OfType<IDependedTypesProvider>() .OfType<IDependedTypesProvider>()
.ToList(); .ToList();
foreach (var dependsOnAttribute in dependsOnAttributes) return dependsOnAttributes
{ .SelectMany(a => a.GetDependedTypes());
foreach (var dependedType in dependsOnAttribute.GetDependedTypes()) }
{
AddWithDependencies(dependedType); //Recursive call
}
}
AddInstanceToContributors(contributorType); private bool IsAlreadyAdded(Type contributorType)
{
return _contributors.Any(c => c.GetType() == contributorType);
} }
private void AddInstanceToContributors(Type contributorType) private void AddInstanceToContributors(Type contributorType)

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

@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
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;
@ -46,7 +45,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic
{ {
bundle bundle
.AddBaseBundles(StandardBundles.Styles.Global) .AddBaseBundles(StandardBundles.Styles.Global)
.AddContributors(new BasicThemeGlobalStyleContributor()); .AddContributors(typeof(BasicThemeGlobalStyleContributor));
}); });
options options

36
framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Builder/AbpAspNetCoreMvcApplicationBuilderExtensions.cs

@ -0,0 +1,36 @@
using System;
using Microsoft.AspNetCore.Routing;
namespace Microsoft.AspNetCore.Builder
{
public static class AbpAspNetCoreMvcApplicationBuilderExtensions
{
/// <summary>
/// Adds MVC to the <see cref="T:Microsoft.AspNetCore.Builder.IApplicationBuilder" /> request execution pipeline
/// with the following default routes:
///
/// - a default route named 'defaultWithArea' and the following template: '{area}/{controller=Home}/{action=Index}/{id?}'.
/// - a default route named 'default' and the following template: '{controller=Home}/{action=Index}/{id?}'.
/// </summary>
/// <param name="app">The <see cref="T:Microsoft.AspNetCore.Builder.IApplicationBuilder" />.</param>
/// <param name="additionalConfigurationAction">Additional action to configure routes</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IApplicationBuilder UseMvcWithDefaultRouteAndArea(
this IApplicationBuilder app,
Action<IRouteBuilder> additionalConfigurationAction = null)
{
return app.UseMvc(routes =>
{
routes.MapRoute(
name: "defaultWithArea",
template: "{area}/{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
additionalConfigurationAction?.Invoke(routes);
});
}
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs

@ -63,14 +63,6 @@ namespace Volo.Abp.AspNetCore.Mvc
options.IgnoredInterfaces.AddIfNotContains(typeof(IActionFilter)); options.IgnoredInterfaces.AddIfNotContains(typeof(IActionFilter));
}); });
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(AbpAspNetCoreMvcModule).Assembly, o =>
{
o.RootPath = "abp";
});
});
var mvcCoreBuilder = context.Services.AddMvcCore(); var mvcCoreBuilder = context.Services.AddMvcCore();
context.Services.ExecutePreConfiguredActions(mvcCoreBuilder); context.Services.ExecutePreConfiguredActions(mvcCoreBuilder);

5
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalControllerFeatureProvider.cs

@ -17,6 +17,11 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
protected override bool IsController(TypeInfo typeInfo) protected override bool IsController(TypeInfo typeInfo)
{ {
//TODO: Move this to a lazy loaded field for efficiency. //TODO: Move this to a lazy loaded field for efficiency.
if (_application.ServiceProvider == null)
{
return false;
}
var configuration = _application.ServiceProvider var configuration = _application.ServiceProvider
.GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>().Value .GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>().Value
.ConventionalControllers .ConventionalControllers

39
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs

@ -1,5 +1,8 @@
using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System;
using Volo.Abp.Settings;
namespace Volo.Abp.AspNetCore.Mvc.Localization namespace Volo.Abp.AspNetCore.Mvc.Localization
{ {
@ -8,12 +11,40 @@ namespace Volo.Abp.AspNetCore.Mvc.Localization
public class AbpLanguagesController : AbpController public class AbpLanguagesController : AbpController
{ {
[HttpGet] [HttpGet]
public IActionResult Switch(string culture, string uiCulture = "") //TODO: Implement return URL public IActionResult Switch(string culture, string uiCulture = "", string returnUrl = "")
{ {
//TODO: Check allowed languages and so on... if (!GlobalizationHelper.IsValidCultureCode(culture))
{
throw new AbpException("Unknown language: " + culture + ". It must be a valid culture!");
}
Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, $"c={culture}|uic={uiCulture ?? culture}"); string cookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture, uiCulture));
Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, cookieValue, new CookieOptions
{
Expires = Clock.Now.AddYears(2)
});
if (!string.IsNullOrWhiteSpace(returnUrl))
{
return Redirect(GetRedirectUrl(returnUrl));
}
return Redirect("/"); return Redirect("/");
} }
private string GetRedirectUrl(string returnUrl)
{
if (returnUrl.IsNullOrEmpty())
{
return "/";
}
if (Url.IsLocalUrl(returnUrl))
{
return returnUrl;
}
return "/";
}
} }
} }

26
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/GlobalizationHelper.cs

@ -0,0 +1,26 @@
using System;
using System.Globalization;
namespace Volo.Abp.AspNetCore.Mvc.Localization
{
internal static class GlobalizationHelper
{
public static bool IsValidCultureCode(string cultureCode)
{
if (cultureCode.IsNullOrWhiteSpace())
{
return false;
}
try
{
CultureInfo.GetCultureInfo(cultureCode);
return true;
}
catch (CultureNotFoundException)
{
return false;
}
}
}
}

2
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Modularity/ApplicationInitializationContextExtensions.cs → framework/src/Volo.Abp.AspNetCore/Volo/Abp/ApplicationInitializationContextExtensions.cs

@ -4,7 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Modularity namespace Volo.Abp
{ {
public static class ApplicationInitializationContextExtensions public static class ApplicationInitializationContextExtensions
{ {

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

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Serialization; using Volo.Abp.Serialization;
@ -17,6 +18,11 @@ namespace Volo.Abp.Caching
context.Services.AddDistributedMemoryCache(); context.Services.AddDistributedMemoryCache();
context.Services.AddSingleton(typeof(IDistributedCache<>), typeof(DistributedCache<>)); context.Services.AddSingleton(typeof(IDistributedCache<>), typeof(DistributedCache<>));
context.Services.Configure<CacheOptions>(cacheOptions =>
{
cacheOptions.GlobalCacheEntryOptions.SlidingExpiration = TimeSpan.FromMinutes(20);
});
} }
} }
} }

1
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheNameAttribute.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Volo.Abp.Caching namespace Volo.Abp.Caching

24
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheOptions.cs

@ -0,0 +1,24 @@
using Microsoft.Extensions.Caching.Distributed;
using System;
using System.Collections.Generic;
namespace Volo.Abp.Caching
{
public class CacheOptions
{
/// <summary>
/// Global Cache entry options.
/// </summary>
public DistributedCacheEntryOptions GlobalCacheEntryOptions { get; set; }
/// <summary>
/// List of all cache configurators.
/// (func argument:Name of cache)
/// </summary>
public List<Func<string, DistributedCacheEntryOptions>> CacheConfigurators { get; set; } //TODO list item use a configurator interface instead?
public CacheOptions()
{
CacheConfigurators = new List<Func<string, DistributedCacheEntryOptions>>();
GlobalCacheEntryOptions = new DistributedCacheEntryOptions();
}
}
}

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

@ -3,6 +3,9 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Nito.AsyncEx; using Nito.AsyncEx;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Serialization; using Volo.Abp.Serialization;
@ -13,6 +16,8 @@ namespace Volo.Abp.Caching
public class DistributedCache<TCacheItem> : IDistributedCache<TCacheItem> public class DistributedCache<TCacheItem> : IDistributedCache<TCacheItem>
where TCacheItem : class where TCacheItem : class
{ {
public ILogger<DistributedCache<TCacheItem>> Logger { get; set; }
protected string CacheName { get; set; } protected string CacheName { get; set; }
protected bool IgnoreMultiTenancy { get; set; } protected bool IgnoreMultiTenancy { get; set; }
@ -27,23 +32,52 @@ namespace Volo.Abp.Caching
protected AsyncLock AsyncLock { get; } = new AsyncLock(); protected AsyncLock AsyncLock { get; } = new AsyncLock();
protected DistributedCacheEntryOptions DefaultCacheOptions;
private readonly CacheOptions _cacheOption;
private readonly DistributedCacheOptions _distributedCacheOption;
public DistributedCache( public DistributedCache(
IOptions<CacheOptions> cacheOption,
IOptions<DistributedCacheOptions> distributedCacheOption,
IDistributedCache cache, IDistributedCache cache,
ICancellationTokenProvider cancellationTokenProvider, ICancellationTokenProvider cancellationTokenProvider,
IObjectSerializer objectSerializer, IObjectSerializer objectSerializer,
ICurrentTenant currentTenant) ICurrentTenant currentTenant)
{ {
_distributedCacheOption = distributedCacheOption.Value;
_cacheOption = cacheOption.Value;
Cache = cache; Cache = cache;
CancellationTokenProvider = cancellationTokenProvider; CancellationTokenProvider = cancellationTokenProvider;
Logger = NullLogger<DistributedCache<TCacheItem>>.Instance;
ObjectSerializer = objectSerializer; ObjectSerializer = objectSerializer;
CurrentTenant = currentTenant; CurrentTenant = currentTenant;
SetDefaultOptions(); SetDefaultOptions();
} }
public virtual TCacheItem Get(string key) public virtual TCacheItem Get(string key, bool? hideErrors = null)
{ {
var cachedBytes = Cache.Get(NormalizeKey(key)); hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[] cachedBytes;
try
{
cachedBytes = Cache.Get(NormalizeKey(key));
}
catch (Exception ex)
{
if ((bool) hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return null;
}
throw;
}
if (cachedBytes == null) if (cachedBytes == null)
{ {
return null; return null;
@ -52,9 +86,26 @@ namespace Volo.Abp.Caching
return ObjectSerializer.Deserialize<TCacheItem>(cachedBytes); return ObjectSerializer.Deserialize<TCacheItem>(cachedBytes);
} }
public virtual async Task<TCacheItem> GetAsync(string key, CancellationToken token = default) public virtual async Task<TCacheItem> GetAsync(string key, bool? hideErrors = null, CancellationToken token = default)
{ {
var cachedBytes = await Cache.GetAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token)); hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[] cachedBytes;
try
{
cachedBytes = await Cache.GetAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
}
catch (Exception ex)
{
if ((bool)hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return null;
}
throw;
}
if (cachedBytes == null) if (cachedBytes == null)
{ {
return null; return null;
@ -64,11 +115,12 @@ namespace Volo.Abp.Caching
} }
public TCacheItem GetOrAdd( public TCacheItem GetOrAdd(
string key, string key,
Func<TCacheItem> factory, Func<TCacheItem> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null) Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null)
{ {
var value = Get(key); var value = Get(key, hideErrors);
if (value != null) if (value != null)
{ {
return value; return value;
@ -76,26 +128,28 @@ namespace Volo.Abp.Caching
using (AsyncLock.Lock()) using (AsyncLock.Lock())
{ {
value = Get(key); value = Get(key, hideErrors);
if (value != null) if (value != null)
{ {
return value; return value;
} }
value = factory(); value = factory();
Set(key, value, optionsFactory?.Invoke()); Set(key, value, optionsFactory?.Invoke(), hideErrors);
} }
return value; return value;
} }
public async Task<TCacheItem> GetOrAddAsync( public async Task<TCacheItem> GetOrAddAsync(
string key, string key,
Func<Task<TCacheItem>> factory, Func<Task<TCacheItem>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null, Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
CancellationToken token = default) CancellationToken token = default)
{ {
var value = await GetAsync(key, token); token = CancellationTokenProvider.FallbackToProvider(token);
var value = await GetAsync(key, hideErrors, token);
if (value != null) if (value != null)
{ {
return value; return value;
@ -103,56 +157,142 @@ namespace Volo.Abp.Caching
using (await AsyncLock.LockAsync(token)) using (await AsyncLock.LockAsync(token))
{ {
value = await GetAsync(key, token); value = await GetAsync(key, hideErrors, token);
if (value != null) if (value != null)
{ {
return value; return value;
} }
value = await factory(); value = await factory();
await SetAsync(key, value, optionsFactory?.Invoke(), token); await SetAsync(key, value, optionsFactory?.Invoke(), hideErrors, token);
} }
return value; return value;
} }
public virtual void Set(string key, TCacheItem value, DistributedCacheEntryOptions options = null) public virtual void Set(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null)
{ {
Cache.Set( hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
NormalizeKey(key),
ObjectSerializer.Serialize(value), try
options ?? new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(20) } //TODO: implement per cache item and global defaults!!! {
); Cache.Set(
NormalizeKey(key),
ObjectSerializer.Serialize(value),
options ?? DefaultCacheOptions
);
}
catch (Exception ex)
{
if ((bool) hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return;
}
throw;
}
} }
public virtual Task SetAsync(string key, TCacheItem value, DistributedCacheEntryOptions options = null, CancellationToken token = default) public virtual Task SetAsync(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null, CancellationToken token = default)
{ {
return Cache.SetAsync( hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
NormalizeKey(key),
ObjectSerializer.Serialize(value), try
options ?? new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(20) }, //TODO: implement per cache item and global defaults!!! {
CancellationTokenProvider.FallbackToProvider(token) return Cache.SetAsync(
); NormalizeKey(key),
ObjectSerializer.Serialize(value),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if ((bool)hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return Task.CompletedTask;
}
throw;
}
} }
public virtual void Refresh(string key) public virtual void Refresh(string key, bool? hideErrors = null)
{ {
Cache.Refresh(NormalizeKey(key)); hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
Cache.Refresh(NormalizeKey(key));
}
catch (Exception ex)
{
if ((bool) hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return;
}
throw;
}
} }
public virtual Task RefreshAsync(string key, CancellationToken token = default) public virtual Task RefreshAsync(string key, bool? hideErrors = null, CancellationToken token = default)
{ {
return Cache.RefreshAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token)); hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
return Cache.RefreshAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
}
catch (Exception ex)
{
if ((bool)hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return Task.CompletedTask;
}
throw;
}
} }
public virtual void Remove(string key) public virtual void Remove(string key, bool? hideErrors = null)
{ {
Cache.Remove(NormalizeKey(key)); hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
Cache.Remove(NormalizeKey(key));
}
catch (Exception ex)
{
if ((bool) hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
}
throw;
}
} }
public virtual Task RemoveAsync(string key, CancellationToken token = default) public virtual Task RemoveAsync(string key, bool? hideErrors = null, CancellationToken token = default)
{ {
return Cache.RemoveAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token)); hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
return Cache.RemoveAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
}
catch (Exception ex)
{
if ((bool)hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return Task.CompletedTask;
}
throw;
}
} }
protected virtual string NormalizeKey(string key) protected virtual string NormalizeKey(string key)
@ -167,6 +307,19 @@ namespace Volo.Abp.Caching
return normalizedKey; return normalizedKey;
} }
protected virtual DistributedCacheEntryOptions GetDefaultCacheEntryOptions()
{
foreach (var configure in _cacheOption.CacheConfigurators)
{
var options = configure.Invoke(CacheName);
if (options != null)
{
return options;
}
}
return _cacheOption.GlobalCacheEntryOptions;
}
protected virtual void SetDefaultOptions() protected virtual void SetDefaultOptions()
{ {
//CacheName //CacheName
@ -175,10 +328,13 @@ namespace Volo.Abp.Caching
.OfType<CacheNameAttribute>() .OfType<CacheNameAttribute>()
.FirstOrDefault(); .FirstOrDefault();
CacheName = cacheNameAttribute != null ? cacheNameAttribute.Name : typeof(TCacheItem).Name; CacheName = cacheNameAttribute != null ? cacheNameAttribute.Name : typeof(TCacheItem).FullName;
//IgnoreMultiTenancy //IgnoreMultiTenancy
IgnoreMultiTenancy = typeof(TCacheItem).IsDefined(typeof(IgnoreMultiTenancyAttribute), true); IgnoreMultiTenancy = typeof(TCacheItem).IsDefined(typeof(IgnoreMultiTenancyAttribute), true);
//Configure default cache entry options
DefaultCacheOptions = GetDefaultCacheEntryOptions();
} }
} }
} }

10
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCacheOptions.cs

@ -0,0 +1,10 @@
namespace Volo.Abp.Caching
{
public class DistributedCacheOptions
{
/// <summary>
/// Throw or hide exceptions for the distributed cache.
/// </summary>
public bool HideErrors { get; set; } = true;
}
}

20
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs

@ -10,55 +10,65 @@ namespace Volo.Abp.Caching
where TCacheItem : class where TCacheItem : class
{ {
TCacheItem Get( TCacheItem Get(
string key string key,
bool? hideErrors = null
); );
Task<TCacheItem> GetAsync( Task<TCacheItem> GetAsync(
[NotNull] string key, [NotNull] string key,
bool? hideErrors = null,
CancellationToken token = default CancellationToken token = default
); );
TCacheItem GetOrAdd( TCacheItem GetOrAdd(
string key, string key,
Func<TCacheItem> factory, Func<TCacheItem> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null
); );
Task<TCacheItem> GetOrAddAsync( Task<TCacheItem> GetOrAddAsync(
[NotNull] string key, [NotNull] string key,
Func<Task<TCacheItem>> factory, Func<Task<TCacheItem>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null, Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
CancellationToken token = default CancellationToken token = default
); );
void Set( void Set(
string key, string key,
TCacheItem value, TCacheItem value,
DistributedCacheEntryOptions options = null DistributedCacheEntryOptions options = null,
bool? hideErrors = null
); );
Task SetAsync( Task SetAsync(
[NotNull] string key, [NotNull] string key,
[NotNull] TCacheItem value, [NotNull] TCacheItem value,
[CanBeNull] DistributedCacheEntryOptions options = null, [CanBeNull] DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
CancellationToken token = default CancellationToken token = default
); );
void Refresh( void Refresh(
string key string key,
bool? hideErrors = null
); );
Task RefreshAsync( Task RefreshAsync(
string key, string key,
bool? hideErrors = null,
CancellationToken token = default CancellationToken token = default
); );
void Remove( void Remove(
string key string key,
bool? hideErrors = null
); );
Task RemoveAsync( Task RemoveAsync(
string key, string key,
bool? hideErrors = null,
CancellationToken token = default CancellationToken token = default
); );
} }

10
framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionCommonExtensions.cs

@ -8,6 +8,16 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
public static class ServiceCollectionCommonExtensions public static class ServiceCollectionCommonExtensions
{ {
public static bool IsAdded<T>(this IServiceCollection services)
{
return services.IsAdded(typeof(T));
}
public static bool IsAdded(this IServiceCollection services, Type type)
{
return services.Any(d => d.ServiceType == type);
}
public static T GetSingletonInstanceOrNull<T>(this IServiceCollection services) public static T GetSingletonInstanceOrNull<T>(this IServiceCollection services)
{ {
return (T)services return (T)services

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

@ -14,7 +14,7 @@
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" /> <PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" /> <PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.9" /> <PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.9.2" />
<PackageReference Include="System.Linq.Queryable" Version="4.3.0" /> <PackageReference Include="System.Linq.Queryable" Version="4.3.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Localization" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" />
<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.2.1" /> <PackageReference Include="JetBrains.Annotations" Version="2018.3.0" />
<PackageReference Include="Nito.AsyncEx.Coordination" Version="1.0.2" /> <PackageReference Include="Nito.AsyncEx.Coordination" Version="1.0.2" />
<PackageReference Include="Nito.AsyncEx.Context" Version="1.1.0" /> <PackageReference Include="Nito.AsyncEx.Context" Version="1.1.0" />
</ItemGroup> </ItemGroup>

13
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/AbpEventBusRabbitMqModule.cs

@ -1,13 +0,0 @@
using Volo.Abp.Modularity;
using Volo.Abp.RabbitMQ;
namespace Volo.Abp.EventBus.Distributed.RabbitMq
{
[DependsOn(
typeof(AbpEventBusModule),
typeof(AbpRabbitMqModule))]
public class AbpEventBusRabbitMqModule : AbpModule
{
}
}

19
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpEventBusRabbitMqModule.cs

@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.RabbitMQ;
namespace Volo.Abp.EventBus.RabbitMq
{
[DependsOn(
typeof(AbpEventBusModule),
typeof(AbpRabbitMqModule))]
public class AbpEventBusRabbitMqModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<RabbitMqEventBusOptions>(configuration.GetSection("RabbitMQ:EventBus"));
}
}
}

28
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBus.cs → framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs

@ -4,13 +4,14 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.RabbitMQ;
using RabbitMQ.Client; using RabbitMQ.Client;
using RabbitMQ.Client.Events; using RabbitMQ.Client.Events;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.RabbitMQ;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace Volo.Abp.EventBus.Distributed.RabbitMq namespace Volo.Abp.EventBus.RabbitMq
{ {
/* TODO: How to handle unsubscribe to unbind on RabbitMq (may not be possible for) /* TODO: How to handle unsubscribe to unbind on RabbitMq (may not be possible for)
* TODO: Implement Retry system * TODO: Implement Retry system
@ -20,7 +21,7 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
[ExposeServices(typeof(IDistributedEventBus), typeof(RabbitMqDistributedEventBus))] [ExposeServices(typeof(IDistributedEventBus), typeof(RabbitMqDistributedEventBus))]
public class RabbitMqDistributedEventBus : EventBusBase, IDistributedEventBus, ISingletonDependency public class RabbitMqDistributedEventBus : EventBusBase, IDistributedEventBus, ISingletonDependency
{ {
protected RabbitMqDistributedEventBusOptions RabbitMqDistributedEventBusOptions { get; } protected RabbitMqEventBusOptions RabbitMqEventBusOptions { get; }
protected DistributedEventBusOptions DistributedEventBusOptions { get; } protected DistributedEventBusOptions DistributedEventBusOptions { get; }
protected IConnectionPool ConnectionPool { get; } protected IConnectionPool ConnectionPool { get; }
protected IRabbitMqSerializer Serializer { get; } protected IRabbitMqSerializer Serializer { get; }
@ -32,7 +33,7 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
protected IRabbitMqMessageConsumer Consumer { get; } protected IRabbitMqMessageConsumer Consumer { get; }
public RabbitMqDistributedEventBus( public RabbitMqDistributedEventBus(
IOptions<RabbitMqDistributedEventBusOptions> options, IOptions<RabbitMqEventBusOptions> options,
IConnectionPool connectionPool, IConnectionPool connectionPool,
IRabbitMqSerializer serializer, IRabbitMqSerializer serializer,
IHybridServiceScopeFactory serviceScopeFactory, IHybridServiceScopeFactory serviceScopeFactory,
@ -44,24 +45,24 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
Serializer = serializer; Serializer = serializer;
MessageConsumerFactory = messageConsumerFactory; MessageConsumerFactory = messageConsumerFactory;
DistributedEventBusOptions = distributedEventBusOptions.Value; DistributedEventBusOptions = distributedEventBusOptions.Value;
RabbitMqDistributedEventBusOptions = options.Value; RabbitMqEventBusOptions = options.Value;
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>(); HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
EventTypes = new ConcurrentDictionary<string, Type>(); EventTypes = new ConcurrentDictionary<string, Type>();
Consumer = MessageConsumerFactory.Create( Consumer = MessageConsumerFactory.Create(
new ExchangeDeclareConfiguration( new ExchangeDeclareConfiguration(
RabbitMqDistributedEventBusOptions.ExchangeName, RabbitMqEventBusOptions.ExchangeName,
type: "direct", type: "direct",
durable: true durable: true
), ),
new QueueDeclareConfiguration( new QueueDeclareConfiguration(
RabbitMqDistributedEventBusOptions.ClientName, RabbitMqEventBusOptions.ClientName,
durable: true, durable: true,
exclusive: false, exclusive: false,
autoDelete: false autoDelete: false
), ),
RabbitMqDistributedEventBusOptions.ConnectionName RabbitMqEventBusOptions.ConnectionName
); );
Consumer.OnMessageReceived(ProcessEventAsync); Consumer.OnMessageReceived(ProcessEventAsync);
@ -96,8 +97,7 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
if (handlerFactories.Count == 1) //TODO: Multi-threading! if (handlerFactories.Count == 1) //TODO: Multi-threading!
{ {
var eventName = EventNameAttribute.GetNameOrDefault(eventType); Consumer.BindAsync(EventNameAttribute.GetNameOrDefault(eventType));
Consumer.BindAsync(eventName);
} }
return new EventHandlerFactoryUnregistrar(this, eventType, factory); return new EventHandlerFactoryUnregistrar(this, eventType, factory);
@ -162,10 +162,10 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
var eventName = EventNameAttribute.GetNameOrDefault(eventType); var eventName = EventNameAttribute.GetNameOrDefault(eventType);
var body = Serializer.Serialize(eventData); var body = Serializer.Serialize(eventData);
using (var channel = ConnectionPool.Get(RabbitMqDistributedEventBusOptions.ConnectionName).CreateModel()) using (var channel = ConnectionPool.Get(RabbitMqEventBusOptions.ConnectionName).CreateModel())
{ {
channel.ExchangeDeclare( channel.ExchangeDeclare(
RabbitMqDistributedEventBusOptions.ExchangeName, RabbitMqEventBusOptions.ExchangeName,
"direct", "direct",
durable: true durable: true
); );
@ -174,7 +174,7 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
properties.DeliveryMode = RabbitMqConsts.DeliveryModes.Persistent; properties.DeliveryMode = RabbitMqConsts.DeliveryModes.Persistent;
channel.BasicPublish( channel.BasicPublish(
exchange: RabbitMqDistributedEventBusOptions.ExchangeName, exchange: RabbitMqEventBusOptions.ExchangeName,
routingKey: eventName, routingKey: eventName,
mandatory: true, mandatory: true,
basicProperties: properties, basicProperties: properties,

4
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBusOptions.cs → framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqEventBusOptions.cs

@ -1,6 +1,6 @@
namespace Volo.Abp.EventBus.Distributed.RabbitMq namespace Volo.Abp.EventBus.RabbitMq
{ {
public class RabbitMqDistributedEventBusOptions public class RabbitMqEventBusOptions
{ {
public string ConnectionName { get; set; } public string ConnectionName { get; set; }

8
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IocEventHandlerFactory.cs

@ -12,12 +12,12 @@ namespace Volo.Abp.EventBus
{ {
public Type HandlerType { get; } public Type HandlerType { get; }
protected IServiceScope ServiceScope { get; } protected IHybridServiceScopeFactory ScopeFactory { get; }
public IocEventHandlerFactory(IHybridServiceScopeFactory scopeFactory, Type handlerType) public IocEventHandlerFactory(IHybridServiceScopeFactory scopeFactory, Type handlerType)
{ {
ScopeFactory = scopeFactory;
HandlerType = handlerType; HandlerType = handlerType;
ServiceScope = scopeFactory.CreateScope();
} }
/// <summary> /// <summary>
@ -26,7 +26,7 @@ namespace Volo.Abp.EventBus
/// <returns>Resolved handler object</returns> /// <returns>Resolved handler object</returns>
public IEventHandlerDisposeWrapper GetHandler() public IEventHandlerDisposeWrapper GetHandler()
{ {
var scope = ServiceScope.ServiceProvider.CreateScope(); var scope = ScopeFactory.CreateScope();
return new EventHandlerDisposeWrapper( return new EventHandlerDisposeWrapper(
(IEventHandler) scope.ServiceProvider.GetRequiredService(HandlerType), (IEventHandler) scope.ServiceProvider.GetRequiredService(HandlerType),
() => scope.Dispose() () => scope.Dispose()
@ -35,7 +35,7 @@ namespace Volo.Abp.EventBus
public void Dispose() public void Dispose()
{ {
ServiceScope.Dispose();
} }
} }
} }

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

@ -14,7 +14,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="IdentityModel" Version="3.10.4" />
<ProjectReference Include="..\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj" /> <ProjectReference Include="..\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj" />
<ProjectReference Include="..\Volo.Abp.IdentityModel\Volo.Abp.IdentityModel.csproj" /> <ProjectReference Include="..\Volo.Abp.IdentityModel\Volo.Abp.IdentityModel.csproj" />
</ItemGroup> </ItemGroup>

2
framework/src/Volo.Abp.Http.Client.IdentityModel/Volo/Abp/Http/Client/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs

@ -30,7 +30,7 @@ namespace Volo.Abp.Http.Client.IdentityModel
} }
else else
{ {
await IdentityModelHttpClientAuthenticator.Authenticate( await IdentityModelHttpClientAuthenticator.AuthenticateAsync(
new IdentityModelHttpClientAuthenticateContext( new IdentityModelHttpClientAuthenticateContext(
context.Client, context.Client,
context.RemoteService.GetIdentityClient() context.RemoteService.GetIdentityClient()

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

@ -4,6 +4,8 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy; using Volo.Abp.DynamicProxy;
@ -29,6 +31,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IRemoteServiceHttpClientAuthenticator _clientAuthenticator; private readonly IRemoteServiceHttpClientAuthenticator _clientAuthenticator;
public ILogger<DynamicHttpProxyInterceptor<TService>> Logger { get; set; }
static DynamicHttpProxyInterceptor() static DynamicHttpProxyInterceptor()
{ {
GenericInterceptAsyncMethod = typeof(DynamicHttpProxyInterceptor<TService>) GenericInterceptAsyncMethod = typeof(DynamicHttpProxyInterceptor<TService>)
@ -50,6 +54,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying
_clientAuthenticator = clientAuthenticator; _clientAuthenticator = clientAuthenticator;
_clientOptions = clientOptions.Value; _clientOptions = clientOptions.Value;
_remoteServiceOptions = remoteServiceOptions.Value; _remoteServiceOptions = remoteServiceOptions.Value;
Logger = NullLogger<DynamicHttpProxyInterceptor<TService>>.Instance;
} }
public override void Intercept(IAbpMethodInvocation invocation) public override void Intercept(IAbpMethodInvocation invocation)

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

@ -14,7 +14,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="IdentityModel" Version="3.10.4" /> <PackageReference Include="IdentityModel" Version="3.10.5" />
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" /> <ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
</ItemGroup> </ItemGroup>

2
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IIdentityModelHttpClientAuthenticator.cs

@ -4,6 +4,6 @@ namespace Volo.Abp.IdentityModel
{ {
public interface IIdentityModelHttpClientAuthenticator public interface IIdentityModelHttpClientAuthenticator
{ {
Task Authenticate(IdentityModelHttpClientAuthenticateContext context); Task AuthenticateAsync(IdentityModelHttpClientAuthenticateContext context);
} }
} }

10
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticateContext.cs

@ -1,4 +1,5 @@
using System.Net.Http; using System.Net.Http;
using JetBrains.Annotations;
namespace Volo.Abp.IdentityModel namespace Volo.Abp.IdentityModel
{ {
@ -11,16 +12,13 @@ namespace Volo.Abp.IdentityModel
/// </summary> /// </summary>
public string IdentityClientName { get; } public string IdentityClientName { get; }
/// <summary> /// <param name="client"><see cref="HttpClient"/> object to be authenticated</param>
///
/// </summary>
/// <param name="client"><see cref="HttpClient"/> object to be authorized</param>
/// <param name="identityClientName">The identity client name configured with the <see cref="IdentityClientOptions"/>.</param> /// <param name="identityClientName">The identity client name configured with the <see cref="IdentityClientOptions"/>.</param>
public IdentityModelHttpClientAuthenticateContext( public IdentityModelHttpClientAuthenticateContext(
HttpClient client, [NotNull] HttpClient client,
string identityClientName = null) string identityClientName = null)
{ {
Client = client; Client = Check.NotNull(client, nameof(client));
IdentityClientName = identityClientName; IdentityClientName = identityClientName;
} }
} }

27
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticatorExtensions.cs

@ -0,0 +1,27 @@
using System.Net.Http;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.IdentityModel
{
public static class IdentityModelHttpClientAuthenticatorExtensions
{
/// <param name="authenticator">Authenticator object</param>
/// <param name="client"><see cref="HttpClient"/> object to be authenticated</param>
/// <param name="identityClientName">The identity client name configured with the <see cref="IdentityClientOptions"/>.</param>
public static Task AuthenticateAsync(
[NotNull] this IIdentityModelHttpClientAuthenticator authenticator,
[NotNull] HttpClient client,
string identityClientName = null)
{
Check.NotNull(authenticator, nameof(authenticator));
return authenticator.AuthenticateAsync(
new IdentityModelHttpClientAuthenticateContext(
client,
identityClientName
)
);
}
}
}

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

@ -4,6 +4,8 @@ using System.Net.Http.Headers;
using System.Threading.Tasks; using System.Threading.Tasks;
using IdentityModel; using IdentityModel;
using IdentityModel.Client; using IdentityModel.Client;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -12,15 +14,18 @@ namespace Volo.Abp.IdentityModel
[Dependency(ReplaceServices = true)] [Dependency(ReplaceServices = true)]
public class IdentityModelHttpClientAuthenticator : IIdentityModelHttpClientAuthenticator, ITransientDependency public class IdentityModelHttpClientAuthenticator : IIdentityModelHttpClientAuthenticator, ITransientDependency
{ {
protected IdentityClientOptions ClientOptions { get; } public ILogger<IdentityModelHttpClientAuthenticator> Logger { get; set; }
protected IdentityClientOptions ClientOptions { get; }
public IdentityModelHttpClientAuthenticator( public IdentityModelHttpClientAuthenticator(
IOptions<IdentityClientOptions> options) IOptions<IdentityClientOptions> options)
{ {
ClientOptions = options.Value; ClientOptions = options.Value;
Logger = NullLogger<IdentityModelHttpClientAuthenticator>.Instance;
} }
public async Task Authenticate(IdentityModelHttpClientAuthenticateContext context) public async Task AuthenticateAsync(IdentityModelHttpClientAuthenticateContext context)
{ {
var accessToken = await GetAccessTokenFromServerOrNullAsync(context); var accessToken = await GetAccessTokenFromServerOrNullAsync(context);
@ -34,22 +39,27 @@ namespace Volo.Abp.IdentityModel
protected virtual async Task<string> GetAccessTokenFromServerOrNullAsync(IdentityModelHttpClientAuthenticateContext context) protected virtual async Task<string> GetAccessTokenFromServerOrNullAsync(IdentityModelHttpClientAuthenticateContext context)
{ {
//TODO: Better logging
var configuration = GetClientConfiguration(context); var configuration = GetClientConfiguration(context);
if (configuration == null) if (configuration == null)
{ {
Logger.LogWarning($"Could not find {nameof(IdentityClientConfiguration)} for {context.IdentityClientName}. Either define a configuration for {context.IdentityClientName} or set a default configuration.");
return null; return null;
} }
var discoveryResponse = await GetDiscoveryResponse(configuration); var discoveryResponse = await GetDiscoveryResponse(configuration);
if (discoveryResponse.IsError) if (discoveryResponse.IsError)
{ {
Logger.LogError($"Could not retrieve the OpenId Connect discovery document! ErrorType: {discoveryResponse.ErrorType}. Error: {discoveryResponse.Error}");
return null; return null;
} }
var tokenResponse = await GetTokenResponse(discoveryResponse, configuration); var tokenResponse = await GetTokenResponse(discoveryResponse, configuration);
if (tokenResponse.IsError) if (tokenResponse.IsError)
{ {
Logger.LogError($"Could not get token from the OpenId Connect server! ErrorType: {tokenResponse.ErrorType}. Error: {tokenResponse.Error}. ErrorDescription: {tokenResponse.ErrorDescription}. HttpStatusCode: {tokenResponse.HttpStatusCode}");
return null; return null;
} }

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

@ -18,7 +18,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" /> <ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" />
</ItemGroup> </ItemGroup>

5
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs

@ -10,7 +10,10 @@ namespace Volo.Abp.MongoDB
{ {
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
context.Services.TryAddTransient(typeof(IMongoDbContextProvider<>), typeof(UnitOfWorkMongoDbContextProvider<>)); context.Services.TryAddTransient(
typeof(IMongoDbContextProvider<>),
typeof(UnitOfWorkMongoDbContextProvider<>)
);
} }
} }
} }

6
framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqModule.cs

@ -11,6 +11,12 @@ namespace Volo.Abp.RabbitMQ
)] )]
public class AbpRabbitMqModule : AbpModule public class AbpRabbitMqModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpRabbitMqOptions>(configuration.GetSection("RabbitMQ"));
}
public override void OnApplicationShutdown(ApplicationShutdownContext context) public override void OnApplicationShutdown(ApplicationShutdownContext context)
{ {
context.ServiceProvider context.ServiceProvider

4
framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqOptions.cs

@ -2,11 +2,11 @@
{ {
public class AbpRabbitMqOptions public class AbpRabbitMqOptions
{ {
public RabbitMqConnections ConnectionFactories { get; } public RabbitMqConnections Connections { get; }
public AbpRabbitMqOptions() public AbpRabbitMqOptions()
{ {
ConnectionFactories = new RabbitMqConnections(); Connections = new RabbitMqConnections();
} }
} }
} }

2
framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs

@ -28,7 +28,7 @@ namespace Volo.Abp.RabbitMQ
return Connections.GetOrAdd( return Connections.GetOrAdd(
connectionName, connectionName,
() => Options () => Options
.ConnectionFactories .Connections
.GetOrDefault(connectionName) .GetOrDefault(connectionName)
.CreateConnection() .CreateConnection()
); );

2
framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetListExtensions.cs

@ -21,7 +21,7 @@ namespace Volo.Abp.VirtualFileSystem
); );
} }
public static void ReplaceEmbeddedByPyhsical<T>([NotNull] this VirtualFileSetList list, [NotNull] string pyhsicalPath) public static void ReplaceEmbeddedByPhysical<T>([NotNull] this VirtualFileSetList list, [NotNull] string pyhsicalPath)
{ {
Check.NotNull(list, nameof(list)); Check.NotNull(list, nameof(list));
Check.NotNull(pyhsicalPath, nameof(pyhsicalPath)); Check.NotNull(pyhsicalPath, nameof(pyhsicalPath));

1
framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs

@ -2,7 +2,6 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.MultiTenancy; using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.TestBase; using Volo.Abp.AspNetCore.TestBase;
using Volo.Abp.Json; using Volo.Abp.Json;

1
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj

@ -21,7 +21,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
</ItemGroup> </ItemGroup>

1
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs

@ -2,7 +2,6 @@
using Localization.Resources.AbpUi; using Localization.Resources.AbpUi;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.Mvc.Authorization; using Volo.Abp.AspNetCore.Mvc.Authorization;
using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.Localization.Resource; using Volo.Abp.AspNetCore.Mvc.Localization.Resource;

1
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/AbpAspNetCoreMvcUiBootstrapDemoModule.cs

@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic;
using Volo.Abp.Autofac; using Volo.Abp.Autofac;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;

2
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml

@ -66,7 +66,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components
&lt;/div&gt; &lt;/div&gt;
&lt;div class=&quot;col-sm-12 col-md-7&quot;&gt; &lt;div class=&quot;col-sm-12 col-md-7&quot;&gt;
&lt;nav aria-label=&quot;Page navigation&quot;&gt; &lt;nav aria-label=&quot;Page navigation&quot;&gt;
&lt;ul class=&quot;pagination justify-localizationKey-end&quot;&gt; &lt;ul class=&quot;pagination justify-content-end&quot;&gt;
&lt;li class=&quot;page-item &quot;&gt; &lt;li class=&quot;page-item &quot;&gt;
&lt;a tabindex=&quot;-1&quot; class=&quot;page-link&quot; href=&quot;/Components/Paginator?currentPage=7&quot;&gt;Previous&lt;/a&gt; &lt;a tabindex=&quot;-1&quot; class=&quot;page-link&quot; href=&quot;/Components/Paginator?currentPage=7&quot;&gt;Previous&lt;/a&gt;
&lt;/li&gt; &lt;/li&gt;

1
framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs

@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.TestBase; using Volo.Abp.AspNetCore.TestBase;
using Volo.Abp.Autofac; using Volo.Abp.Autofac;
using Volo.Abp.Http.Client; using Volo.Abp.Http.Client;

22
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/AbpCachingTestModule.cs

@ -1,10 +1,30 @@
using Volo.Abp.Modularity; using Microsoft.Extensions.Caching.Distributed;
using System;
using Volo.Abp.Modularity;
namespace Volo.Abp.Caching namespace Volo.Abp.Caching
{ {
[DependsOn(typeof(AbpCachingModule))] [DependsOn(typeof(AbpCachingModule))]
public class AbpCachingTestModule : AbpModule public class AbpCachingTestModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<CacheOptions>(option =>
{
option.CacheConfigurators.Add(cacheName =>
{
if (cacheName == typeof(Sail.Testing.Caching.PersonCacheItem).FullName)
{
return new DistributedCacheEntryOptions()
{
AbsoluteExpiration = DateTime.Parse("2099-01-01 12:00:00")
};
}
return null;
});
option.GlobalCacheEntryOptions.SetSlidingExpiration(TimeSpan.FromMinutes(20));
});
}
} }
} }

51
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_ConfigureOptions_Test.cs

@ -0,0 +1,51 @@
using Microsoft.Extensions.Caching.Distributed;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace Volo.Abp.Caching
{
public class DistributedCache_ConfigureOptions_Test : AbpIntegratedTest<AbpCachingTestModule>
{
[Fact]
public async Task Configure_CacheOptions()
{
var personCache = GetRequiredService<IDistributedCache<Sail.Testing.Caching.PersonCacheItem>>();
var cacheKey = Guid.NewGuid().ToString();
//Get (not exists yet)
var cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
GetDefaultCachingOptions(personCache).SlidingExpiration.ShouldBeNull();
GetDefaultCachingOptions(personCache).AbsoluteExpiration.ShouldBe(new DateTime(2099, 1, 1, 12, 0, 0));
}
[Fact]
public async Task Default_CacheOptions_Should_Be_20_Mins()
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheKey = Guid.NewGuid().ToString();
//Get (not exists yet)
var cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
GetDefaultCachingOptions(personCache).SlidingExpiration.ShouldBe(TimeSpan.FromMinutes(20));
}
private static DistributedCacheEntryOptions GetDefaultCachingOptions(object instance)
{
var defaultOptionsField = instance.GetType().GetTypeInfo().GetField("DefaultCacheOptions", BindingFlags.Instance | BindingFlags.NonPublic);
return (DistributedCacheEntryOptions)defaultOptionsField.GetValue(instance);
}
}
}

52
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs

@ -72,5 +72,55 @@ namespace Volo.Abp.Caching
factoryExecuted.ShouldBeFalse(); factoryExecuted.ShouldBeFalse();
cacheItem.Name.ShouldBe(personName); cacheItem.Name.ShouldBe(personName);
} }
[Fact]
public async Task SameClassName_But_DiffNamespace_Should_Not_Use_Same_Cache()
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var otherPersonCache = GetRequiredService<IDistributedCache<Sail.Testing.Caching.PersonCacheItem>>();
var cacheKey = Guid.NewGuid().ToString();
const string personName = "john nash";
//Get (not exists yet)
var cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
var cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldBeNull();
//Set
cacheItem = new PersonCacheItem(personName);
await personCache.SetAsync(cacheKey, cacheItem);
//Get (it should be available now, but otherPersonCache not exists now.
cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldNotBeNull();
cacheItem.Name.ShouldBe(personName);
cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldBeNull();
//set other person cache
cacheItem1 = new Sail.Testing.Caching.PersonCacheItem(personName);
await otherPersonCache.SetAsync(cacheKey, cacheItem1);
cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldNotBeNull();
cacheItem1.Name.ShouldBe(personName);
//Remove
await personCache.RemoveAsync(cacheKey);
//Get (not exists since removed)
cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldNotBeNull();
}
} }
} }

20
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/PersonCacheItem.cs

@ -9,7 +9,25 @@ namespace Volo.Abp.Caching
private PersonCacheItem() private PersonCacheItem()
{ {
}
public PersonCacheItem(string name)
{
Name = name;
}
}
}
namespace Sail.Testing.Caching
{
[Serializable]
public class PersonCacheItem
{
public string Name { get; private set; }
private PersonCacheItem()
{
} }
public PersonCacheItem(string name) public PersonCacheItem(string name)

7
modules/account/Volo.Abp.Account.sln

@ -7,6 +7,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B5881429-EFF
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Account.Web", "src\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj", "{FCAC4354-7B13-4A91-A2F4-04D00F253C91}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Account.Web", "src\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj", "{FCAC4354-7B13-4A91-A2F4-04D00F253C91}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Account.Web.IdentityServer", "src\Volo.Abp.Account.Web.IdentityServer\Volo.Abp.Account.Web.IdentityServer.csproj", "{841C216F-B0E9-472C-BC19-2C31ADF0664F}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -17,12 +19,17 @@ Global
{FCAC4354-7B13-4A91-A2F4-04D00F253C91}.Debug|Any CPU.Build.0 = Debug|Any CPU {FCAC4354-7B13-4A91-A2F4-04D00F253C91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCAC4354-7B13-4A91-A2F4-04D00F253C91}.Release|Any CPU.ActiveCfg = Release|Any CPU {FCAC4354-7B13-4A91-A2F4-04D00F253C91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCAC4354-7B13-4A91-A2F4-04D00F253C91}.Release|Any CPU.Build.0 = Release|Any CPU {FCAC4354-7B13-4A91-A2F4-04D00F253C91}.Release|Any CPU.Build.0 = Release|Any CPU
{841C216F-B0E9-472C-BC19-2C31ADF0664F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{841C216F-B0E9-472C-BC19-2C31ADF0664F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{841C216F-B0E9-472C-BC19-2C31ADF0664F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{841C216F-B0E9-472C-BC19-2C31ADF0664F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{FCAC4354-7B13-4A91-A2F4-04D00F253C91} = {B5881429-EFF7-4F30-8C0B-0AC41E36B74E} {FCAC4354-7B13-4A91-A2F4-04D00F253C91} = {B5881429-EFF7-4F30-8C0B-0AC41E36B74E}
{841C216F-B0E9-472C-BC19-2C31ADF0664F} = {B5881429-EFF7-4F30-8C0B-0AC41E36B74E}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2B054393-D2B2-4EA8-8A15-D60CBCF3E7A9} SolutionGuid = {2B054393-D2B2-4EA8-8A15-D60CBCF3E7A9}

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

@ -0,0 +1,14 @@
using Volo.Abp.IdentityServer;
using Volo.Abp.Modularity;
namespace Volo.Abp.Account.Web
{
[DependsOn(
typeof(AbpAccountWebModule),
typeof(AbpIdentityServerDomainModule)
)]
public class AbpAccountWebIdentityServerModule : AbpModule
{
}
}

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

@ -0,0 +1,239 @@
using IdentityModel;
using IdentityServer4.Events;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Stores;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System;
using System.Diagnostics;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace Volo.Abp.Account.Web.Pages.Account
{
[ExposeServices(typeof(LoginModel))]
public class IdentityServerSupportedLoginModel : LoginModel
{
protected IIdentityServerInteractionService Interaction { get; }
protected IClientStore ClientStore { get; }
protected IEventService IdentityServerEvents { get; }
public IdentityServerSupportedLoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IIdentityServerInteractionService interaction,
IClientStore clientStore,
IEventService identityServerEvents)
:base(
schemeProvider,
accountOptions)
{
_schemeProvider = schemeProvider;
Interaction = interaction;
ClientStore = clientStore;
IdentityServerEvents = identityServerEvents;
_accountOptions = accountOptions.Value;
}
public override async Task OnGetAsync()
{
LoginInput = new LoginInputModel();
var context = await Interaction.GetAuthorizationContextAsync(ReturnUrl);
if (context != null)
{
LoginInput.UserNameOrEmailAddress = context.LoginHint;
//TODO: Reference AspNetCore MultiTenancy module and use options to get the tenant key!
var tenant = context.Parameters[TenantResolverConsts.DefaultTenantKey];
if (string.IsNullOrEmpty(tenant))
{
if (Request.Cookies.ContainsKey(TenantResolverConsts.DefaultTenantKey))
{
CurrentTenant.Change(null);
Response.Cookies.Delete(TenantResolverConsts.DefaultTenantKey);
}
}
else
{
CurrentTenant.Change(Guid.Parse(tenant));
Response.Cookies.Append(TenantResolverConsts.DefaultTenantKey, tenant);
}
}
if (context?.IdP != null)
{
LoginInput.UserNameOrEmailAddress = context.LoginHint;
ExternalProviders = new[] { new ExternalProviderModel { AuthenticationScheme = context.IdP } };
return;
}
var schemes = await _schemeProvider.GetAllSchemesAsync();
var providers = schemes
.Where(x => x.DisplayName != null || x.Name.Equals(_accountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase))
.Select(x => new ExternalProviderModel
{
DisplayName = x.DisplayName,
AuthenticationScheme = x.Name
})
.ToList();
EnableLocalLogin = true; //TODO: We can get default from a setting?
if (context?.ClientId != null)
{
var client = await ClientStore.FindEnabledClientByIdAsync(context.ClientId);
if (client != null)
{
EnableLocalLogin = client.EnableLocalLogin;
if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any())
{
providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList();
}
}
}
ExternalProviders = providers.ToArray();
if (IsExternalLoginOnly)
{
//return await ExternalLogin(vm.ExternalLoginScheme, returnUrl);
throw new NotImplementedException();
}
}
[UnitOfWork] //TODO: Will be removed when we implement action filter
public override async Task<IActionResult> OnPostAsync(string action)
{
EnableLocalLogin = true; //TODO: We can get default from a setting?
if (action == "Cancel")
{
var context = await Interaction.GetAuthorizationContextAsync(ReturnUrl);
if (context == null)
{
return Redirect("~/");
}
await Interaction.GrantConsentAsync(context, ConsentResponse.Denied);
return Redirect(ReturnUrl);
}
ValidateModel();
await ReplaceEmailToUsernameOfInputIfNeeds();
var result = await SignInManager.PasswordSignInAsync(
LoginInput.UserNameOrEmailAddress,
LoginInput.Password,
LoginInput.RememberMe,
true
);
if (result.RequiresTwoFactor)
{
return RedirectToPage("./SendSecurityCode", new
{
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash,
rememberMe = LoginInput.RememberMe
});
}
if (result.IsLockedOut)
{
Alerts.Warning(L["UserLockedOutMessage"]);
return Page();
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./SendSecurityCode");
}
if (result.IsNotAllowed)
{
Alerts.Warning(L["LoginIsNotAllowed"]);
return Page();
}
if (!result.Succeeded)
{
Alerts.Danger(L["InvalidUserNameOrPassword"]);
return Page();
}
//TODO: Find a way of getting user's id from the logged in user and do not query it again like that!
var user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
Debug.Assert(user != null, nameof(user) + " != null");
await IdentityServerEvents.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName)); //TODO: Use user's name once implemented
return RedirectSafely(ReturnUrl, ReturnUrlHash);
}
[UnitOfWork]
public override async Task<IActionResult> OnPostExternalLogin(string provider)
{
if (_accountOptions.WindowsAuthenticationSchemeName == provider)
{
return await ProcessWindowsLoginAsync();
}
return await base.OnPostExternalLogin(provider);
}
private async Task<IActionResult> ProcessWindowsLoginAsync()
{
var result = await HttpContext.AuthenticateAsync(_accountOptions.WindowsAuthenticationSchemeName);
if (!(result?.Principal is WindowsPrincipal windowsPrincipal))
{
return Challenge(_accountOptions.WindowsAuthenticationSchemeName);
}
var props = new AuthenticationProperties
{
RedirectUri = Url.Page("./Login", pageHandler: "ExternalLoginCallback", values: new { ReturnUrl, ReturnUrlHash }),
Items =
{
{"scheme", _accountOptions.WindowsAuthenticationSchemeName},
}
};
var identity = new ClaimsIdentity(_accountOptions.WindowsAuthenticationSchemeName);
identity.AddClaim(new Claim(JwtClaimTypes.Subject, windowsPrincipal.Identity.Name));
identity.AddClaim(new Claim(JwtClaimTypes.Name, windowsPrincipal.Identity.Name));
//TODO: Consider to add Windows groups the the identity
//if (_accountOptions.IncludeWindowsGroups)
//{
// var windowsIdentity = windowsPrincipal.Identity as WindowsIdentity;
// if (windowsIdentity != null)
// {
// var groups = windowsIdentity.Groups?.Translate(typeof(NTAccount));
// var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value));
// identity.AddClaims(roles);
// }
//}
await HttpContext.SignInAsync(
IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme,
new ClaimsPrincipal(identity),
props
);
return RedirectSafely(props.RedirectUri);
}
}
}

2
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/_ViewImports.cshtml

@ -0,0 +1,2 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap

27
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Properties/launchSettings.json

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:49583/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Volo.Abp.Account.Web.IdentityServer": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:49584/"
}
}
}

33
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj

@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.Account.Web.IdentityServer</AssemblyName>
<PackageId>Volo.Abp.Account.Web.IdentityServer</PackageId>
<IsPackable>true</IsPackable>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace>Volo.Abp.Account.Web</RootNamespace>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\**\*.*" Exclude="*.cs" />
</ItemGroup>
<ItemGroup>
<Content Remove="Properties\launchSettings.json" />
<EmbeddedResource Remove="Pages\Account\IdentityServerSupportedLoginModel.cs" />
<None Include="Properties\launchSettings.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\identityserver\src\Volo.Abp.IdentityServer.Domain\Volo.Abp.IdentityServer.Domain.csproj" />
<ProjectReference Include="..\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" />
</ItemGroup>
</Project>

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

@ -15,8 +15,10 @@ using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Account.Web namespace Volo.Abp.Account.Web
{ {
[DependsOn(typeof(AbpIdentityAspNetCoreModule))] [DependsOn(
[DependsOn(typeof(AbpAspNetCoreMvcUiThemeSharedModule))] typeof(AbpIdentityAspNetCoreModule),
typeof(AbpAspNetCoreMvcUiThemeSharedModule)
)]
public class AbpAccountWebModule : AbpModule public class AbpAccountWebModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context) public override void PreConfigureServices(ServiceConfigurationContext context)

73
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml

@ -1,30 +1,59 @@
@page @page
@model Volo.Abp.Account.Web.Pages.Account.LoginModel
@using Volo.Abp.Account.Web.Settings @using Volo.Abp.Account.Web.Settings
@inherits Volo.Abp.Account.Web.Pages.Account.AccountPage @model Volo.Abp.Account.Web.Pages.Account.LoginModel
@inject Volo.Abp.Settings.ISettingManager SettingManager @inject Volo.Abp.Settings.ISettingManager SettingManager
<h2>@L["Login"]</h2> @if (Model.EnableLocalLogin)
{
<form method="post">
<input asp-for="ReturnUrl" />
<input asp-for="ReturnUrlHash" />
<div class="form-group">
<label asp-for="LoginInput.UserNameOrEmailAddress"></label>
<input asp-for="LoginInput.UserNameOrEmailAddress" class="form-control" />
<span asp-validation-for="LoginInput.UserNameOrEmailAddress" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LoginInput.Password"></label>
<input asp-for="LoginInput.Password" class="form-control" />
<span asp-validation-for="LoginInput.Password" class="text-danger"></span>
</div>
<div class="form-check">
<label asp-for="LoginInput.RememberMe" class="form-check-label">
<input asp-for="LoginInput.RememberMe" class="form-check-input" />
@Html.DisplayNameFor(m => m.LoginInput.RememberMe)
</label>
</div>
<abp-button type="button" button-type="Secondary" name="Action" value="Cancel">Cancel</abp-button> @* TODO: Only show if identity server is used *@
<abp-button type="submit" button-type="Primary" name="Action" value="Login">Login</abp-button>
</form>
<form method="post"> <div style="padding-top: 20px">
<abp-input asp-for="Input.UserNameOrEmailAddress" auto-focus="true" /> @if (string.Equals(await SettingManager.GetOrNullAsync(AccountSettingNames.IsSelfRegistrationEnabled), "true", StringComparison.OrdinalIgnoreCase))
<abp-input asp-for="Input.Password" /> {
<abp-input asp-for="Input.RememberMe" class="mb-3" /> <a href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})">Register</a>
<abp-button button-type="Primary" type="submit">@L["Login"]</abp-button> }
@if (string.Equals(await SettingManager.GetOrNullAsync(AccountSettingNames.IsSelfRegistrationEnabled), "true", StringComparison.OrdinalIgnoreCase)) </div>
{ }
<a abp-button="Secondary" href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})">@L["Register"]</a>
}
</form>
@if (Model.ExternalLogins.Any()) @if (Model.VisibleExternalProviders.Any())
{ {
<h4>Use another service to log in.</h4> <div class="col-md-6">
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post"> <h4>Use another service to log in.</h4>
<div> <form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post">
@foreach (var provider in Model.ExternalLogins) <input asp-for="ReturnUrl" />
<input asp-for="ReturnUrlHash" />
@foreach (var provider in Model.VisibleExternalProviders)
{ {
<abp-button button-type="Primary" type="submit" name="provider" value="@provider.Name">@provider.DisplayName</abp-button> <button type="submit" class="btn btn-primary" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
} }
</div> </form>
</form> </div>
} }
@if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
{
<div class="alert alert-warning">
<strong>Invalid login request</strong>
There are no login schemes configured for this client.
</div>
}

156
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs

@ -1,49 +1,112 @@
using System; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Volo.Abp.Identity; using Volo.Abp.Identity;
using Volo.Abp.Security.Claims; using Volo.Abp.Security.Claims;
using Volo.Abp.Uow; using Volo.Abp.Uow;
using Volo.Abp.Validation;
namespace Volo.Abp.Account.Web.Pages.Account namespace Volo.Abp.Account.Web.Pages.Account
{ {
public class LoginModel : AccountPageModel public class LoginModel : AccountPageModel
{ {
[HiddenInput]
[BindProperty(SupportsGet = true)] [BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; } public string ReturnUrl { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)] [BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; } public string ReturnUrlHash { get; set; }
[BindProperty] [BindProperty]
public PostInput Input { get; set; } public LoginInputModel LoginInput { get; set; }
public bool EnableLocalLogin { get; set; }
public IList<AuthenticationScheme> ExternalLogins { get; set; } //TODO: Why there is an ExternalProviders if only the VisibleExternalProviders is used.
public IEnumerable<ExternalProviderModel> ExternalProviders { get; set; }
public IEnumerable<ExternalProviderModel> VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName));
public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1;
public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null;
//Optional IdentityServer services
//public IIdentityServerInteractionService Interaction { get; set; }
//public IClientStore ClientStore { get; set; }
//public IEventService IdentityServerEvents { get; set; }
protected IAuthenticationSchemeProvider _schemeProvider;
protected AbpAccountOptions _accountOptions;
public LoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions)
{
_schemeProvider = schemeProvider;
_accountOptions = accountOptions.Value;
}
public async Task OnGetAsync() public virtual async Task OnGetAsync()
{ {
ExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToList(); LoginInput = new LoginInputModel();
var schemes = await _schemeProvider.GetAllSchemesAsync();
var providers = schemes
.Where(x => x.DisplayName != null || x.Name.Equals(_accountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase))
.Select(x => new ExternalProviderModel
{
DisplayName = x.DisplayName,
AuthenticationScheme = x.Name
})
.ToList();
EnableLocalLogin = true; //TODO: We can get default from a setting?
ExternalProviders = providers.ToArray();
if (IsExternalLoginOnly)
{
//return await ExternalLogin(vm.ExternalLoginScheme, returnUrl);
throw new NotImplementedException();
}
} }
[UnitOfWork] //TODO: Will be removed when we implement action filter [UnitOfWork] //TODO: Will be removed when we implement action filter
public virtual async Task<IActionResult> OnPostAsync() public virtual async Task<IActionResult> OnPostAsync(string action)
{ {
EnableLocalLogin = true; //TODO: We can get default from a setting?
ValidateModel(); ValidateModel();
await ReplaceEmailToUsernameOfInputIfNeeds();
var result = await SignInManager.PasswordSignInAsync( var result = await SignInManager.PasswordSignInAsync(
Input.UserNameOrEmailAddress, LoginInput.UserNameOrEmailAddress,
Input.Password, LoginInput.Password,
Input.RememberMe, LoginInput.RememberMe,
true true
); );
if (result.RequiresTwoFactor)
{
return RedirectToPage("./SendSecurityCode", new
{
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash,
rememberMe = LoginInput.RememberMe
});
}
if (result.IsLockedOut) if (result.IsLockedOut)
{ {
Alerts.Warning(L["UserLockedOutMessage"]); Alerts.Warning(L["UserLockedOutMessage"]);
@ -67,22 +130,33 @@ namespace Volo.Abp.Account.Web.Pages.Account
return Page(); return Page();
} }
//TODO: Find a way of getting user's id from the logged in user and do not query it again like that!
var user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
Debug.Assert(user != null, nameof(user) + " != null");
return RedirectSafely(ReturnUrl, ReturnUrlHash); return RedirectSafely(ReturnUrl, ReturnUrlHash);
} }
[UnitOfWork] //TODO: Will be removed when we implement action filter
public virtual IActionResult OnPostExternalLogin(string provider, string returnUrl = "", string returnUrlHash = "")
{
var redirectUrl = Url.Page("./Login", pageHandler: "ExternalLoginCallback", values: new { returnUrl, returnUrlHash });
[UnitOfWork]
public virtual async Task<IActionResult> OnPostExternalLogin(string provider)
{
var redirectUrl = Url.Page("./Login", pageHandler: "ExternalLoginCallback", values: new { ReturnUrl, ReturnUrlHash });
var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
properties.Items["scheme"] = provider;
return new ChallengeResult(provider, properties); return Challenge(properties, provider);
} }
[UnitOfWork] //TODO: Will be removed when we implement action filter [UnitOfWork]
public virtual async Task<IActionResult> OnGetExternalLoginCallbackAsync(string returnUrl = "", string returnUrlHash = "", string remoteError = null) public virtual async Task<IActionResult> OnGetExternalLoginCallbackAsync(string returnUrl = "", string returnUrlHash = "", string remoteError = null)
{ {
//TODO: Did not implemented Identity Server 4 sample for this method (see ExternalLoginCallback in Quickstart of IDS4 sample)
/* Also did not implement these:
* - Logout(string logoutId)
*/
if (remoteError != null) if (remoteError != null)
{ {
Logger.LogWarning($"External login callback error: {remoteError}"); Logger.LogWarning($"External login callback error: {remoteError}");
@ -108,13 +182,13 @@ namespace Volo.Abp.Account.Web.Pages.Account
throw new UserFriendlyException("Cannot proceed because user is locked out!"); throw new UserFriendlyException("Cannot proceed because user is locked out!");
} }
//TODO: Handle other cases
if (result.Succeeded) if (result.Succeeded)
{ {
return RedirectSafely(returnUrl, returnUrlHash); return RedirectSafely(returnUrl, returnUrlHash);
} }
//TODO: Handle other cases for result!
// Get the information about the user from the external login provider // Get the information about the user from the external login provider
var info = await SignInManager.GetExternalLoginInfoAsync(); var info = await SignInManager.GetExternalLoginInfoAsync();
if (info == null) if (info == null)
@ -128,7 +202,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
return RedirectSafely(returnUrl, returnUrlHash); return RedirectSafely(returnUrl, returnUrlHash);
} }
private async Task<IdentityUser> CreateExternalUserAsync(ExternalLoginInfo info) protected virtual async Task<IdentityUser> CreateExternalUserAsync(ExternalLoginInfo info)
{ {
var emailAddress = info.Principal.FindFirstValue(AbpClaimTypes.Email); var emailAddress = info.Principal.FindFirstValue(AbpClaimTypes.Email);
@ -141,18 +215,46 @@ namespace Volo.Abp.Account.Web.Pages.Account
return user; return user;
} }
public class PostInput protected virtual async Task ReplaceEmailToUsernameOfInputIfNeeds()
{
if (!ValidationHandler.IsValidEmailAddress(LoginInput.UserNameOrEmailAddress))
{
return;
}
var userByUsername = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress);
if (userByUsername != null)
{
return;
}
var userByEmail = await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
if (userByEmail == null)
{
return;
}
LoginInput.UserNameOrEmailAddress = userByEmail.UserName;
}
public class LoginInputModel
{ {
[Required] [Required]
[StringLength(255)] [StringLength(IdentityUserConsts.MaxEmailLength)]
public string UserNameOrEmailAddress { get; set; } public string UserNameOrEmailAddress { get; set; }
[Required] [Required]
[StringLength(32)] [StringLength(IdentityUserConsts.MaxPasswordLength)]
[DataType(DataType.Password)] [DataType(DataType.Password)]
public string Password { get; set; } public string Password { get; set; }
public bool RememberMe { get; set; } public bool RememberMe { get; set; }
} }
public class ExternalProviderModel
{
public string DisplayName { get; set; }
public string AuthenticationScheme { get; set; }
}
} }
} }

1
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj

@ -6,7 +6,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.2.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.2.0" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> <ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.BackgroundJobs.EntityFrameworkCore\Volo.Abp.BackgroundJobs.EntityFrameworkCore.csproj" /> <ProjectReference Include="..\..\src\Volo.Abp.BackgroundJobs.EntityFrameworkCore\Volo.Abp.BackgroundJobs.EntityFrameworkCore.csproj" />

4
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Volo.BloggingTestApp.EntityFrameworkCore.csproj

@ -8,10 +8,6 @@
<Compile Remove="Migrations\20181108103917_Identity_Changes.cs" /> <Compile Remove="Migrations\20181108103917_Identity_Changes.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.0" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Volo.Blogging.EntityFrameworkCore\Volo.Blogging.EntityFrameworkCore.csproj" /> <ProjectReference Include="..\..\src\Volo.Blogging.EntityFrameworkCore\Volo.Blogging.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.SqlServer\Volo.Abp.EntityFrameworkCore.SqlServer.csproj" /> <ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.SqlServer\Volo.Abp.EntityFrameworkCore.SqlServer.csproj" />

48
modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs

@ -13,18 +13,20 @@ using Microsoft.Extensions.Options;
using Swashbuckle.AspNetCore.Swagger; using Swashbuckle.AspNetCore.Swagger;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Account.Web; using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI; using Volo.Abp.AspNetCore.Mvc.UI;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
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;
using Volo.Abp.Identity; using Volo.Abp.Identity;
using Volo.Abp.Identity.Web; using Volo.Abp.Identity.Web;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.Threading; using Volo.Abp.Threading;
using Volo.Abp.UI; using Volo.Abp.UI;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;
@ -45,6 +47,8 @@ namespace Volo.BloggingTestApp
typeof(AbpAccountWebModule), typeof(AbpAccountWebModule),
typeof(AbpIdentityWebModule), typeof(AbpIdentityWebModule),
typeof(AbpIdentityApplicationModule), typeof(AbpIdentityApplicationModule),
typeof(AbpPermissionManagementDomainIdentityModule),
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpAutofacModule), typeof(AbpAutofacModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule) typeof(AbpAspNetCoreMvcUiBasicThemeModule)
)] )]
@ -75,13 +79,13 @@ namespace Volo.BloggingTestApp
{ {
Configure<VirtualFileSystemOptions>(options => Configure<VirtualFileSystemOptions>(options =>
{ {
options.FileSets.ReplaceEmbeddedByPyhsical<AbpUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.UI", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<AbpUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiBootstrapModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Bootstrap", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiBootstrapModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Bootstrap", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiThemeSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiThemeSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiBasicThemeModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiBasicThemeModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<BloggingDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}Volo.Blogging.Domain", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<BloggingDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}Volo.Blogging.Domain", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<BloggingWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}Volo.Blogging.Web", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical<BloggingWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}Volo.Blogging.Web", Path.DirectorySeparatorChar)));
}); });
} }
@ -90,6 +94,7 @@ namespace Volo.BloggingTestApp
{ {
options.SwaggerDoc("v1", new Info { Title = "Blogging API", Version = "v1" }); options.SwaggerDoc("v1", new Info { Title = "Blogging API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true); options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
}); });
var cultures = new List<CultureInfo> { new CultureInfo("en"), new CultureInfo("tr") }; var cultures = new List<CultureInfo> { new CultureInfo("en"), new CultureInfo("tr") };
@ -142,15 +147,26 @@ namespace Volo.BloggingTestApp
template: "{controller=Home}/{action=Index}/{id?}"); template: "{controller=Home}/{action=Index}/{id?}");
}); });
AsyncHelper.RunSync(async () =>
using (var scope = context.ServiceProvider.CreateScope())
{ {
await context.ServiceProvider AsyncHelper.RunSync(async () =>
.GetRequiredService<IIdentityDataSeeder>() {
.SeedAsync( await scope.ServiceProvider
"1q2w3E*", .GetRequiredService<IIdentityDataSeeder>()
IdentityPermissions.GetAll().Union(BloggingPermissions.GetAll()) .SeedAsync(
); "1q2w3E*"
}); );
await scope.ServiceProvider
.GetRequiredService<IPermissionDataSeeder>()
.SeedAsync(
RolePermissionValueProvider.ProviderName,
"admin",
IdentityPermissions.GetAll().Union(BloggingPermissions.GetAll())
);
});
}
} }
} }
} }

2
modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj

@ -31,6 +31,8 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj" /> <ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj" />
<ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.Identity.Application\Volo.Abp.Identity.Application.csproj" /> <ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.Identity.Application\Volo.Abp.Identity.Application.csproj" />
<ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.Identity.Web\Volo.Abp.Identity.Web.csproj" /> <ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.Identity.Web\Volo.Abp.Identity.Web.csproj" />
<ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.PermissionManagement.Domain.Identity\Volo.Abp.PermissionManagement.Domain.Identity.csproj" />
<ProjectReference Include="..\..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.Application\Volo.Abp.PermissionManagement.Application.csproj" />
<ProjectReference Include="..\..\..\..\modules\account\src\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" /> <ProjectReference Include="..\..\..\..\modules\account\src\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" />
</ItemGroup> </ItemGroup>

2
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/ICommentAppService.cs

@ -8,7 +8,7 @@ namespace Volo.Blogging.Comments
{ {
public interface ICommentAppService : IApplicationService public interface ICommentAppService : IApplicationService
{ {
Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(GetCommentListOfPostAsync input); Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(Guid postId);
Task<CommentWithDetailsDto> CreateAsync(CreateCommentDto input); Task<CommentWithDetailsDto> CreateAsync(CreateCommentDto input);

2
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Tagging/Dtos/GetPopularTagsInput.cs

@ -7,7 +7,5 @@ namespace Volo.Blogging.Tagging.Dtos
public int ResultCount { get; set; } = 10; public int ResultCount { get; set; } = 10;
public int? MinimumPostCount { get; set; } public int? MinimumPostCount { get; set; }
public Guid BlogId { get; set; }
} }
} }

2
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Tagging/ITagAppService.cs

@ -8,7 +8,7 @@ namespace Volo.Blogging.Tagging
{ {
public interface ITagAppService : IApplicationService public interface ITagAppService : IApplicationService
{ {
Task<List<TagDto>> GetPopularTags(GetPopularTagsInput input); Task<List<TagDto>> GetPopularTags(Guid blogId, GetPopularTagsInput input);
} }
} }

2
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs

@ -15,7 +15,7 @@ namespace Volo.Blogging
public BloggingApplicationAutoMapperProfile() public BloggingApplicationAutoMapperProfile()
{ {
CreateMap<Blog, BlogDto>(); CreateMap<Blog, BlogDto>();
CreateMap<Post, PostWithDetailsDto>().Ignore(x=>x.Writer).Ignore(x=>x.CommentCount); CreateMap<Post, PostWithDetailsDto>().Ignore(x=>x.Writer).Ignore(x=>x.CommentCount).Ignore(x=>x.Tags);
CreateMap<Comment, CommentWithDetailsDto>().Ignore(x => x.Writer); CreateMap<Comment, CommentWithDetailsDto>().Ignore(x => x.Writer);
CreateMap<Tag, TagDto>(); CreateMap<Tag, TagDto>();
} }

8
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAppService.cs

@ -26,9 +26,9 @@ namespace Volo.Blogging.Comments
UserLookupService = userLookupService; UserLookupService = userLookupService;
} }
public async Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(GetCommentListOfPostAsync input) public async Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(Guid postId)
{ {
var comments = await GetListOfPostAsync(input); var comments = await GetListOfPostAsync(postId);
var userDictionary = new Dictionary<Guid, BlogUserDto>(); var userDictionary = new Dictionary<Guid, BlogUserDto>();
foreach (var commentDto in comments) foreach (var commentDto in comments)
@ -73,9 +73,9 @@ namespace Volo.Blogging.Comments
return hierarchicalComments; return hierarchicalComments;
} }
private async Task<List<CommentWithDetailsDto>> GetListOfPostAsync(GetCommentListOfPostAsync input) private async Task<List<CommentWithDetailsDto>> GetListOfPostAsync(Guid postId)
{ {
var comments = await _commentRepository.GetListOfPostAsync(input.PostId); var comments = await _commentRepository.GetListOfPostAsync(postId);
return new List<CommentWithDetailsDto>( return new List<CommentWithDetailsDto>(
ObjectMapper.Map<List<Comment>, List<CommentWithDetailsDto>>(comments)); ObjectMapper.Map<List<Comment>, List<CommentWithDetailsDto>>(comments));

4
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Tagging/TagAppService.cs

@ -22,9 +22,9 @@ namespace Volo.Blogging.Tagging
_tagRepository = tagRepository; _tagRepository = tagRepository;
} }
public async Task<List<TagDto>> GetPopularTags(GetPopularTagsInput input) public async Task<List<TagDto>> GetPopularTags(Guid blogId, GetPopularTagsInput input)
{ {
var postTags = (await _tagRepository.GetListAsync(input.BlogId)).OrderByDescending(t=>t.UsageCount) var postTags = (await _tagRepository.GetListAsync(blogId)).OrderByDescending(t=>t.UsageCount)
.WhereIf(input.MinimumPostCount != null, t=>t.UsageCount >= input.MinimumPostCount) .WhereIf(input.MinimumPostCount != null, t=>t.UsageCount >= input.MinimumPostCount)
.Take(input.ResultCount).ToList(); .Take(input.ResultCount).ToList();

11
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogsController.cs

@ -1,6 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Volo.Abp; using Volo.Abp;
@ -14,10 +12,7 @@ namespace Volo.Blogging
{ {
[RemoteService] [RemoteService]
[Area("blogging")] [Area("blogging")]
[Controller]
[ControllerName("Blogs")]
[Route("api/blogging/blogs")] [Route("api/blogging/blogs")]
[DisableAuditing]
public class BlogsController : AbpController, IBlogAppService public class BlogsController : AbpController, IBlogAppService
{ {
private readonly IBlogAppService _blogAppService; private readonly IBlogAppService _blogAppService;
@ -28,21 +23,20 @@ namespace Volo.Blogging
} }
[HttpGet] [HttpGet]
[Route("")]
public async Task<PagedResultDto<BlogDto>> GetListPagedAsync(PagedAndSortedResultRequestDto input) public async Task<PagedResultDto<BlogDto>> GetListPagedAsync(PagedAndSortedResultRequestDto input)
{ {
return await _blogAppService.GetListPagedAsync(input); return await _blogAppService.GetListPagedAsync(input);
} }
[HttpGet] [HttpGet]
[Route("/all")] [Route("all")]
public async Task<ListResultDto<BlogDto>> GetListAsync() public async Task<ListResultDto<BlogDto>> GetListAsync()
{ {
return await _blogAppService.GetListAsync(); return await _blogAppService.GetListAsync();
} }
[HttpGet] [HttpGet]
[Route("{shortName}")] [Route("by-shortname/{shortName}")]
public async Task<BlogDto> GetByShortNameAsync(string shortName) public async Task<BlogDto> GetByShortNameAsync(string shortName)
{ {
return await _blogAppService.GetByShortNameAsync(shortName); return await _blogAppService.GetByShortNameAsync(shortName);
@ -69,6 +63,7 @@ namespace Volo.Blogging
} }
[HttpDelete] [HttpDelete]
[Route("{id}")]
public async Task Delete(Guid id) public async Task Delete(Guid id)
{ {
await _blogAppService.Delete(id); await _blogAppService.Delete(id);

53
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/CommentsController.cs

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Auditing;
using Volo.Blogging.Comments;
using Volo.Blogging.Comments.Dtos;
namespace Volo.Blogging
{
[RemoteService]
[Area("blogging")]
[Route("api/blogging/comments")]
public class CommentsController : AbpController, ICommentAppService
{
private readonly ICommentAppService _commentAppService;
public CommentsController(ICommentAppService commentAppService)
{
_commentAppService = commentAppService;
}
[HttpGet]
[Route("hierarchical/{postId}")]
public Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(Guid postId)
{
return _commentAppService.GetHierarchicalListOfPostAsync(postId);
}
[HttpPost]
public Task<CommentWithDetailsDto> CreateAsync(CreateCommentDto input)
{
return _commentAppService.CreateAsync(input);
}
[HttpPut]
[Route("{id}")]
public Task<CommentWithDetailsDto> UpdateAsync(Guid id, UpdateCommentDto input)
{
return _commentAppService.UpdateAsync(id, input);
}
[HttpDelete]
[Route("{id}")]
public Task DeleteAsync(Guid id)
{
throw new NotImplementedException();
}
}
}

65
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/PostsController.cs

@ -0,0 +1,65 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Auditing;
using Volo.Blogging.Posts;
namespace Volo.Blogging
{
[RemoteService]
[Area("blogging")]
[Route("api/blogging/posts")]
public class PostsController : AbpController, IPostAppService
{
private readonly IPostAppService _postAppService;
public PostsController(IPostAppService postAppService)
{
_postAppService = postAppService;
}
[HttpGet]
[Route("{blogId}/all")]
public Task<ListResultDto<PostWithDetailsDto>> GetListByBlogIdAndTagName(Guid blogId, string tagName)
{
return _postAppService.GetListByBlogIdAndTagName(blogId, tagName);
}
[HttpGet]
[Route("read/{id}")]
public Task<PostWithDetailsDto> GetForReadingAsync(GetPostInput input)
{
return _postAppService.GetForReadingAsync(input);
}
[HttpGet]
[Route("{id}")]
public Task<PostWithDetailsDto> GetAsync(Guid id)
{
return _postAppService.GetAsync(id);
}
[HttpPost]
public Task<PostWithDetailsDto> CreateAsync(CreatePostDto input)
{
return _postAppService.CreateAsync(input);
}
[HttpPut]
[Route("{id}")]
public Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input)
{
return _postAppService.UpdateAsync(id, input);
}
[HttpDelete]
[Route("{id}")]
public Task DeleteAsync(Guid id)
{
return _postAppService.DeleteAsync(id);
}
}
}

32
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/TagsController.cs

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Auditing;
using Volo.Blogging.Tagging;
using Volo.Blogging.Tagging.Dtos;
namespace Volo.Blogging
{
[RemoteService]
[Area("blogging")]
[Route("api/blogging/tags")]
public class TagsController : AbpController, ITagAppService
{
private readonly ITagAppService _tagAppService;
public TagsController(ITagAppService tagAppService)
{
_tagAppService = tagAppService;
}
[HttpGet]
[Route("popular/{blogId}")]
public Task<List<TagDto>> GetPopularTags(Guid blogId, GetPopularTagsInput input)
{
return _tagAppService.GetPopularTags(blogId, input);
}
}
}

2
modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs

@ -40,8 +40,6 @@ namespace Volo.Blogging
context.Menu.AddItem(managementRootMenuItem); context.Menu.AddItem(managementRootMenuItem);
} }
} }
} }
} }
} }

4
modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Create.cshtml.cs

@ -35,9 +35,9 @@ namespace Volo.Blogging.Pages.Admin.Blogs
public async Task<IActionResult> OnPostAsync() public async Task<IActionResult> OnPostAsync()
{ {
var language = ObjectMapper.Map<BlogCreateModalView, CreateBlogDto>(Blog); var blogDto = ObjectMapper.Map<BlogCreateModalView, CreateBlogDto>(Blog);
await _blogAppService.Create(language); await _blogAppService.Create(blogDto);
return NoContent(); return NoContent();
} }

2
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml.cs

@ -70,7 +70,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
{ {
Blog = await _blogAppService.GetByShortNameAsync(BlogShortName); Blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Post = await _postAppService.GetForReadingAsync(new GetPostInput { BlogId = Blog.Id, Url = PostUrl }); Post = await _postAppService.GetForReadingAsync(new GetPostInput { BlogId = Blog.Id, Url = PostUrl });
CommentsWithReplies = await _commentAppService.GetHierarchicalListOfPostAsync(new GetCommentListOfPostAsync() { PostId = Post.Id }); CommentsWithReplies = await _commentAppService.GetHierarchicalListOfPostAsync(Post.Id);
CountComments(); CountComments();
} }

2
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml.cs

@ -39,7 +39,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
{ {
Blog = await _blogAppService.GetByShortNameAsync(BlogShortName); Blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Posts = (await _postAppService.GetListByBlogIdAndTagName(Blog.Id, TagName)).Items; Posts = (await _postAppService.GetListByBlogIdAndTagName(Blog.Id, TagName)).Items;
PopularTags = (await _tagAppService.GetPopularTags(new GetPopularTagsInput {BlogId = Blog.Id, ResultCount = 10, MinimumPostCount = 2})); PopularTags = (await _tagAppService.GetPopularTags(Blog.Id, new GetPopularTagsInput {ResultCount = 10, MinimumPostCount = 2}));
} }
} }
} }

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

Loading…
Cancel
Save