Browse Source

Merge branch 'dev' into net7

pull/13626/head
maliming 3 years ago
parent
commit
6e66251d5a
No known key found for this signature in database GPG Key ID: 96224957E51C89E
  1. 4
      .github/workflows/labeler.yml
  2. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
  3. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json
  4. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json
  5. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hant.json
  6. 8
      docs/en/Apps/VoloDocs.md
  7. 34
      docs/en/Dependency-Injection.md
  8. 4
      docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md
  9. 2
      docs/en/Getting-Started-Running-Solution.md
  10. 4
      docs/en/Migration-Guides/OpenIddict-Step-by-Step.md
  11. 14
      docs/en/Modules/OpenIddict.md
  12. 8
      docs/en/Startup-Templates/Application.md
  13. 4
      docs/en/docs-nav.json
  14. BIN
      docs/en/images/bookstore-rider-solution-tiered.png
  15. BIN
      docs/en/images/solution-structure-solution-explorer-rider.png
  16. BIN
      docs/en/images/tiered-solution-applications-authserver.png
  17. 183
      docs/pt-BR/Validation.md
  18. 7
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Components/LayoutHooks/LayoutHook.razor
  19. 39
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Components/LayoutHooks/LayoutHook.razor.cs
  20. 6
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/StandardLayouts.cs
  21. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs
  22. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs
  23. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalSize.cs
  24. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalSizeExtensions.cs
  25. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/Default.cshtml
  26. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/LayoutHookViewComponent.cs
  27. 8
      framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs
  28. 21
      framework/src/Volo.Abp.Autofac/Volo/Abp/Autofac/AbpPropertySelector.cs
  29. 8
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs
  30. 40
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
  31. 43
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs
  32. 12
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/CommercialPackages.cs
  33. 7
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/CreateAppSettingsSecretsStep.cs
  34. 17
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs
  35. 6
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs
  36. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
  37. 10
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/CmdHelper.cs
  38. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/ICmdHelper.cs
  39. 9
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DisablePropertyInjectionAttribute.cs
  40. 2
      framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/ConsumerPool.cs
  41. 2
      framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/ProducerPool.cs
  42. 2
      framework/src/Volo.Abp.UI/Volo/Abp/Ui/LayoutHooks/AbpLayoutHookOptions.cs
  43. 4
      framework/src/Volo.Abp.UI/Volo/Abp/Ui/LayoutHooks/LayoutHookInfo.cs
  44. 2
      framework/src/Volo.Abp.UI/Volo/Abp/Ui/LayoutHooks/LayoutHookViewModel.cs
  45. 2
      framework/src/Volo.Abp.UI/Volo/Abp/Ui/LayoutHooks/LayoutHooks.cs
  46. 62
      framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/DependencyInjection_Tests.cs
  47. 5
      modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/MainLayout.razor
  48. 1
      modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
  49. 1
      modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml
  50. 1
      modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml
  51. 2
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs
  52. 9
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/IContentAdminAppService.cs
  53. 4
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo.CmsKit.Admin.Application.csproj
  54. 32
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs
  55. 22
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/ContentAdminClientProxy.Generated.cs
  56. 7
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/ContentAdminClientProxy.cs
  57. 29
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/cms-kit-admin-generate-proxy.json
  58. 25
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Contents/ContentAdminController.cs
  59. 47
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Contents/AddWidgetModal.cshtml
  60. 22
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Contents/AddWidgetModal.cshtml.cs
  61. 15
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/wwwroot/client-proxies/cms-kit-admin-proxy.js
  62. 5
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/BlogPostCommonDto.cs
  63. 10
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/IContentAppService.cs
  64. 4
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/PageDto.cs
  65. 22
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentAppService.cs
  66. 9
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/ContentPreview/ContentPreviewViewComponent.cs
  67. 2
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Contents/CmsKitContentWidgetOptions.cs
  68. 3
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Contents/ContentParser.cs
  69. 2
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Contents/ContentWidgetConfig.cs
  70. 11
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs
  71. 8
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs
  72. 8
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs
  73. 2
      modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs
  74. 96
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml
  75. 35
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml.cs
  76. 28
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPostViewModel.cs
  77. 20
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml
  78. 18
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml.cs
  79. 7
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/PageViewModel.cs
  80. 27
      modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentAdminAppService_Tests.cs
  81. 138
      modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentParser_Test.cs
  82. 2
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/DocumentDto.cs
  83. 11
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/DocumentInfoDto.cs
  84. 2
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/IDocumentAdminAppService.cs
  85. 2
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/tr.json
  86. 2
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/IProjectAdminAppService.cs
  87. 9
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/ProjectWithoutDetailsDto.cs
  88. 5
      modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/DocsAdminApplicationAutoMapperProfile.cs
  89. 8
      modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs
  90. 6
      modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs
  91. 6
      modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/DocumentsAdminClientProxy.Generated.cs
  92. 6
      modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/ProjectsAdminClientProxy.Generated.cs
  93. 34
      modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/docs-admin-generate-proxy.json
  94. 7
      modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/DocumentsAdminController.cs
  95. 8
      modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/ProjectsAdminController.cs
  96. 83
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/Index.cshtml
  97. 16
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/Index.cshtml.cs
  98. 104
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/index.js
  99. 14
      modules/docs/src/Volo.Docs.Admin.Web/wwwroot/client-proxies/docs-admin-proxy.js
  100. 11
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/DocumentInfo.cs

4
.github/workflows/labeler.yml

@ -2,8 +2,12 @@ name: Pull request labeler
on:
schedule:
- cron: '0 12 */1 * *'
permissions:
contents: read
jobs:
labeler:
permissions:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: paulfantom/periodic-labeler@master

4
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json

@ -177,6 +177,8 @@
"CheckAllCommunityTalks": "Check All Community Posts",
"ReadMore": "Read More",
"Post": "Post",
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "Explore the contents created by the core ABP team and the ABP community."
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "Explore the contents created by the core ABP team and the ABP community.",
"WelcomeFallCampaign": "Welcome Fall Campaign!",
"GiveAwayForNewPurchases": "Application Development Classroom Training will be given away for the new purchases!"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json

@ -39,6 +39,8 @@
"TrialLicensePeriodHasExpired": "Deneme lisansınızın süresi {0} gün önce sona erdi.",
"TrialLicensePeriodWillExpire": "Deneme lisansınızın süresi {0} gün içinde dolacak.",
"TrialLicensePeriodExpireToday": "Deneme lisans süreniz bugün sona erecek.",
"PurchaseNow": "Şimdi satın al!"
"PurchaseNow": "Şimdi satın al!",
"WelcomeFallCampaign": "Hoş Geldin Sonbahar Kampanyası!",
"GiveAwayForNewPurchases": "Yeni alımlar için Uygulama Geliştirme Sınıfı Eğitimi hediye edilecektir!"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json

@ -174,6 +174,8 @@
"CheckAllCommunityTalks": "检查所有社区帖子",
"ReadMore": "阅读更多",
"Post": "邮政",
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "探索核心 ABP 团队和 ABP 社区创建的内容。"
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "探索核心 ABP 团队和 ABP 社区创建的内容。",
"WelcomeFallCampaign": "欢迎秋季活动!",
"GiveAwayForNewPurchases": "新购买将赠送应用程序开发课堂培训!"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hant.json

@ -39,6 +39,8 @@
"TrialLicensePeriodHasExpired": "您的試用許可期限已於 {0} 天前到期。",
"TrialLicensePeriodWillExpire": "您的試用許可期限將在 {0} 天后到期。",
"TrialLicensePeriodExpireToday": "您的試用許可期將於今天到期。",
"PurchaseNow": "現在買!"
"PurchaseNow": "現在買!",
"WelcomeFallCampaign": "欢迎秋季活动!",
"GiveAwayForNewPurchases": "新购买将赠送应用程序开发课堂培训!"
}
}

8
docs/en/Apps/VoloDocs.md

@ -23,13 +23,13 @@ https://github.com/abpframework/abp/tree/master/modules/docs
You can download the VoloDocs release from the following links:
http://apps.abp.io/VoloDocs/VoloDocs.win-x64.zip - **Windows 64 bit**
https://apps.abp.io/VoloDocs/VoloDocs.win-x64.zip - **Windows 64 bit**
http://apps.abp.io/VoloDocs/VoloDocs.win-x86.zip - **Windows 32 bit**
https://apps.abp.io/VoloDocs/VoloDocs.win-x86.zip - **Windows 32 bit**
http://apps.abp.io/VoloDocs/VoloDocs.osx-x64.zip - **MacOS**
https://apps.abp.io/VoloDocs/VoloDocs.osx-x64.zip - **MacOS**
http://apps.abp.io/VoloDocs/VoloDocs.linux-x64.zip - **Linux**
https://apps.abp.io/VoloDocs/VoloDocs.linux-x64.zip - **Linux**
Notice that, all installations are self-contained deployments. It means all the required third-party dependencies along with the version of .NET Core is included. So you don't need to install any .NET Core SDK / Runtime.

34
docs/en/Dependency-Injection.md

@ -244,25 +244,35 @@ One restriction of property injection is that you cannot use the dependency in y
Property injection is also useful when you want to design a base class that has some common services injected by default. If you're going to use constructor injection, all derived classes should also inject depended services into their own constructors which makes development harder. However, be very careful using property injection for non-optional services as it makes it harder to clearly see the requirements of a class.
### Resolve Service from IServiceProvider
#### DisablePropertyInjectionAttribute
You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below:
You can use `[DisablePropertyInjection]` attribute on class or properties to disable property injection for the whole class or some specific properties.
````C#
[DisablePropertyInjection]
public class MyService : ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public ITaxCalculator TaxCalculator { get; set; }
}
public MyService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
public void DoSomething()
{
var taxCalculator = _serviceProvider.GetService<ITaxCalculator>();
//...
}
[DisablePropertyInjection]
public ITaxCalculator TaxCalculator { get; set; }
}
````
### Resolve Service from IServiceProvider
You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below:
````C#
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
}
````

4
docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md

@ -86,7 +86,7 @@ Defining multiple connections is allowed. In this case, you can specify the conn
This allows you to use multiple RabbitMQ server in your application, but select one of them for the event bus.
You can use any of the [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dotnet-client/api/RabbitMQ.Client.ConnectionFactory.html#properties) properties as the connection properties.
You can use any of the [ConnectionFactory](http://rabbitmq.github.io/rabbitmq-dotnet-client/api/RabbitMQ.Client.ConnectionFactory.html#properties) properties as the connection properties.
**Example: Specify the connection port**
@ -152,4 +152,4 @@ Configure<AbpRabbitMqEventBusOptions>(options =>
});
````
Using these options classes can be combined with the `appsettings.json` way. Configuring an option property in the code overrides the value in the configuration file.
Using these options classes can be combined with the `appsettings.json` way. Configuring an option property in the code overrides the value in the configuration file.

2
docs/en/Getting-Started-Running-Solution.md

@ -163,6 +163,8 @@ You can see the application APIs and test them here. Get [more info](https://swa
### Running the Blazor Application (Client Side)
Go to the Blazor project folder, open a command line terminal, type the `abp bundle -f` command (If the project was created by ABP Cli tool, you don't need to do this).
Ensure that the `.Blazor` project is the startup project and run the application.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.

4
docs/en/Migration-Guides/OpenIddict-Step-by-Step.md

@ -76,6 +76,8 @@ Use the `abp update` command to update your existing application. See [Upgrading
- Create a folder named *OpenIddict* under the Domain project and copy the [OpenIddictDataSeedContributor.cs](https://github.com/abpframework/abp-samples/blob/master/Ids2OpenId/src/Ids2OpenId.Domain/OpenIddict/OpenIddictDataSeedContributor.cs) under this folder. Rename all the `Ids2OpenId` with your project name.
- Delete *IdentityServer* folder that contains `IdentityServerDataSeedContributor.cs` which is no longer needed.
You can also create a project with the same name and copy the `OpenIddict` folder of the new project into your project.
### EntityFrameworkCore Layer
If you are using MongoDB, skip this step and check the *MongoDB* layer section.
@ -226,4 +228,4 @@ for creating the host builder.
## See Also
* [ABP Version 6.0 Migration Guide](Abp-6_0.md)
* [ABP Version 6.0 Migration Guide](Abp-6_0.md)

14
docs/en/Modules/OpenIddict.md

@ -330,7 +330,19 @@ public class MyClaimDestinationsProvider : IAbpOpenIddictClaimDestinationsProvid
{
public virtual Task SetDestinationsAsync(AbpOpenIddictClaimDestinationsProviderContext context)
{
// ...
foreach (var claim in context.Claims)
{
if (claim.Type == MyClaims.MyClaimsType)
{
claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken, OpenIddictConstants.Destinations.IdentityToken);
}
if (claim.Type == MyClaims.MyClaimsType2)
{
claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken);
}
}
return Task.CompletedTask;
}
}

8
docs/en/Startup-Templates/Application.md

@ -78,7 +78,7 @@ Based on the options you've specified, you will get a slightly different solutio
If you don't specify any additional options, you will have a solution as shown below:
![bookstore-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-v3.png)
![bookstore-rider-solution-v6](../images/solution-structure-solution-explorer-rider.png)
Projects are organized in `src` and `test` folders. `src` folder contains the actual application which is layered based on [DDD](../Domain-Driven-Design.md) principles as mentioned before.
@ -225,7 +225,7 @@ So, the resulting solution allows a 4-tiered deployment, by comparing to 3-tiere
The solution structure is shown below:
![bookstore-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-tiered.png)
![bookstore-rider-solution-v6](../images/bookstore-rider-solution-tiered.png)
As different from the default structure, two new projects come into play: `.AuthServer` & `.HttpApi.Host`.
@ -233,9 +233,9 @@ As different from the default structure, two new projects come into play: `.Auth
This project is used as an authentication server for other projects. `.Web` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the AuthServer. Then uses the access token to call the HTTP API server. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
![tiered-solution-applications](../images/tiered-solution-applications.png)
![tiered-solution-applications](../images/tiered-solution-applications-authserver.png)
ABP uses the open source [OpenIddcit](https://github.com/openiddict/openiddict-core) framework for the authentication between applications. See [OpenIddcit documentation](https://documentation.openiddict.com/) for details about the OpenIddict and OpenID Connect protocol.
ABP uses the [OpenIddict Module](../Modules/OpenIddict.md) that uses the open-source [OpenIddict-core](https://github.com/openiddict/openiddict-core) library for the authentication between applications. See [OpenIddict documentation](https://documentation.openiddict.com/) for details about the OpenIddict and OpenID Connect protocol.
It has its own `appsettings.json` that contains database connection and other configurations.

4
docs/en/docs-nav.json

@ -851,6 +851,10 @@
"text": "Page Header",
"path": "UI/Blazor/Page-Header.md"
},
{
"text": "Page Layout",
"path": "UI/Blazor/Page-Layout.md"
},
{
"text": "Toolbars",
"path": "UI/Blazor/Toolbars.md"

BIN
docs/en/images/bookstore-rider-solution-tiered.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

BIN
docs/en/images/solution-structure-solution-explorer-rider.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/en/images/tiered-solution-applications-authserver.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

183
docs/pt-BR/Validation.md

@ -1,3 +1,182 @@
## Validation
# Validação
Façam
O sistema de validação é utilizado para validar a entrada do usuário ou a requisição do cliente para uma ação de um controller ou por um serviço.
O ABP é compatível com o sistema de Validação de Modelos do ASP.NET Core e tudo escrito na [sua documentação](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation) é válido para aplicações baseadas no ABP. Logo, esse documento foca nas funcionalidades do ABP ao invés de repetir a documentação da Microsoft.
Além disso, o ABP adiciona os seguintes benefícios:
* Define `IValidationEnabled` para adicionar validação automática para uma classe qualquer. Como todos os [serviços de aplicação](Application-Services.md) já o implementam, eles também são validados automaticamente.
* Automaticamente traduz os erros de validação para os atributos de anotação de dados.
* Provê serviços extensíveis para validar a chamada de um método ou o estado de um objeto.
* Provê integração com o [FluentValidation](https://fluentvalidation.net/)
## Validando DTOs
Essa seção introduz brevemente o sistema de validação. Para mais detalhes, veja a [Documentação da Validação de Modelo em ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation).
### Atributos de anotação de dados
Utilizar anotações de dados é uma maneira simples de implementar uma validação formal para um [DTO](Data-Transfer-Objects.md) de uma forma declarativa. Exemplo:
````csharp
public class CreateBookDto
{
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
}
````
Quando você utilizar essa classe como parâmetro para um [serviço da aplicação](Application-Services.md) ou um controller, ele será automaticamente validado e a validação traduzida será lançada ([e tratada](Exception-Handling.md) pelo ABP framework).
### IValidatableObject
`IValidatableObject` pode ser implementado por um DTO para executar uma lógica customizada de validação. O `CreateBookDto` no exemplo a seguir implementa essa interface e verifica se o `Name` é igual a `Description` e retorna um erro de validação nesse caso.
````csharp
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Acme.BookStore
{
public class CreateBookDto : IValidatableObject
{
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
public IEnumerable<ValidationResult> Validate(
ValidationContext validationContext)
{
if (Name == Description)
{
yield return new ValidationResult(
"Name and Description can not be the same!",
new[] { "Name", "Description" }
);
}
}
}
}
````
#### Resolvendo um serviço.
Se você precisar resolver um serviço do [sistema de injeção de dependências](Dependency-Injection.md), você pode utilizar o objeto `ValidationContext`.
````csharp
var myService = validationContext.GetRequiredService<IMyService>();
````
> Enquanto resolver os serviços no método `Validate` permite várias possibilidades, não é um boa prática implementar sua lógica de validação do domínio nos DTOs. Mantenha os DTOs simples. Seu propósito é transferir dados (DTO: Data Transfer Object, ou Objeto de Transferência de Dados).
## Infraestrutura de Validação.
Essa seção explica alguns serviços adicionais fornecidos pelo ABP Framework.
### Interface IValidationEnabled
`IValidationEnabled` é um marcador vazio de interface que pode ser implementado por qualquer classe (registrada e resolvida a partir do [DI](Dependency-Injection.md)) para permitir que o ABP framework realize o sistema de validação para os métodos da classe. Por exemplo:
````csharp
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Validation;
namespace Acme.BookStore
{
public class MyService : ITransientDependency, IValidationEnabled
{
public virtual async Task DoItAsync(MyInput input)
{
//...
}
}
}
````
> O ABP framework utiliza o sistema de [Proxying Dinâmico / Interceptadores](Dynamic-Proxying-Interceptors.md) para realizar a validação. Para fazê-lo funcionar, seu método deve ser **virtual** ou seu serviço deve ser injetado e utilizado através de uma **interface** (como `IMyService`).
#### Habilitando e Desabilitando Validações
Você pode utilizar o `[DisableValidation]` e desabilitar a validação para métodos, classes e propriedades.
````csharp
[DisableValidation]
public Void MyMethod()
{
}
[DisableValidation]
public class InputClass
{
public string MyProperty { get; set; }
}
public class InputClass
{
[DisableValidation]
public string MyProperty { get; set; }
}
````
### AbpValidationException
Uma vez que o ABP determina um erro de validação, é lançada uma validação do tipo `AbpValidationException`. O código da sua aplicação poderá lançar o `AbpValidationException`, mas na maioria das vezes não será necessário.
* A propriedade `ValidationErrors` do `AbpValidationException` contem a lista com os erros de validação.
* O nível de log do `AbpValidationException` é definido como `Warning`. Todos os erros de validação são logados no [Sistema de Logging](Logging.md).
* `AbpValidationException` é tratado automaticamente pelo ABP framework e é convertido em um erro utilizável com o código de status HTTP 400. Veja a documentação de [Manipulação de Exceção](Exception-Handling.md) para mais informações.
## Tópicos Avançados
### IObjectValidator
Além da validação automática, você pode querer validar um objeto manualmente. Nesse caso, [injete](Dependency-Injection.md) e use o serviço `IObjectValidator`:
* O método `ValidateAsync` valida o objeto informado baseado nas regras de validação e lança uma `AbpValidationException` se não estiver em um estado válido.
* `GetErrorsAsync` não lança uma exceção, somente retorna os erros de validação.
`IObjectValidator` é implementado pelo `ObjectValidator` por padrão. `ObjectValidator` é extensível; você pode implementar a interface `IObjectValidationContributor` para contribuir com uma lógica customizada. Exemplo:
````csharp
public class MyObjectValidationContributor
: IObjectValidationContributor, ITransientDependency
{
public Task AddErrorsAsync(ObjectValidationContext context)
{
//Get the validating object
var obj = context.ValidatingObject;
//Add the validation errors if available
context.Errors.Add(...);
return Task.CompletedTask;
}
}
````
* Lembre-se de registrar sua classe no [DI](Dependency-Injection.md) (implementar `ITransientDependency` faz isso no exemplo anterior)
* ABP vai automaticamente descobrir sua classe e utilizá-la em qualquer tipo de validação de objetos (incluindo chamadas de métodos de validação automáticas).
### IMethodInvocationValidator
`IMethodInvocationValidator` é utilizado para validar a chamada de um método. Ele utiliza internamente o `IObjectValidator` para validar os objetos passados na chamada do método. Você normalmente não precisa deste serviço, já que ele é utilizado automaticamente pelo framework, mas você pode querer reutilizar ou substituir na sua aplicação em alguns casos raros.
## Integração com FluentValidation
O pacote Volo.Abp.FluentValidation integra a biblioteca FluentValidation com o sistema de validação (implementando o `IObjectValidationContributor`). Veja o [documento de Integração com o FluentValidation](FluentValidation.md) para mais informações.

7
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Components/LayoutHooks/LayoutHook.razor

@ -0,0 +1,7 @@
@if (LayoutHookViewModel.Hooks.Any())
{
foreach (var hook in LayoutHookViewModel.Hooks)
{
<DynamicComponent Type="@hook.ComponentType" />
}
}

39
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Components/LayoutHooks/LayoutHook.razor.cs

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Options;
using Volo.Abp.Ui.LayoutHooks;
namespace Volo.Abp.AspNetCore.Components.Web.Theming.Components.LayoutHooks;
public partial class LayoutHook : ComponentBase
{
[Parameter]
public string Name { get; set; }
[Parameter]
public string Layout { get; set; }
[Inject]
protected IOptions<AbpLayoutHookOptions> LayoutHookOptions { get; set; }
protected LayoutHookViewModel LayoutHookViewModel { get; private set; }
protected override Task OnInitializedAsync()
{
if (LayoutHookOptions.Value.Hooks.TryGetValue(Name, out var layoutHooks))
{
layoutHooks = layoutHooks
.WhereIf(string.IsNullOrWhiteSpace(Layout), x => x.Layout == Layout)
.ToList();
}
layoutHooks ??= new List<LayoutHookInfo>();
LayoutHookViewModel = new LayoutHookViewModel(layoutHooks.ToArray(), Layout);
return Task.CompletedTask;
}
}

6
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/StandardLayouts.cs

@ -0,0 +1,6 @@
namespace Volo.Abp.AspNetCore.Components.Web.Theming.Layout;
public static class StandardLayouts
{
public const string Application = "Application";
}

13
framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs

@ -1,8 +1,5 @@
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace Volo.Abp.AspNetCore.Mvc.Client;
@ -12,13 +9,5 @@ namespace Volo.Abp.AspNetCore.Mvc.Client;
)]
public class AbpAspNetCoreMvcClientModule : AbpModule
{
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context));
}
public async override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{
await context.ServiceProvider.GetRequiredService<MvcCachedApplicationConfigurationClient>().InitializeAsync();
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs

@ -30,11 +30,6 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu
Cache = cache;
}
public async Task InitializeAsync()
{
await GetAsync();
}
public async Task<ApplicationConfigurationDto> GetAsync()
{
var cacheKey = CreateCacheKey();
@ -45,7 +40,6 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu
return configuration;
}
configuration = await Cache.GetOrAddAsync(
cacheKey,
async () => await ApplicationConfigurationAppService.GetAsync(),

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

@ -5,5 +5,11 @@ public enum AbpModalSize
Default,
Small,
Large,
ExtraLarge
ExtraLarge,
Fullscreen,
FullscreenSmDown,
FullscreenMdDown,
FullscreenLgDown,
FullscreenXlDown,
FullscreenXxlDown
}

12
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalSizeExtensions.cs

@ -14,6 +14,18 @@ public static class AbpModalSizeExtensions
return "modal-xl";
case AbpModalSize.Default:
return "";
case AbpModalSize.Fullscreen:
return "modal-fullscreen";
case AbpModalSize.FullscreenSmDown:
return "modal-fullscreen-sm-down";
case AbpModalSize.FullscreenMdDown:
return "modal-fullscreen-md-down";
case AbpModalSize.FullscreenLgDown:
return "modal-fullscreen-lg-down";
case AbpModalSize.FullscreenXlDown:
return "modal-fullscreen-xl-down";
case AbpModalSize.FullscreenXxlDown:
return "modal-fullscreen-xxl-down";
default:
throw new AbpException($"Unknown {nameof(AbpModalSize)}: {size}");
}

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/Default.cshtml

@ -1,5 +1,4 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook
@model LayoutHookViewModel
@model Volo.Abp.Ui.LayoutHooks.LayoutHookViewModel
@if (Model.Hooks.Any())
{
foreach (var hook in Model.Hooks)

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/LayoutHookViewComponent.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Volo.Abp.Ui.LayoutHooks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook;

8
framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using Autofac.Core;
using Autofac.Extras.DynamicProxy;
using Volo.Abp.Autofac;
using Volo.Abp.Castle.DynamicProxy;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Modularity;
@ -67,10 +68,11 @@ public static class AbpRegistrationBuilderExtensions
Type implementationType)
where TActivatorData : ReflectionActivatorData
{
//Enable Property Injection only for types in an assembly containing an AbpModule
if (moduleContainer.Modules.Any(m => m.Assembly == implementationType.Assembly))
// Enable Property Injection only for types in an assembly containing an AbpModule and without a DisablePropertyInjection attribute on class or properties.
if (moduleContainer.Modules.Any(m => m.Assembly == implementationType.Assembly) &&
implementationType.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).IsNullOrEmpty())
{
registrationBuilder = registrationBuilder.PropertiesAutowired();
registrationBuilder = registrationBuilder.PropertiesAutowired(new AbpPropertySelector(false));
}
return registrationBuilder;

21
framework/src/Volo.Abp.Autofac/Volo/Abp/Autofac/AbpPropertySelector.cs

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Reflection;
using Autofac.Core;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Autofac;
public class AbpPropertySelector : DefaultPropertySelector
{
public AbpPropertySelector(bool preserveSetValues)
: base(preserveSetValues)
{
}
public override bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
return propertyInfo.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).IsNullOrEmpty() &&
base.InjectProperty(propertyInfo, instance);
}
}

8
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs

@ -74,9 +74,9 @@ public class AuthService : IAuthService, ITransientDependency
{
var configuration = new IdentityClientConfiguration(
CliUrls.AccountAbpIo,
"role email abpio abpio_www abpio_commercial offline_access",
"abpio offline_access",
"abp-cli",
"1q2w3e*",
null,
OidcConstants.GrantTypes.Password,
userName,
password
@ -96,9 +96,9 @@ public class AuthService : IAuthService, ITransientDependency
{
var configuration = new IdentityClientConfiguration(
CliUrls.AccountAbpIo,
"role email abpio abpio_www abpio_commercial openid offline_access",
"abpio offline_access",
"abp-cli",
"1q2w3e*",
null,
OidcConstants.GrantTypes.DeviceCode
);

40
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Bundling;
using Volo.Abp.Cli.Commands.Services;
using Volo.Abp.Cli.LIbs;
using Volo.Abp.Cli.ProjectBuilding;
@ -26,23 +27,35 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien
protected TemplateProjectBuilder TemplateProjectBuilder { get; }
public ITemplateInfoProvider TemplateInfoProvider { get; }
public NewCommand(TemplateProjectBuilder templateProjectBuilder
, ITemplateInfoProvider templateInfoProvider,
ConnectionStringProvider connectionStringProvider,
public NewCommand(
ConnectionStringProvider connectionStringProvider,
SolutionPackageVersionFinder solutionPackageVersionFinder,
ICmdHelper cmdHelper,
IInstallLibsService installLibsService,
AngularPwaSupportAdder angularPwaSupportAdder,
IInstallLibsService installLibsService,
CliService cliService,
AngularPwaSupportAdder angularPwaSupportAdder,
InitialMigrationCreator initialMigrationCreator,
ThemePackageAdder themePackageAdder,
ILocalEventBus eventBus)
: base(connectionStringProvider, solutionPackageVersionFinder, cmdHelper, installLibsService, angularPwaSupportAdder, initialMigrationCreator, themePackageAdder, eventBus)
ThemePackageAdder themePackageAdder,
ILocalEventBus eventBus,
IBundlingService bundlingService,
ITemplateInfoProvider templateInfoProvider,
TemplateProjectBuilder templateProjectBuilder) :
base(connectionStringProvider,
solutionPackageVersionFinder,
cmdHelper,
installLibsService,
cliService,
angularPwaSupportAdder,
initialMigrationCreator,
themePackageAdder,
eventBus,
bundlingService)
{
TemplateProjectBuilder = templateProjectBuilder;
TemplateInfoProvider = templateInfoProvider;
TemplateProjectBuilder = templateProjectBuilder;
}
public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
var projectName = NamespaceHelper.NormalizeNamespace(commandLineArgs.Target);
@ -72,7 +85,7 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien
Logger.LogInformation("Tiered: yes");
}
var projectArgs = GetProjectBuildArgs(commandLineArgs, template, projectName);
var projectArgs = await GetProjectBuildArgsAsync(commandLineArgs, template, projectName);
var result = await TemplateProjectBuilder.BuildAsync(
projectArgs
@ -92,6 +105,8 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien
await RunInstallLibsForWebTemplateAsync(projectArgs);
}
await RunBundleForBlazorWasmTemplateAsync(projectArgs);
await ConfigurePwaSupportForAngular(projectArgs);
OpenRelatedWebPage(projectArgs, template, isTiered, commandLineArgs);
@ -155,5 +170,4 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien
{
return "Generate a new solution based on the ABP startup templates.";
}
}

43
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs

@ -10,6 +10,7 @@ using NuGet.Versioning;
using NUglify.Helpers;
using Volo.Abp.Cli.ProjectModification;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Bundling;
using Volo.Abp.Cli.Commands.Services;
using Volo.Abp.Cli.LIbs;
using Volo.Abp.Cli.ProjectBuilding;
@ -25,10 +26,12 @@ namespace Volo.Abp.Cli.Commands;
public abstract class ProjectCreationCommandBase
{
private readonly IBundlingService _bundlingService;
public ConnectionStringProvider ConnectionStringProvider { get; }
public SolutionPackageVersionFinder SolutionPackageVersionFinder { get; }
public ICmdHelper CmdHelper { get; }
public IInstallLibsService InstallLibsService { get; }
public CliService CliService { get; }
public AngularPwaSupportAdder AngularPwaSupportAdder { get; }
public InitialMigrationCreator InitialMigrationCreator { get; }
public ILocalEventBus EventBus { get; }
@ -41,15 +44,19 @@ public abstract class ProjectCreationCommandBase
SolutionPackageVersionFinder solutionPackageVersionFinder,
ICmdHelper cmdHelper,
IInstallLibsService installLibsService,
CliService cliService,
AngularPwaSupportAdder angularPwaSupportAdder,
InitialMigrationCreator initialMigrationCreator,
ThemePackageAdder themePackageAdder,
ILocalEventBus eventBus)
ILocalEventBus eventBus,
IBundlingService bundlingService)
{
_bundlingService = bundlingService;
ConnectionStringProvider = connectionStringProvider;
SolutionPackageVersionFinder = solutionPackageVersionFinder;
CmdHelper = cmdHelper;
InstallLibsService = installLibsService;
CliService = cliService;
AngularPwaSupportAdder = angularPwaSupportAdder;
InitialMigrationCreator = initialMigrationCreator;
EventBus = eventBus;
@ -58,7 +65,7 @@ public abstract class ProjectCreationCommandBase
Logger = NullLogger<NewCommand>.Instance;
}
protected ProjectBuildArgs GetProjectBuildArgs(CommandLineArgs commandLineArgs, string template, string projectName)
protected async Task<ProjectBuildArgs> GetProjectBuildArgsAsync(CommandLineArgs commandLineArgs, string template, string projectName)
{
var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long);
@ -71,6 +78,15 @@ public abstract class ProjectCreationCommandBase
if (preview)
{
Logger.LogInformation("Preview: yes");
var cliVersion = await CliService.GetCurrentCliVersionAsync(typeof(CliService).Assembly);
if (!cliVersion.IsPrerelease)
{
throw new CliUsageException(
"You can only create a new preview solution with preview CLI version." +
" Update your ABP CLI to the preview version.");
}
}
var pwa = commandLineArgs.Options.ContainsKey(Options.ProgressiveWebApp.Short);
@ -317,14 +333,14 @@ public abstract class ProjectCreationCommandBase
var tieredYesNo = tiered ? "yes" : "no";
var url = $"https://{urlPrefix}.abp.io/project-created-success?ui={uiFramework:g}&db={databaseProvider:g}&tiered={tieredYesNo}";
CmdHelper.OpenWebPage(url);
CmdHelper.Open(url);
}
protected void OpenMicroserviceDocumentPage()
{
var url = "https://docs.abp.io/en/commercial/latest/startup-templates/microservice/index";
CmdHelper.OpenWebPage(url);
CmdHelper.Open(url);
}
protected bool GetCreateSolutionFolderPreference(CommandLineArgs commandLineArgs)
@ -392,6 +408,25 @@ public abstract class ProjectCreationCommandBase
}
}
protected async Task RunBundleForBlazorWasmTemplateAsync(ProjectBuildArgs projectArgs)
{
if (AppTemplateBase.IsAppTemplate(projectArgs.TemplateName) && projectArgs.UiFramework == UiFramework.Blazor)
{
Logger.LogInformation("Generating bundles for Blazor Wasm...");
await EventBus.PublishAsync(new ProjectCreationProgressEvent
{
Message = "Generating bundles for Blazor Wasm"
}, false);
var directory = Path.GetDirectoryName(
Directory.GetFiles(projectArgs.OutputFolder, "*.Blazor.csproj", SearchOption.AllDirectories).First()
);
await _bundlingService.BundleAsync(directory, true);
}
}
protected async Task CreateInitialMigrationsAsync(ProjectBuildArgs projectArgs)
{
if (projectArgs.DatabaseProvider == DatabaseProvider.MongoDb)

12
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/CommercialPackages.cs

@ -14,6 +14,16 @@ internal static class CommercialPackages
public static bool IsCommercial(string packageId)
{
return Packages.Contains(packageId.ToLowerInvariant());
return Packages.Contains(packageId.ToLowerInvariant()) || IsLeptonXPackage(packageId);
}
private static bool IsLeptonXPackage(string packageId)
{
return !IsLeptonXLitePackage(packageId) && packageId.Contains("LeptonX");
}
private static bool IsLeptonXLitePackage(string packageId)
{
return packageId.Contains("LeptonXLite");
}
}

7
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/CreateAppSettingsSecretsStep.cs

@ -11,7 +11,7 @@ public class CreateAppSettingsSecretsStep : ProjectBuildPipelineStep
public override void Execute(ProjectBuildContext context)
{
var appSettingsFiles = context.Files
.Where(x => x.Name.EndsWith(CliConsts.AppSettingsJsonFileName) && NotBlazorWasmProject(x.Name))
.Where(x => x.Name.EndsWith(CliConsts.AppSettingsJsonFileName) && NotBlazorWasmProject(x.Name) && NotInDockerFiles(x.Name))
.ToList();
if (!appSettingsFiles.Any())
@ -66,6 +66,11 @@ public class CreateAppSettingsSecretsStep : ProjectBuildPipelineStep
return !fileName.Contains("Blazor/wwwroot") && !fileName.Contains("Blazor.Host/wwwroot");
}
private static bool NotInDockerFiles(string fileName)
{
return !fileName.Contains("etc/docker/");
}
private static string ReplaceAppSettingsSecretsPlaceholder(string content)
{
var path = string.Empty;

17
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs

@ -37,6 +37,7 @@ public abstract class AppTemplateBase : TemplateInfo
ConfigureTieredArchitecture(context, steps);
ConfigurePublicWebSite(context, steps);
ConfigureTheme(context, steps);
ConfigureVersion(context, steps);
RemoveUnnecessaryPorts(context, steps);
RandomizeSslPorts(context, steps);
RandomizeStringEncryption(context, steps);
@ -193,12 +194,16 @@ public abstract class AppTemplateBase : TemplateInfo
return;
}
if (context.BuildArgs.Theme != Theme.NotSpecified)
{
context.Symbols.Add(context.BuildArgs.Theme.Value.ToString().ToUpper());
}
if (context.BuildArgs.Theme == Theme.LeptonX)
{
context.Symbols.Add("LEPTONX");
steps.Add(new ChangeThemeStyleStep());
}
if (IsDefaultThemeForTemplate(context.BuildArgs.Theme.Value))
{
return;
@ -489,6 +494,14 @@ public abstract class AppTemplateBase : TemplateInfo
steps.Add(new RemoveUnnecessaryPortsStep());
}
protected void ConfigureVersion(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
if (context.BuildArgs.Version == null || SemanticVersion.Parse(context.BuildArgs.Version) >= SemanticVersion.Parse("6.0.0-rc.1"))
{
context.Symbols.Add("newer-than-6.0");
}
}
protected void RandomizeSslPorts(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
if (context.BuildArgs.ExtraProperties.ContainsKey("no-random-port"))

6
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs

@ -35,10 +35,14 @@ public abstract class MicroserviceTemplateBase : TemplateInfo
{
return;
}
if (context.BuildArgs.Theme != Theme.NotSpecified)
{
context.Symbols.Add(context.BuildArgs.Theme.Value.ToString().ToUpper());
}
if (context.BuildArgs.Theme == Theme.LeptonX)
{
context.Symbols.Add("LEPTONX");
steps.Add(new ChangeThemeStyleStep());
return;
}

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs

@ -153,7 +153,7 @@ public class SolutionModuleAdder : ITransientDependency
var documentationLink = module.GetFirstDocumentationLinkOrNull();
if (documentationLink != null)
{
CmdHelper.OpenWebPage(documentationLink);
CmdHelper.Open(documentationLink);
}
return module;

10
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/CmdHelper.cs

@ -18,20 +18,20 @@ public class CmdHelper : ICmdHelper, ITransientDependency
CliOptions = cliOptions.Value;
}
public void OpenWebPage(string url)
public void Open(string pathOrUrl)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
url = url.Replace("&", "^&");
Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true });
pathOrUrl = pathOrUrl.Replace("&", "^&");
Process.Start(new ProcessStartInfo("cmd", $"/c start {pathOrUrl}") { CreateNoWindow = true });
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Process.Start("xdg-open", url);
Process.Start("xdg-open", pathOrUrl);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
Process.Start("open", url);
Process.Start("open", pathOrUrl);
}
}

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/ICmdHelper.cs

@ -4,7 +4,7 @@ namespace Volo.Abp.Cli.Utils;
public interface ICmdHelper
{
void OpenWebPage(string url);
void Open(string pathOrUrl);
void Run(string file, string arguments);

9
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DisablePropertyInjectionAttribute.cs

@ -0,0 +1,9 @@
using System;
namespace Volo.Abp.DependencyInjection;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
public class DisablePropertyInjectionAttribute : Attribute
{
}

2
framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/ConsumerPool.cs

@ -37,7 +37,7 @@ public class ConsumerPool : IConsumerPool, ISingletonDependency
return Consumers.GetOrAdd(
connectionName, connection => new Lazy<IConsumer<string, byte[]>>(() =>
{
var config = new ConsumerConfig(Options.Connections.GetOrDefault(connection))
var config = new ConsumerConfig(Options.Connections.GetOrDefault(connection).ToDictionary(k => k.Key, v => v.Value))
{
GroupId = groupId,
EnableAutoCommit = false

2
framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/ProducerPool.cs

@ -39,7 +39,7 @@ public class ProducerPool : IProducerPool, ISingletonDependency
return Producers.GetOrAdd(
connectionName, connection => new Lazy<IProducer<string, byte[]>>(() =>
{
var producerConfig = new ProducerConfig(Options.Connections.GetOrDefault(connection));
var producerConfig = new ProducerConfig(Options.Connections.GetOrDefault(connection).ToDictionary(k => k.Key, v => v.Value));
Options.ConfigureProducer?.Invoke(producerConfig);
return new ProducerBuilder<string, byte[]>(producerConfig).Build();

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/AbpLayoutHookOptions.cs → framework/src/Volo.Abp.UI/Volo/Abp/Ui/LayoutHooks/AbpLayoutHookOptions.cs

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook;
namespace Volo.Abp.Ui.LayoutHooks;
public class AbpLayoutHookOptions
{

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/LayoutHookInfo.cs → framework/src/Volo.Abp.UI/Volo/Abp/Ui/LayoutHooks/LayoutHookInfo.cs

@ -1,11 +1,11 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook;
namespace Volo.Abp.Ui.LayoutHooks;
public class LayoutHookInfo
{
/// <summary>
/// ViewComponent type.
/// Component type.
/// </summary>
public Type ComponentType { get; }

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/LayoutHookViewModel.cs → framework/src/Volo.Abp.UI/Volo/Abp/Ui/LayoutHooks/LayoutHookViewModel.cs

@ -1,4 +1,4 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook;
namespace Volo.Abp.Ui.LayoutHooks;
public class LayoutHookViewModel
{

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/LayoutHooks.cs → framework/src/Volo.Abp.UI/Volo/Abp/Ui/LayoutHooks/LayoutHooks.cs

@ -1,4 +1,4 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook;
namespace Volo.Abp.Ui.LayoutHooks;
public static class LayoutHooks
{

62
framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/DependencyInjection_Tests.cs

@ -47,19 +47,36 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest<Dep
[Fact]
public void Should_Inject_Services_As_Properties()
{
GetRequiredService<ServiceWithPropertyInject>().ProperyInjectedService.ShouldNotBeNull();
GetRequiredService<ServiceWithPropertyInject>().PropertyInjectedService.ShouldNotBeNull();
}
[Fact]
public void Should_Inject_Services_As_Properties_For_Generic_Classes()
{
GetRequiredService<GenericServiceWithPropertyInject<int>>().ProperyInjectedService.ShouldNotBeNull();
GetRequiredService<GenericServiceWithPropertyInject<int>>().PropertyInjectedService.ShouldNotBeNull();
}
[Fact]
public void Should_Inject_Services_As_Properties_For_Generic_Concrete_Classes()
{
GetRequiredService<ConcreteGenericServiceWithPropertyInject>().ProperyInjectedService.ShouldNotBeNull();
GetRequiredService<ConcreteGenericServiceWithPropertyInject>().PropertyInjectedService.ShouldNotBeNull();
}
[Fact]
public void Should_Not_Inject_Services_As_Properties_When_Class_With_DisablePropertyInjection()
{
GetRequiredService<DisablePropertyInjectionOnClass>().PropertyInjectedService.ShouldBeNull();
GetRequiredService<GenericServiceWithDisablePropertyInjectionOnClass<string>>().PropertyInjectedService.ShouldBeNull();
}
[Fact]
public void Should_Not_Inject_Services_As_Properties_When_Property_With_DisablePropertyInjection()
{
GetRequiredService<DisablePropertyInjectionOnProperty>().PropertyInjectedService.ShouldNotBeNull();
GetRequiredService<DisablePropertyInjectionOnProperty>().DisablePropertyInjectionService.ShouldBeNull();
GetRequiredService<GenericServiceWithDisablePropertyInjectionOnProperty<string>>().PropertyInjectedService.ShouldNotBeNull();
GetRequiredService<GenericServiceWithDisablePropertyInjectionOnProperty<string>>().DisablePropertyInjectionService.ShouldBeNull();
}
[Fact]
@ -145,18 +162,19 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest<Dep
context.Services.AddType<ServiceWithPropertyInject>();
context.Services.AddType<MySingletonExposingMultipleServices>();
context.Services.AddTransient(typeof(GenericServiceWithPropertyInject<>));
context.Services.AddTransient(typeof(ConcreteGenericServiceWithPropertyInject));
context.Services.AddTransient(typeof(GenericServiceWithDisablePropertyInjectionOnClass<>));
context.Services.AddTransient(typeof(GenericServiceWithDisablePropertyInjectionOnProperty<>));
}
}
public class ServiceWithPropertyInject : ITransientDependency
{
public MyEmptyTransientService ProperyInjectedService { get; set; }
public MyEmptyTransientService PropertyInjectedService { get; set; }
}
public class GenericServiceWithPropertyInject<T> : ITransientDependency
{
public MyEmptyTransientService ProperyInjectedService { get; set; }
public MyEmptyTransientService PropertyInjectedService { get; set; }
public T Value { get; set; }
}
@ -165,4 +183,36 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest<Dep
{
}
[DisablePropertyInjection]
public class DisablePropertyInjectionOnClass : ITransientDependency
{
public MyEmptyTransientService PropertyInjectedService { get; set; }
}
public class DisablePropertyInjectionOnProperty : ITransientDependency
{
public MyEmptyTransientService PropertyInjectedService { get; set; }
[DisablePropertyInjection]
public MyEmptyTransientService DisablePropertyInjectionService { get; set; }
}
[DisablePropertyInjection]
public class GenericServiceWithDisablePropertyInjectionOnClass<T> : ITransientDependency
{
public MyEmptyTransientService PropertyInjectedService { get; set; }
public T Value { get; set; }
}
public class GenericServiceWithDisablePropertyInjectionOnProperty<T> : ITransientDependency
{
public MyEmptyTransientService PropertyInjectedService { get; set; }
[DisablePropertyInjection]
public MyEmptyTransientService DisablePropertyInjectionService { get; set; }
public T Value { get; set; }
}
}

5
modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/MainLayout.razor

@ -1,4 +1,5 @@
@using Volo.Abp.AspNetCore.Components.Web.Theming.Components;
@using Volo.Abp.Ui.LayoutHooks
@using Volo.Abp.AspNetCore.Components.Web.Theming.Layout
@inherits LayoutComponentBase
<nav class="navbar navbar-expand-md navbar-dark bg-dark shadow-sm flex-column flex-md-row mb-4" id="main-navbar" style="min-height: 4rem;">
<div class="container">
@ -18,7 +19,9 @@
</nav>
<div class="container">
<PageAlert />
<LayoutHook Name="@LayoutHooks.Body.First" Layout="@StandardLayouts.Application" />
@Body
<LayoutHook Name="@LayoutHooks.Body.Last" Layout="@StandardLayouts.Application" />
<DynamicLayoutComponent />
<UiMessageAlert />
<UiNotificationAlert />

1
modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml

@ -12,6 +12,7 @@
@using Volo.Abp.MultiTenancy
@using Volo.Abp.Localization
@using Volo.Abp.Ui.Branding
@using Volo.Abp.Ui.LayoutHooks
@inject IBrandingProvider BrandingProvider
@inject IOptions<AbpMultiTenancyOptions> MultiTenancyOptions
@inject ICurrentTenant CurrentTenant

1
modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml

@ -8,6 +8,7 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles
@using Volo.Abp.Localization
@using Volo.Abp.Ui.Branding
@using Volo.Abp.Ui.LayoutHooks
@inject IBrandingProvider BrandingProvider
@inject IPageLayout PageLayout
@{

1
modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml

@ -7,6 +7,7 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles
@using Volo.Abp.Localization
@using Volo.Abp.Ui.Branding
@using Volo.Abp.Ui.LayoutHooks
@inject IBrandingProvider BrandingProvider
@inject IPageLayout PageLayout
@{

2
modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs

@ -36,7 +36,6 @@ using Volo.Abp.Threading;
using Volo.Abp.VirtualFileSystem;
using Volo.CmsKit.Admin.Web;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.EntityFrameworkCore;
using Volo.CmsKit.MediaDescriptors;
using Volo.CmsKit.MultiTenancy;
@ -45,6 +44,7 @@ using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Web;
using Volo.CmsKit.Web.Contents;
namespace Volo.CmsKit;

9
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/IContentAdminAppService.cs

@ -1,9 +0,0 @@
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Volo.CmsKit.Admin.Contents;
public interface IContentAdminAppService : IApplicationService
{
Task<ListResultDto<ContentWidgetDto>> GetWidgetsAsync();
}

4
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo.CmsKit.Admin.Application.csproj

@ -13,4 +13,8 @@
<ProjectReference Include="..\Volo.CmsKit.Common.Application\Volo.CmsKit.Common.Application.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Volo\CmsKit\Admin\Contents\" />
</ItemGroup>
</Project>

32
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs

@ -1,32 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.Application.Dtos;
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.Admin.Contents;
public class ContentAdminAppService : CmsKitAdminAppServiceBase, IContentAdminAppService
{
private readonly CmsKitContentWidgetOptions _options;
public ContentAdminAppService(IOptions<CmsKitContentWidgetOptions> options)
{
_options = options.Value;
}
public virtual Task<ListResultDto<ContentWidgetDto>> GetWidgetsAsync()
{
return Task.FromResult(new ListResultDto<ContentWidgetDto>()
{
Items = _options.WidgetConfigs
.Select(n =>
new ContentWidgetDto
{
Key = n.Key,
Details = new WidgetDetailDto() { EditorComponentName = n.Value.EditorComponentName, Name = n.Value.Name },
}).ToList()
});
}
}

22
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/ContentAdminClientProxy.Generated.cs

@ -1,22 +0,0 @@
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Modeling;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.CmsKit.Admin.Contents;
// ReSharper disable once CheckNamespace
namespace Volo.CmsKit.Admin.Contents.ClientProxies;
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IContentAdminAppService), typeof(ContentAdminClientProxy))]
public partial class ContentAdminClientProxy : ClientProxyBase<IContentAdminAppService>, IContentAdminAppService
{
public virtual async Task<ListResultDto<ContentWidgetDto>> GetWidgetsAsync()
{
return await RequestAsync<ListResultDto<ContentWidgetDto>>(nameof(GetWidgetsAsync));
}
}

7
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/ContentAdminClientProxy.cs

@ -1,7 +0,0 @@
// This file is part of ContentAdminClientProxy, you can customize it here
// ReSharper disable once CheckNamespace
namespace Volo.CmsKit.Admin.Contents.ClientProxies;
public partial class ContentAdminClientProxy
{
}

29
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/cms-kit-admin-generate-proxy.json

@ -1191,35 +1191,6 @@
}
}
},
"Volo.CmsKit.Admin.Contents.ContentAdminController": {
"controllerName": "ContentAdmin",
"controllerGroupName": "ContentAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Admin.Contents.ContentAdminController",
"interfaces": [
{
"type": "Volo.CmsKit.Admin.Contents.IContentAdminAppService"
}
],
"actions": {
"GetWidgetsAsync": {
"uniqueName": "GetWidgetsAsync",
"name": "GetWidgetsAsync",
"httpMethod": "GET",
"url": "api/cms-kit-admin/contents",
"supportedVersions": [],
"parametersOnMethod": [],
"parameters": [],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.ListResultDto<Volo.CmsKit.Admin.Contents.ContentWidgetDto>",
"typeSimple": "Volo.Abp.Application.Dtos.ListResultDto<Volo.CmsKit.Admin.Contents.ContentWidgetDto>"
},
"allowAnonymous": null,
"implementFrom": "Volo.CmsKit.Admin.Contents.IContentAdminAppService"
}
}
},
"Volo.CmsKit.Admin.Comments.CommentAdminController": {
"controllerName": "CommentAdmin",
"controllerGroupName": "CommentAdmin",

25
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Contents/ContentAdminController.cs

@ -1,25 +0,0 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Admin.Contents;
[RemoteService(Name = CmsKitAdminRemoteServiceConsts.RemoteServiceName)]
[Area(CmsKitAdminRemoteServiceConsts.ModuleName)]
[Route("api/cms-kit-admin/contents")]
public class ContentAdminController : CmsKitAdminController, IContentAdminAppService
{
protected IContentAdminAppService ContentAdminAppService { get; }
public ContentAdminController(IContentAdminAppService contentAdminAppService)
{
ContentAdminAppService = contentAdminAppService;
}
[HttpGet]
public virtual Task<ListResultDto<ContentWidgetDto>> GetWidgetsAsync()
{
return ContentAdminAppService.GetWidgetsAsync();
}
}

47
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Contents/AddWidgetModal.cshtml

@ -14,34 +14,39 @@
@section scripts {
<abp-script src="/Pages/CmsKit/Contents/addWidgetModal.js" />
}
@{
bool isDefinedWidget = Model.Widgets.Count() > 1;
}
<abp-modal size="Large" id="addWidgetModal">
<abp-modal-header title="@L["AddWidget"].Value"></abp-modal-header>
<abp-modal-body>
<div class="container">
@if (isDefinedWidget)
{
<abp-select asp-for="ViewModel.Widget" />
<abp-modal size="Large" id="addWidgetModal">
<abp-modal-header title="@L["AddWidget"].Value"></abp-modal-header>
<abp-modal-body>
<div class="container">
@if (Model.Widgets.Count() > 1)
foreach (var item in Model.ViewModel.Details)
{
<abp-select asp-for="ViewModel.Widget" />
foreach (var item in Model.ViewModel.Details)
if (item.EditorComponentName is not null)
{
if (item.EditorComponentName is not null)
{
<div hidden id="editor-@item.Name">
<form data-check-form-on-close="false">
@await Component.InvokeAsync(@item.EditorComponentName)
</form>
</div>
}
<div hidden id="editor-@item.Name">
<form data-check-form-on-close="false">
@await Component.InvokeAsync(@item.EditorComponentName)
</form>
</div>
}
}
else
{
<p>@L["PleaseConfigureWidgets"].Value</p>
}
}
else
{
<p>@L["PleaseConfigureWidgets"].Value</p>
}
</div>
</abp-modal-body>
<abp-modal-footer>
<button class="btn btn-primary float-end save-changes" type="submit">@L["Add"]</button>
@if (isDefinedWidget)
{
<button class="btn btn-primary float-end save-changes" type="submit">@L["Add"]</button>
}
</abp-modal-footer>
</abp-modal>

22
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Contents/AddWidgetModal.cshtml.cs

@ -3,38 +3,48 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.CmsKit.Admin.Contents;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Web.Contents;
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents;
public class AddWidgetModal : AbpPageModel
{
protected IContentAdminAppService ContentAdminAppService { get; }
[BindProperty]
public ContentViewModel ViewModel { get; set; }
public List<SelectListItem> Widgets { get; set; } = new();
public AddWidgetModal(IContentAdminAppService contentAdminAppService)
private readonly CmsKitContentWidgetOptions _options;
public AddWidgetModal(IOptions<CmsKitContentWidgetOptions> options)
{
ContentAdminAppService = contentAdminAppService;
_options = options.Value;
}
public async Task OnGetAsync()
{
var widgets = await ContentAdminAppService.GetWidgetsAsync();
var widgets = _options.WidgetConfigs
.Select(n =>
new ContentWidgetDto
{
Key = n.Key,
Details = new WidgetDetailDto() { EditorComponentName = n.Value.EditorComponentName, Name = n.Value.Name },
}).ToList();
ViewModel = new ContentViewModel()
{
Details = widgets.Items.Select(p => p.Details).ToList()
Details = widgets.Select(p => p.Details).ToList()
};
Widgets = new List<SelectListItem>() { new(string.Empty, string.Empty) };
Widgets.AddRange(widgets
.Items
.Select(w => new SelectListItem(w.Key, w.Details.Name))
.ToList());
}

15
modules/cms-kit/src/Volo.CmsKit.Admin.Web/wwwroot/client-proxies/cms-kit-admin-proxy.js

@ -247,21 +247,6 @@
})();
// controller volo.cmsKit.admin.contents.contentAdmin
(function(){
abp.utils.createNamespace(window, 'volo.cmsKit.admin.contents.contentAdmin');
volo.cmsKit.admin.contents.contentAdmin.getWidgets = function(ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/cms-kit-admin/contents',
type: 'GET'
}, ajaxParams));
};
})();
// controller volo.cmsKit.admin.comments.commentAdmin
(function(){

5
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/BlogPostCommonDto.cs

@ -1,12 +1,11 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.Contents;
[Serializable]
public class BlogPostCommonDto : AuditedEntityDto<Guid>, IContent
public class BlogPostCommonDto : AuditedEntityDto<Guid>
{
public Guid BlogId { get; set; }
@ -16,8 +15,6 @@ public class BlogPostCommonDto : AuditedEntityDto<Guid>, IContent
public string ShortDescription { get; set; }
public List<ContentFragment> ContentFragments { get; set; }
public string Content { get; set; }
public Guid? CoverImageMediaId { get; set; }

10
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/IContentAppService.cs

@ -1,10 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace Volo.CmsKit.Contents;
public interface IContentAppService : IApplicationService
{
Task<List<ContentFragment>> ParseAsync(string content);
}

4
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/PageDto.cs

@ -5,7 +5,7 @@ using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Contents;
[Serializable]
public class PageDto : EntityDto<Guid>, IContent
public class PageDto : EntityDto<Guid>
{
public string Title { get; set; }
@ -13,8 +13,6 @@ public class PageDto : EntityDto<Guid>, IContent
public string Content { get; set; }
public List<ContentFragment> ContentFragments { get; set; }
public string Script { get; set; }
public string Style { get; set; }

22
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentAppService.cs

@ -1,22 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.GlobalFeatures;
using Volo.CmsKit.GlobalFeatures;
namespace Volo.CmsKit.Contents;
[RequiresGlobalFeature(typeof(PagesFeature))]
public class ContentAppService : CmsKitAppServiceBase, IContentAppService
{
protected ContentParser ContentParser { get; }
public ContentAppService(ContentParser contentParser)
{
ContentParser = contentParser;
}
public async Task<List<ContentFragment>> ParseAsync(string content)
{
return await ContentParser.ParseAsync(content);
}
}

9
modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/ContentPreview/ContentPreviewViewComponent.cs

@ -6,21 +6,22 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Web.Contents;
namespace Volo.CmsKit.Web.Pages.CmsKit.Components.ContentPreview;
public class ContentPreviewViewComponent : AbpViewComponent
{
protected IContentAppService ContentAppService { get; }
protected ContentParser ContentParser { get; }
public ContentPreviewViewComponent(IContentAppService contentAppService)
public ContentPreviewViewComponent(ContentParser contentParser)
{
ContentAppService = contentAppService;
ContentParser = contentParser;
}
public virtual async Task<IViewComponentResult> InvokeAsync(string content)
{
var fragments = await ContentAppService.ParseAsync(content);
var fragments = await ContentParser.ParseAsync(content);
return View("~/Pages/CmsKit/Components/ContentPreview/Default.cshtml", new DefaultContentDto
{

2
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/CmsKitContentWidgetOptions.cs → modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Contents/CmsKitContentWidgetOptions.cs

@ -1,6 +1,6 @@
using System.Collections.Generic;
namespace Volo.CmsKit.Contents;
namespace Volo.CmsKit.Web.Contents;
public class CmsKitContentWidgetOptions
{

3
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentParser.cs → modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Contents/ContentParser.cs

@ -5,9 +5,10 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.CmsKit.Contents;
using static Volo.CmsKit.Contents.ContentConsts;
namespace Volo.CmsKit.Contents;
namespace Volo.CmsKit.Web.Contents;
public class ContentParser : ITransientDependency
{

2
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentWidgetConfig.cs → modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Contents/ContentWidgetConfig.cs

@ -1,4 +1,4 @@
namespace Volo.CmsKit.Contents;
namespace Volo.CmsKit.Web.Contents;
public class ContentWidgetConfig
{

11
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs

@ -6,6 +6,7 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Features;
using Volo.Abp.GlobalFeatures;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Features;
using Volo.CmsKit.GlobalFeatures;
@ -20,16 +21,13 @@ public class BlogPostPublicAppService : CmsKitPublicAppServiceBase, IBlogPostPub
protected IBlogRepository BlogRepository { get; }
protected IBlogPostRepository BlogPostRepository { get; }
protected ContentParser ContentParser { get; }
public BlogPostPublicAppService(
IBlogRepository blogRepository,
IBlogPostRepository blogPostRepository,
ContentParser contentParser)
IBlogPostRepository blogPostRepository)
{
BlogRepository = blogRepository;
BlogPostRepository = blogPostRepository;
ContentParser = contentParser;
}
public virtual async Task<BlogPostCommonDto> GetAsync(
@ -39,10 +37,7 @@ public class BlogPostPublicAppService : CmsKitPublicAppServiceBase, IBlogPostPub
var blogPost = await BlogPostRepository.GetBySlugAsync(blog.Id, blogPostSlug);
var blogPostDto = ObjectMapper.Map<BlogPost, BlogPostCommonDto>(blogPost);
blogPostDto.ContentFragments = await ContentParser.ParseAsync(blogPost.Content);
return blogPostDto;
return ObjectMapper.Map<BlogPost, BlogPostCommonDto>(blogPost);
}
public virtual async Task<PagedResultDto<BlogPostCommonDto>> GetListAsync([NotNull] string blogSlug, BlogPostGetListInput input)

8
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs

@ -13,12 +13,10 @@ namespace Volo.CmsKit.Public.Pages;
public class PagePublicAppService : CmsKitPublicAppServiceBase, IPagePublicAppService
{
protected IPageRepository PageRepository { get; }
protected ContentParser ContentParser { get; }
public PagePublicAppService(IPageRepository pageRepository, ContentParser contentParser)
public PagePublicAppService(IPageRepository pageRepository)
{
PageRepository = pageRepository;
ContentParser = contentParser;
}
public virtual async Task<PageDto> FindBySlugAsync(string slug)
@ -30,8 +28,6 @@ public class PagePublicAppService : CmsKitPublicAppServiceBase, IPagePublicAppSe
return null;
}
var pageDto = ObjectMapper.Map<Page, PageDto>(page);
pageDto.ContentFragments = await ContentParser.ParseAsync(page.Content);
return pageDto;
return ObjectMapper.Map<Page, PageDto>(page);
}
}

8
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs

@ -30,11 +30,9 @@ public class PublicApplicationAutoMapperProfile : Profile
CreateMap<Rating, RatingDto>();
CreateMap<Page, PageDto>()
.Ignore(x => x.ContentFragments);
CreateMap<BlogPost, BlogPostCommonDto>()
.Ignore(x => x.ContentFragments);
CreateMap<Page, PageDto>();
CreateMap<BlogPost, BlogPostCommonDto>();
CreateMap<MenuItem, MenuItemDto>();

2
modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs

@ -2,7 +2,7 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook;
using Volo.Abp.Ui.LayoutHooks;
using Volo.Abp.AutoMapper;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Http.ProxyScripting.Generators.JQuery;

96
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml

@ -9,9 +9,9 @@
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.ReactionSelection
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Rating
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Tags
@using Volo.CmsKit.Web.Renderers
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.HighlightJs
@using Volo.CmsKit.Contents
@using Volo.CmsKit.Web.Renderers
@inherits CmsKitPublicPageBase
@ -21,7 +21,7 @@
@{
string dummyImageSource = "https://dummyimage.com/1280x720/a3a3a3/fff.png?text=" + Model.BlogPost.Title;
string dummyImageSource = "https://dummyimage.com/1280x720/a3a3a3/fff.png?text=" + Model.ViewModel.Title;
var isScrollIndexEnabled = GlobalFeatureManager.Instance.IsEnabled<BlogPostScrollIndexFeature>() && Model.BlogPostScrollIndexFeature?.IsEnabled == true;
}
@ -32,14 +32,13 @@
<abp-style src="/Pages/Public/CmsKit/Blogs/bootstrap-toc.css" />
</abp-abp-style-bundle>
}
<abp-abp-style-bundle>
<abp-style src="/Pages/Public/CmsKit/Blogs/blogPost.css" />
<abp-style type="typeof(HighlightJsStyleContributor)" />
</abp-abp-style-bundle>
}
@section scripts{
@section scripts{
@if (isScrollIndexEnabled)
{
<abp-script-bundle>
@ -55,41 +54,51 @@
}
<div class="row">
<div @Html.Raw(isScrollIndexEnabled ? "class=\"col-md-10 col-sm-12\"" : "class=\"col-md-12\"")>
<abp-card class="mb-4">
<img src="/api/cms-kit/media/@Model.BlogPost.CoverImageMediaId" class="card-img-top" onerror="this.src='@dummyImageSource'" />
<abp-card-body>
<abp-row>
<div class="col-lg-8 col-md-10 mx-auto pb-4">
<h1 class="mt-lg-4 mt-md-3">@Model.BlogPost.Title</h1>
<p class="mb-lg-5 mb-md-3">
<a href="/blogs/@Model.BlogSlug?authorId=@Model.BlogPost.Author.Id">
<span class="font-weight-bold">@@@Model.BlogPost.Author?.UserName</span>
</a>
<small style="opacity:.65;">@Model.BlogPost.CreationTime</small>
</p>
@await Component.InvokeAsync("ContentFragment", new { contentDto = Model.BlogPost })
<p class="mb-3">
@if (Model.BlogPost.LastModificationTime != null)
<div class="row">
<div @Html.Raw(isScrollIndexEnabled ? "class=\"col-md-10 col-sm-12\"" : "class=\"col-md-12\"")>
<abp-card class="mb-4">
<img src="/api/cms-kit/media/@Model.ViewModel.CoverImageMediaId" class="card-img-top" onerror="this.src='@dummyImageSource'" />
<abp-card-body>
<abp-row>
<div class="col-lg-8 col-md-10 mx-auto pb-4">
<h1 class="mt-lg-4 mt-md-3">@Model.ViewModel.Title</h1>
<p class="mb-lg-5 mb-md-3">
<a href="/blogs/@Model.BlogSlug?authorId=@Model.ViewModel.Author.Id">
<span class="font-weight-bold">@@@Model.ViewModel.Author?.UserName</span>
</a>
<small style="opacity:.65;">@Model.ViewModel.CreationTime</small>
</p>
@foreach (var contentFragment in Model.ViewModel.ContentFragments)
{
<small style="opacity:.65;">@L["LastModification"].Value : @Model.BlogPost.LastModificationTime</small>
if (contentFragment.Type == ContentConsts.Markdown)
{
@Html.Raw(await MarkdownRenderer.RenderAsync(contentFragment.GetProperty<string>("Content"), allowHtmlTags: true, preventXSS: true))
}
else if (contentFragment.Type == ContentConsts.Widget)
{
@await Component.InvokeAsync(contentFragment.GetProperty<string>("Type"), contentFragment.ExtraProperties.ConvertToDynamicObject())
}
}
</p>
<hr />
<p class="mb-3">
@if (Model.ViewModel.LastModificationTime != null)
{
<small style="opacity:.65;">@L["LastModification"].Value : @Model.ViewModel.LastModificationTime</small>
}
</p>
<hr />
@if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{
if (Model.TagsFeature?.IsEnabled == true)
@if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{
@await Component.InvokeAsync(typeof(TagViewComponent), new
if (Model.TagsFeature?.IsEnabled == true)
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString(),
urlFormat = $"/blogs/{Model.BlogSlug}?tagId={{TagId}}"
})
@await Component.InvokeAsync(typeof(TagViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.ViewModel.Id.ToString(),
urlFormat = $"/blogs/{Model.BlogSlug}?tagId={{TagId}}"
})
}
}
}
</div>
</abp-row>
@ -100,10 +109,10 @@
if (Model.ReactionsFeature?.IsEnabled == true)
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.ViewModel.Id.ToString()
})
}
}
</abp-column>
@ -113,10 +122,10 @@
if (Model.RatingsFeature?.IsEnabled == true)
{
@await Component.InvokeAsync(typeof(RatingViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.ViewModel.Id.ToString()
})
}
}
</abp-column>
@ -124,7 +133,6 @@
</abp-card-body>
</abp-card>
</div>
@if (isScrollIndexEnabled)
{
<div class="col-md-2 d-sm-none d-md-block">
@ -132,7 +140,7 @@
<h5>@L["InThisDocument"]</h5>
<nav id="blog-post-sticky-index" class="navbar index-scroll pt-0">
</nav>
<div class="row">
<div class="col p-0 py-3">
<a href="#" class="scroll-top-btn">
@ -153,7 +161,7 @@
@await Component.InvokeAsync(typeof(DefaultBlogPostCommentViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
entityId = Model.ViewModel.Id.ToString()
})
}
}

35
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml.cs

@ -1,10 +1,12 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.ObjectMapping;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Contents;
using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Public.Blogs;
using Volo.CmsKit.Web.Contents;
namespace Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Blogs;
@ -16,7 +18,7 @@ public class BlogPostModel : CmsKitPublicPageModelBase
[BindProperty(SupportsGet = true)]
public string BlogPostSlug { get; set; }
public BlogPostCommonDto BlogPost { get; private set; }
public BlogPostViewModel ViewModel { get; private set; }
public BlogFeatureDto CommentsFeature { get; private set; }
@ -25,48 +27,61 @@ public class BlogPostModel : CmsKitPublicPageModelBase
public BlogFeatureDto RatingsFeature { get; private set; }
public BlogFeatureDto TagsFeature { get; private set; }
public BlogFeatureDto BlogPostScrollIndexFeature { get; private set; }
protected IBlogPostPublicAppService BlogPostPublicAppService { get; }
protected IBlogFeatureAppService BlogFeatureAppService { get; }
protected ContentParser ContentParser { get; }
public BlogPostModel(
IBlogPostPublicAppService blogPostPublicAppService,
IBlogFeatureAppService blogFeaturePublicAppService)
IBlogFeatureAppService blogFeaturePublicAppService,
ContentParser contentParser)
{
BlogPostPublicAppService = blogPostPublicAppService;
BlogFeatureAppService = blogFeaturePublicAppService;
ContentParser = contentParser;
}
public virtual async Task OnGetAsync()
public virtual async Task<IActionResult> OnGetAsync()
{
BlogPost = await BlogPostPublicAppService.GetAsync(BlogSlug, BlogPostSlug);
var blogPostPublicDto = await BlogPostPublicAppService.GetAsync(BlogSlug, BlogPostSlug);
ViewModel = ObjectMapper.Map<BlogPostCommonDto, BlogPostViewModel>(blogPostPublicDto);
if (ViewModel == null)
{
return NotFound();
}
ViewModel.ContentFragments = await ContentParser.ParseAsync(blogPostPublicDto.Content);
if (GlobalFeatureManager.Instance.IsEnabled<CommentsFeature>())
{
CommentsFeature = await BlogFeatureAppService.GetOrDefaultAsync(BlogPost.BlogId, GlobalFeatures.CommentsFeature.Name);
CommentsFeature = await BlogFeatureAppService.GetOrDefaultAsync(ViewModel.BlogId, GlobalFeatures.CommentsFeature.Name);
}
if (GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
ReactionsFeature = await BlogFeatureAppService.GetOrDefaultAsync(BlogPost.BlogId, GlobalFeatures.ReactionsFeature.Name);
ReactionsFeature = await BlogFeatureAppService.GetOrDefaultAsync(ViewModel.BlogId, GlobalFeatures.ReactionsFeature.Name);
}
if (GlobalFeatureManager.Instance.IsEnabled<RatingsFeature>())
{
RatingsFeature = await BlogFeatureAppService.GetOrDefaultAsync(BlogPost.BlogId, GlobalFeatures.RatingsFeature.Name);
RatingsFeature = await BlogFeatureAppService.GetOrDefaultAsync(ViewModel.BlogId, GlobalFeatures.RatingsFeature.Name);
}
if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{
TagsFeature = await BlogFeatureAppService.GetOrDefaultAsync(BlogPost.BlogId, GlobalFeatures.TagsFeature.Name);
TagsFeature = await BlogFeatureAppService.GetOrDefaultAsync(ViewModel.BlogId, GlobalFeatures.TagsFeature.Name);
}
if (GlobalFeatureManager.Instance.IsEnabled<BlogPostScrollIndexFeature>())
{
BlogPostScrollIndexFeature = await BlogFeatureAppService.GetOrDefaultAsync(BlogPost.BlogId, GlobalFeatures.BlogPostScrollIndexFeature.Name);
BlogPostScrollIndexFeature = await BlogFeatureAppService.GetOrDefaultAsync(ViewModel.BlogId, GlobalFeatures.BlogPostScrollIndexFeature.Name);
}
return Page();
}
}

28
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPostViewModel.cs

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using AutoMapper;
using Volo.Abp.Application.Dtos;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Blogs;
[AutoMap(typeof(BlogPostCommonDto), ReverseMap = true)]
public class BlogPostViewModel : AuditedEntityDto<Guid>
{
public Guid BlogId { get; set; }
public string Title { get; set; }
public string Slug { get; set; }
public string ShortDescription { get; set; }
public string Content { get; set; }
public List<ContentFragment> ContentFragments { get; set; }
public Guid? CoverImageMediaId { get; set; }
public CmsUserDto Author { get; set; }
}

20
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml

@ -5,27 +5,39 @@
@using Volo.Abp.Data
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.HighlightJs;
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
@using Volo.CmsKit.Web.Renderers
@model Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Pages.IndexModel
@inject IMarkdownToHtmlRenderer MarkdownRenderer
@section styles{
<abp-style src="/Pages/Public/CmsKit/Pages/index.css" />
<style>
@Html.Raw(Model.PageDto.Style)
@Html.Raw(Model.ViewModel.Style)
</style>
}
@section scripts{
<script>
@Html.Raw(Model.PageDto.Script)
@Html.Raw(Model.ViewModel.Script)
</script>
}
<abp-card>
<abp-card-body>
@await Component.InvokeAsync("ContentFragment", new { contentDto = Model.PageDto })
@foreach (ContentFragment contentFragment in Model.ViewModel.ContentFragments)
{
if (contentFragment.Type == ContentConsts.Markdown)
{
@Html.Raw(await MarkdownRenderer.RenderAsync(contentFragment.GetProperty<string>("Content"), allowHtmlTags: true, preventXSS: true))
}
else if (contentFragment.Type == ContentConsts.Widget)
{
@await Component.InvokeAsync(contentFragment.GetProperty<string>("Type"), contentFragment.ExtraProperties.ConvertToDynamicObject())
}
}
</abp-card-body>
</abp-card>

18
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml.cs

@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Mvc;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Public.Pages;
using Volo.CmsKit.Web.Contents;
using Volo.CmsKit.Web.Pages;
namespace Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Pages;
@ -13,21 +14,26 @@ public class IndexModel : CommonPageModel
protected IPagePublicAppService PagePublicAppService { get; }
public PageDto PageDto { get; private set; }
protected ContentParser ContentParser { get; }
public IndexModel(IPagePublicAppService pagePublicAppService)
public PageViewModel ViewModel { get; private set; }
public IndexModel(IPagePublicAppService pagePublicAppService, ContentParser contentParser)
{
PagePublicAppService = pagePublicAppService;
ContentParser = contentParser;
}
public async Task<IActionResult> OnGetAsync()
public virtual async Task<IActionResult> OnGetAsync()
{
PageDto = await PagePublicAppService.FindBySlugAsync(Slug);
if (PageDto == null)
var pageDto = await PagePublicAppService.FindBySlugAsync(Slug);
ViewModel = ObjectMapper.Map<PageDto, PageViewModel>(pageDto);
if (ViewModel == null)
{
return NotFound();
}
ViewModel.ContentFragments = await ContentParser.ParseAsync(pageDto.Content);
return Page();
}

7
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/PageViewModel.cs

@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using AutoMapper;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Public.Pages;
namespace Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Pages;
[AutoMap(typeof(PageDto), ReverseMap = true)]
public class PageViewModel
{
public Guid Id { get; set; }
@ -11,4 +14,8 @@ public class PageViewModel
public string Title { get; set; }
public List<ContentFragment> ContentFragments { get; set; }
public string Script { get; set; }
public string Style { get; set; }
}

27
modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentAdminAppService_Tests.cs

@ -1,27 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.CmsKit.Admin.Contents;
using Xunit;
namespace Volo.CmsKit.Contents;
public class ContentAdminAppService_Tests : CmsKitApplicationTestBase
{
private readonly IContentAdminAppService _contentAdminAppService;
public ContentAdminAppService_Tests()
{
_contentAdminAppService = GetRequiredService<IContentAdminAppService>();
}
[Fact]
public async Task ShouldGet_PagedListAsync()
{
var widgets = await _contentAdminAppService.GetWidgetsAsync();
widgets.Items.Count.ShouldBe(0);
widgets.Items.Any().ShouldBeFalse();
}
}

138
modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentParser_Test.cs

@ -1,138 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Shouldly;
using Xunit;
namespace Volo.CmsKit.Contents;
public class ContentParser_Test : CmsKitDomainTestBase
{
private readonly CmsKitTestData testData;
private readonly IOptions<CmsKitContentWidgetOptions> _options;
private ContentParser contentParser;
public ContentParser_Test()
{
testData = GetRequiredService<CmsKitTestData>();
_options = GetRequiredService<IOptions<CmsKitContentWidgetOptions>>();
}
[Fact]
public async Task ParseAsync_ShouldWorkWithDifferentWidgetTypes()
{
_options.Value.AddWidget(testData.PollName, testData.WidgetName, string.Empty);
_options.Value.AddWidget("ImageGallery", "ImageGallery", string.Empty);
contentParser = new ContentParser(_options);
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type=""Poll"" Code=""poll-name""]
Thanks _for_ *your * feedback.
[Widget GalleryName=""Xyz"" Type=""ImageGallery"" Source=""GoogleDrive""]";
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(4);
widgets[1].ExtraProperties.Count.ShouldBe(2);
widgets[3].ExtraProperties.Count.ShouldBe(3);
}
[Fact]
public async Task ParseAsync_ShouldWorkWithoutConfigOptions()
{
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type= ""Poll"" Code =""poll-name""]
Thanks _for_ *your * feedback.";
contentParser = new ContentParser(_options);
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(1);//Ignored Widget
}
[Fact]
public async Task ParseAsync_ShouldWorkWithWrongConfigOptions()
{
_options.Value.AddWidget(testData.WidgetName, testData.PollName, string.Empty);
contentParser = new ContentParser(_options);
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type= ""Poll"" Code =""poll-name""]
Thanks _for_ *your * feedback.";
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(2);
}
[Fact]
public async Task ParseAsync_ShouldWorkWithWrongWidgetType()
{
_options.Value.AddWidget(testData.PollName, testData.WidgetName, string.Empty);
contentParser = new ContentParser(_options);
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Wrong Type= ""Poll"" Code =""poll-name""]
Thanks _for_ *your * feedback.";
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(2);
}
[Fact]
public async Task ParseAsync_ShouldWorkWithWrongPollName()
{
_options.Value.AddWidget(testData.PollName, testData.WidgetName, string.Empty);
contentParser = new ContentParser(_options);
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type= ""Poll"" PollWrongName =""poll-name""]
Thanks _for_ *your * feedback.";
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(3);
}
[Theory]
[MemberData(nameof(ExampleData))]
public async Task ParseAsync_ShouldWorkProperlyWithCorrectInputs(string content, int expectedLine)
{
_options.Value.AddWidget(testData.PollName, testData.WidgetName, string.Empty);
contentParser = new ContentParser(_options);
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(expectedLine);
}
public static IEnumerable<object[]> ExampleData =>
new List<object[]>
{
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type=""Poll"" Code=""poll-name""]
Thanks _for_ *your * feedback.", 3},
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type=""Poll"" Code=""poll-name""]
Thanks _for_ *your * feedback.
[Widget Type=""Poll"" Code=""poll-name1""]", 4 },
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
Thanks _for_ *your * feedback.
[Widget Type=""Poll"" Code=""poll-name""]", 2 },
new object[] { @"[Widget Type=""Poll"" Code=""poll-name""] gg [Widget Type=""Poll"" Code=""poll-name1""]**ABP Framework** is completely open source and developed in a community-driven manner.
Thanks _for_ *your * feedback.
Thanks _for_ *your * feedback.", 4},
new object[] { @"Thanks _for_ *your * feedback.
Thanks _for_ *your * feedback.", 1}
};
}

2
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/DocumentDto.cs

@ -8,6 +8,8 @@ namespace Volo.Docs.Admin.Documents
public class DocumentDto : EntityDto<Guid>
{
public virtual Guid ProjectId { get; set; }
public virtual string ProjectName { get; set; }
public virtual string Name { get; set; }

11
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/DocumentInfoDto.cs

@ -0,0 +1,11 @@
using System;
namespace Volo.Docs.Admin.Documents;
public class DocumentInfoDto
{
public string Version { get; set; }
public string Format { get; set; }
public string LanguageCode { get; set; }
public Guid ProjectId { get; set; }
}

2
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/IDocumentAdminAppService.cs

@ -19,5 +19,7 @@ namespace Volo.Docs.Admin.Documents
Task RemoveFromCacheAsync(Guid documentId);
Task ReindexAsync(Guid documentId);
Task<List<DocumentInfoDto>> GetFilterItemsAsync();
}
}

2
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/tr.json

@ -48,7 +48,7 @@
"Search": "Arama",
"StartDate": "Başlangıç tarihi",
"EndDate": "Bitiş tarihi",
"CreationTime": "oluşturma zamanı",
"CreationTime": "Oluşturma Zamanı",
"LastUpdateTime": "Son Güncelleme",
"LastSignificantUpdateTime": "Son önemli güncelleme",
"Version": "Sürüm",

2
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/IProjectAdminAppService.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
@ -20,5 +21,6 @@ namespace Volo.Docs.Admin.Projects
Task ReindexAsync(ReindexInput input);
Task ReindexAllAsync();
Task<List<ProjectWithoutDetailsDto>> GetListWithoutDetailsAsync();
}
}

9
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/ProjectWithoutDetailsDto.cs

@ -0,0 +1,9 @@
using System;
namespace Volo.Docs.Admin.Projects;
public class ProjectWithoutDetailsDto
{
public Guid Id { get; set; }
public string Name { get; set; }
}

5
modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/DocsAdminApplicationAutoMapperProfile.cs

@ -1,4 +1,5 @@
using AutoMapper;
using Volo.Abp.AutoMapper;
using Volo.Docs.Admin.Documents;
using Volo.Docs.Admin.Projects;
using Volo.Docs.Documents;
@ -11,8 +12,10 @@ namespace Volo.Docs.Admin
public DocsAdminApplicationAutoMapperProfile()
{
CreateMap<Project, ProjectDto>();
CreateMap<Document, DocumentDto>();
CreateMap<Document, DocumentDto>().Ignore(x => x.ProjectName);
CreateMap<DocumentWithoutContent, DocumentDto>();
CreateMap<ProjectWithoutDetails, ProjectWithoutDetailsDto>();
CreateMap<DocumentInfo, DocumentInfoDto>();
}
}
}

8
modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs

@ -5,7 +5,6 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using Volo.Abp;
using Volo.Abp.Uow;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Caching;
@ -217,6 +216,13 @@ namespace Volo.Docs.Admin.Documents
await _elasticSearchService.AddOrUpdateAsync(document);
}
public async Task<List<DocumentInfoDto>> GetFilterItemsAsync()
{
var documents = await _documentRepository.GetUniqueListDocumentInfoAsync();
return ObjectMapper.Map<List<DocumentInfo>, List<DocumentInfoDto>>(documents);
}
private async Task UpdateDocumentUpdateInfoCache(Document document)
{
var cacheKey = $"DocumentUpdateInfo{document.ProjectId}#{document.Name}#{document.LanguageCode}#{document.Version}";

6
modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs

@ -156,5 +156,11 @@ namespace Volo.Docs.Admin.Projects
await ReindexProjectAsync(project.Id);
}
}
public async Task<List<ProjectWithoutDetailsDto>> GetListWithoutDetailsAsync()
{
var projects = await _projectRepository.GetListWithoutDetailsAsync();
return ObjectMapper.Map<List<ProjectWithoutDetails>, List<ProjectWithoutDetailsDto>>(projects);
}
}
}

6
modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/DocumentsAdminClientProxy.Generated.cs

@ -7,6 +7,7 @@ using Volo.Abp.Http.Modeling;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.Docs.Admin.Documents;
using System.Collections.Generic;
// ReSharper disable once CheckNamespace
namespace Volo.Docs.Admin.ClientProxies;
@ -62,4 +63,9 @@ public partial class DocumentsAdminClientProxy : ClientProxyBase<IDocumentAdminA
{ typeof(Guid), documentId }
});
}
public virtual async Task<List<DocumentInfoDto>> GetFilterItemsAsync()
{
return await RequestAsync<List<DocumentInfoDto>>(nameof(GetFilterItemsAsync));
}
}

6
modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/ProjectsAdminClientProxy.Generated.cs

@ -7,6 +7,7 @@ using Volo.Abp.Http.Modeling;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.Docs.Admin.Projects;
using System.Collections.Generic;
// ReSharper disable once CheckNamespace
namespace Volo.Docs.Admin.ClientProxies;
@ -61,6 +62,11 @@ public partial class ProjectsAdminClientProxy : ClientProxyBase<IProjectAdminApp
await RequestAsync(nameof(ReindexAllAsync));
}
public virtual async Task<List<ProjectWithoutDetailsDto>> GetListWithoutDetailsAsync()
{
return await RequestAsync<List<ProjectWithoutDetailsDto>>(nameof(GetListWithoutDetailsAsync));
}
public virtual async Task ReindexAsync(ReindexInput input)
{
await RequestAsync(nameof(ReindexAsync), new ClientProxyRequestTypeValue

34
modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/docs-admin-generate-proxy.json

@ -7,6 +7,8 @@
"Volo.Docs.Admin.DocumentsAdminController": {
"controllerName": "DocumentsAdmin",
"controllerGroupName": "DocumentsAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.Docs.Admin.DocumentsAdminController",
"interfaces": [
{
@ -427,12 +429,29 @@
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Admin.Documents.IDocumentAdminAppService"
},
"GetFilterItemsAsync": {
"uniqueName": "GetFilterItemsAsync",
"name": "GetFilterItemsAsync",
"httpMethod": "GET",
"url": "api/docs/admin/documents/GetFilterItems",
"supportedVersions": [],
"parametersOnMethod": [],
"parameters": [],
"returnValue": {
"type": "System.Collections.Generic.List<Volo.Docs.Admin.Documents.DocumentInfoDto>",
"typeSimple": "[Volo.Docs.Admin.Documents.DocumentInfoDto]"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Admin.Documents.IDocumentAdminAppService"
}
}
},
"Volo.Docs.Admin.ProjectsAdminController": {
"controllerName": "ProjectsAdmin",
"controllerGroupName": "ProjectsAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.Docs.Admin.ProjectsAdminController",
"interfaces": [
{
@ -684,6 +703,21 @@
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Admin.Projects.IProjectAdminAppService"
},
"GetListWithoutDetailsAsync": {
"uniqueName": "GetListWithoutDetailsAsync",
"name": "GetListWithoutDetailsAsync",
"httpMethod": "GET",
"url": "api/docs/admin/projects/GetListProjectWithoutDetailsAsync",
"supportedVersions": [],
"parametersOnMethod": [],
"parameters": [],
"returnValue": {
"type": "System.Collections.Generic.List<Volo.Docs.Admin.Projects.ProjectWithoutDetailsDto>",
"typeSimple": "[Volo.Docs.Admin.Projects.ProjectWithoutDetailsDto]"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Admin.Projects.IProjectAdminAppService"
},
"ReindexAsyncByInput": {
"uniqueName": "ReindexAsyncByInput",
"name": "ReindexAsync",

7
modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/DocumentsAdminController.cs

@ -63,5 +63,12 @@ namespace Volo.Docs.Admin
{
await _documentAdminAppService.ReindexAsync(documentId);
}
[HttpGet]
[Route("GetFilterItems")]
public async Task<List<DocumentInfoDto>> GetFilterItemsAsync()
{
return await _documentAdminAppService.GetFilterItemsAsync();
}
}
}

8
modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/ProjectsAdminController.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
@ -61,6 +62,13 @@ namespace Volo.Docs.Admin
return _projectAppService.ReindexAllAsync();
}
[HttpGet]
[Route("GetListProjectWithoutDetailsAsync")]
public Task<List<ProjectWithoutDetailsDto>> GetListWithoutDetailsAsync()
{
return _projectAppService.GetListWithoutDetailsAsync();
}
[HttpPost]
[Route("Reindex")]
public Task ReindexAsync(ReindexInput input)

83
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/Index.cshtml

@ -34,50 +34,60 @@
<abp-row>
<abp-column size="_4">
<abp-column size="Auto">
<div class="input-group mb-2">
<div class="input-group-text">@L["Project"].Value</div>
<input type="text"
id="ProjectId"
name="ProjectId"
class="form-control">
<select
id="ProjectId"
name="ProjectId"
class="form-select">
<option></option>
@foreach (var project in Model.Projects)
{
<option value="@project.Id">@project.Name</option>
}
</select>
</div>
</abp-column>
<abp-column size="_4">
<abp-column size="Auto">
<div class="input-group mb-2">
<div class="input-group-text">@L["Name"].Value</div>
<div class="input-group-text">@L["Version"].Value</div>
<input type="text"
id="Name"
name="Name"
class="form-control">
<select
id="Version"
name="Version"
class="form-select">
</select>
</div>
</abp-column>
<abp-column size="_2">
<abp-column size="Auto">
<div class="input-group mb-2">
<div class="input-group-text">@L["Version"].Value</div>
<div class="input-group-text">@L["Name"].Value</div>
<input type="text"
id="Version"
name="Version"
id="Name"
name="Name"
class="form-control">
</div>
</abp-column>
<abp-column size="_3">
<abp-column size="Auto">
<div class="input-group mb-2">
<div class="input-group-text">@L["LanguageCode"].Value</div>
<input type="text"
id="LanguageCode"
name="LanguageCode"
class="form-control">
<select
id="LanguageCode"
name="LanguageCode"
class="form-select">
</select>
</div>
</abp-column>
<abp-column size="Auto">
<abp-button button-type="Primary" icon="search" id="SearchButton" style="line-height: 25px;" text="@L["Search"].Value"></abp-button>
</abp-column>
<abp-row class="mt-3 mb-3">
<abp-column size-md="_12">
<a href="javascript:;" id="AdvancedFilterSectionToggler" class="text-decoration-none">@L["AdvancedFilters"]</a>
@ -85,7 +95,7 @@
</abp-row>
<abp-row id="AdvancedFilterSection" style="display: none;">
<abp-column size="_4">
<abp-column size="Auto">
<div class="input-group mb-2">
<div class="input-group-text">@L["CreationTime"].Value</div>
@ -103,7 +113,7 @@
</div>
</abp-column>
<abp-column size="_4">
<abp-column size="Auto">
<div class="input-group mb-2">
<div class="input-group-text">@L["LastUpdateTime"].Value</div>
<input type="date"
@ -111,7 +121,6 @@
name="LastUpdatedTimeMin"
class="form-control datepicker"
placeholder="@L["StartDate"].Value">
<span>-</span>
<input type="date"
id="LastUpdatedTimeMax"
name="LastUpdatedTimeMax"
@ -120,7 +129,7 @@
</div>
</abp-column>
<abp-column size="_4">
<abp-column size="Auto">
<div class="input-group mb-2">
<div class="input-group-text">@L["LastCachedTime"].Value</div>
@ -138,21 +147,22 @@
</div>
</abp-column>
<abp-column size="_2">
<abp-column size="Auto">
<div class="input-group mb-2">
<div class="input-group-text">@L["Format"].Value</div>
<input type="text"
id="Format"
name="Format"
class="form-control">
<select
id="Format"
name="Format"
class="form-select">
</select>
</div>
</abp-column>
<abp-column size="_2">
<abp-column size="Auto">
</abp-column>
<abp-column size="_5">
<abp-column size="Auto">
<div class="input-group mb-2">
<div class="input-group-text">@L["LastSignificantUpdateTime"].Value</div>
@ -171,10 +181,6 @@
</abp-column>
</abp-row>
<div class="col-auto">
<abp-button button-type="Primary" icon="search" id="SearchButton" style="line-height: 25px;" text="@L["Search"].Value"></abp-button>
</div>
</abp-row>
</form>
</abp-card-header>
@ -183,6 +189,7 @@
<thead>
<tr>
<th>@L["Actions"]</th>
<th>@L["ProjectName"]</th>
<th>@L["Name"]</th>
<th>@L["Version"]</th>
<th>@L["LanguageCode"]</th>

16
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/Index.cshtml.cs

@ -1,14 +1,24 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Volo.Docs.Admin.Projects;
namespace Volo.Docs.Admin.Pages.Docs.Admin.Documents;
[Authorize(DocsAdminPermissions.Projects.Default)]
public class IndexModel : DocsAdminPageModel
{
public virtual Task<IActionResult> OnGet()
private readonly IProjectAdminAppService _projectAdminAppService;
public List<ProjectWithoutDetailsDto> Projects { get; set; }
public IndexModel(IProjectAdminAppService projectAdminAppService)
{
_projectAdminAppService = projectAdminAppService;
}
public virtual async Task<IActionResult> OnGet()
{
return Task.FromResult<IActionResult>(Page());
Projects = await _projectAdminAppService.GetListWithoutDetailsAsync();
return Page();
}
}
}

104
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/index.js

@ -6,6 +6,106 @@ $(function () {
return $datePicker.data().datepicker.getFormattedDate('yyyy-mm-dd');
};
var comboboxItems = [];
service.getFilterItems()
.then(function (result) {
comboboxItems = result;
fillOptions();
}).catch(function (error) {
abp.message.error(error);
});
var $projectId = $('#ProjectId');
$projectId.on('change', function () {
fillOptions();
});
var comboboxs = {
version: $('#Version'),
languageCode: $('#LanguageCode'),
format: $('#Format')
};
for (var key in comboboxs) {
comboboxs[key].on('change', function () {
fillOptions();
});
}
var selectedItem = getSelectedItem();
function emptyComboboxs() {
for (var key in comboboxs) {
comboboxs[key].empty();
}
}
function getSelectedItem() {
var item = {};
for (var key in comboboxs) {
item[key] = comboboxs[key].val();
}
return item;
}
function SetComboboxsValues(item) {
for (var key in comboboxs) {
comboboxs[key].val(item[key]);
}
}
function addComboboxsEmptyItem() {
for (var key in comboboxs) {
comboboxs[key].append($('<option/>').val('').text(''));
}
}
function fillOptions() {
selectedItem = getSelectedItem();
var selectedProjectId = $projectId.val();
emptyComboboxs();
addComboboxsEmptyItem();
var selectedProjectItems = comboboxItems.filter((item) => !selectedProjectId || item.projectId === selectedProjectId);
for (var key in selectedItem) {
var item = selectedProjectItems.find((item) => item[key] === selectedItem[key]);
if (item) {
selectedItem[key] = item[key];
}else {
selectedItem[key] = '';
}
}
selectedProjectItems.forEach(function (item) {
for (var key in comboboxs) {
appendComboboxItem(comboboxs[key], item, key);
}
});
SetComboboxsValues(selectedItem);
}
function appendComboboxItem($combobox, item , key) {
$.each(item, function (index, value) {
if(index !== key) {
if(selectedItem[index] && selectedItem[index] !== value) {
return ;
}}
});
if($combobox.find('option[value="' + item[key] + '"]').length === 0){
$combobox.append($('<option/>').val(item[key]).text(item[key]));
}
}
var getFilter = function () {
return {
projectId: $('#ProjectId').val(),
@ -79,6 +179,10 @@ $(function () {
],
},
},
{
target: 0,
data: 'projectName',
},
{
target: 1,
data: 'name',

14
modules/docs/src/Volo.Docs.Admin.Web/wwwroot/client-proxies/docs-admin-proxy.js

@ -61,6 +61,13 @@
}, ajaxParams));
};
volo.docs.admin.documentsAdmin.getFilterItems = function(ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/admin/documents/GetFilterItems',
type: 'GET'
}, ajaxParams));
};
})();
// controller volo.docs.admin.projectsAdmin
@ -115,6 +122,13 @@
}, ajaxParams));
};
volo.docs.admin.projectsAdmin.getListWithoutDetails = function(ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/admin/projects/GetListProjectWithoutDetailsAsync',
type: 'GET'
}, ajaxParams));
};
volo.docs.admin.projectsAdmin.reindex = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/admin/projects/Reindex',

11
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/DocumentInfo.cs

@ -0,0 +1,11 @@
using System;
namespace Volo.Docs.Documents;
public class DocumentInfo
{
public string Version { get; set; }
public string Format { get; set; }
public string LanguageCode { get; set; }
public Guid ProjectId { get; set; }
}

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

Loading…
Cancel
Save