Browse Source

Merge branch 'dev' into feature/data-collection

# Conflicts:
#	abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
#	framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
issue-#6768
Emre KARA 1 year ago
parent
commit
d45b861a72
  1. 20
      .github/workflows/auto-pr.yml
  2. 2
      .github/workflows/build-and-test.yml
  3. 140
      Directory.Packages.props
  4. 2
      README.md
  5. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  6. 5
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  7. 4
      common.props
  8. 86
      docs/en/Blog-Posts/2025-06-02 v9_2_Release_Stable/POST.md
  9. BIN
      docs/en/Blog-Posts/2025-06-02 v9_2_Release_Stable/cover-image.png
  10. BIN
      docs/en/Blog-Posts/2025-06-02 v9_2_Release_Stable/upgrade-abp-packages.png
  11. 97
      docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/POST.md
  12. BIN
      docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/abp-studio-check-for-updates.png
  13. BIN
      docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/abp-studio-download.png
  14. BIN
      docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/containers-type.png
  15. BIN
      docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/cover-image.png
  16. BIN
      docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/health-checks.png
  17. BIN
      docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/new-migration-added.gif
  18. BIN
      docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/saas-health-check.png
  19. BIN
      docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/theme-style-selection-leptonx.png
  20. 4
      docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/post.md
  21. 134
      docs/en/Community-Articles/2025-05-11-AI-Chat/POST.md
  22. BIN
      docs/en/Community-Articles/2025-05-11-AI-Chat/ai-chat.png
  23. BIN
      docs/en/Community-Articles/2025-05-11-AI-Chat/cover.png
  24. 38
      docs/en/Community-Articles/2025-06-10-bootcamp/post.md
  25. BIN
      docs/en/Community-Articles/2025-06-12-fix-mongodb-guid-abp-v9.2.0-upgrade/mongo-db.jpg
  26. 93
      docs/en/Community-Articles/2025-06-12-fix-mongodb-guid-abp-v9.2.0-upgrade/post.md
  27. 113
      docs/en/Community-Articles/2025-06-13-modular-docker-container-management/modular-docker-container-management.md
  28. BIN
      docs/en/Community-Articles/2025-06-13-modular-docker-container-management/solution-runner-containers.png
  29. 6
      docs/en/cli/index.md
  30. 19
      docs/en/framework/infrastructure/background-jobs/index.md
  31. 3
      docs/en/framework/infrastructure/background-workers/index.md
  32. 4
      docs/en/framework/infrastructure/simple-state-checker.md
  33. 4
      docs/en/get-started/maui.md
  34. BIN
      docs/en/images/azure-deploy-create-web-app-2.png
  35. BIN
      docs/en/images/azure-deploy-create-web-app-6.png
  36. 40
      docs/en/modules/audit-logging-pro.md
  37. 4
      docs/en/release-info/release-notes.md
  38. 2
      docs/en/solution-templates/layered-web-application/deployment/azure-deployment/azure-deployment.md
  39. 114
      docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step1-create-azure-resources.md
  40. 216
      docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step2-configuration-application.md
  41. 263
      docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md
  42. 138
      docs/en/solution-templates/layered-web-application/deployment/azure-deployment/terraform-web-app-service.md
  43. 4
      docs/en/tutorials/microservice/part-01.md
  44. 4
      framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs
  45. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js
  46. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/date-range-picker/date-range-picker-extensions.js
  47. 1
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs
  48. 2
      framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj
  49. 3
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs
  50. 36
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/HttpContextWebClientInfoProvider.cs
  51. 9
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs
  52. 4
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobConfiguration.cs
  53. 5
      framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs
  54. 74
      framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs
  55. 46
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzPeriodicBackgroundWorkerAdapter.cs
  56. 5
      framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AsyncPeriodicBackgroundWorkerBase.cs
  57. 9
      framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs
  58. 9
      framework/src/Volo.Abp.BlazoriseUI/wwwroot/volo.abp.blazoriseui.css
  59. 7
      framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj
  60. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Version/PackageVersionCheckerService.cs
  61. 1
      framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj
  62. 4
      framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLoggingExtensions.cs
  63. 14
      framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
  64. 33
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ConventionalRegistrarBase.cs
  65. 4
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs
  66. 9
      framework/src/Volo.Abp.Core/Volo/Abp/Reflection/AssemblyHelper.cs
  67. 15
      framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeFinder.cs
  68. 4
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpEventBusRabbitMqModule.cs
  69. 54
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/PostConfigureAbpRabbitMqEventBusOptions.cs
  70. 37
      framework/src/Volo.Abp.Features/Volo/Abp/Features/EditionFeatureValueProvider.cs
  71. 4
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs
  72. 3
      framework/src/Volo.Abp.Json.SystemTextJson/Volo.Abp.Json.SystemTextJson.csproj
  73. 44
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/AbpMongoClientFactory.cs
  74. 13
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/IAbpMongoClientFactory.cs
  75. 28
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs
  76. 5
      framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantConfiguration.cs
  77. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationScheme.cs
  78. 8
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderDto.cs
  79. 26
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController.cs
  80. 36
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController_Tests.cs
  81. 3
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/TypeFinder_Tests.cs
  82. 3
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToPath.cs
  83. 79
      framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Clients/MongoClient_Factory_Tests.cs
  84. 12
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs
  85. 3
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestAppModule.cs
  86. 9
      latest-versions.json
  87. 1
      modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj
  88. 4
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj
  89. 9
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogExcelFileConsts.cs
  90. 36
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/ar.json
  91. 32
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/cs.json
  92. 5
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/de-DE.json
  93. 32
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/de.json
  94. 17
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/en.json
  95. 32
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/es.json
  96. 38
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/fi.json
  97. 32
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/fr.json
  98. 5
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/hi.json
  99. 5
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/hr.json
  100. 36
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/hu.json

20
.github/workflows/auto-pr.yml

@ -1,13 +1,13 @@
name: Merge branch dev with rel-9.2
name: Merge branch dev with rel-9.3
on:
push:
branches:
- rel-9.2
- rel-9.3
permissions:
contents: read
jobs:
merge-dev-with-rel-9-2:
merge-dev-with-rel-9-3:
permissions:
contents: write # for peter-evans/create-pull-request to create branch
pull-requests: write # for peter-evans/create-pull-request to create a PR
@ -18,14 +18,14 @@ jobs:
ref: dev
- name: Reset promotion branch
run: |
git fetch origin rel-9.2:rel-9.2
git reset --hard rel-9.2
git fetch origin rel-9.3:rel-9.3
git reset --hard rel-9.3
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
branch: auto-merge/rel-9-2/${{github.run_number}}
title: Merge branch dev with rel-9.2
body: This PR generated automatically to merge dev with rel-9.2. Please review the changed files before merging to prevent any errors that may occur.
branch: auto-merge/rel-9-3/${{github.run_number}}
title: Merge branch dev with rel-9.3
body: This PR generated automatically to merge dev with rel-9.3. Please review the changed files before merging to prevent any errors that may occur.
reviewers: maliming
draft: true
token: ${{ github.token }}
@ -34,5 +34,5 @@ jobs:
GH_TOKEN: ${{ secrets.BOT_SECRET }}
run: |
gh pr ready
gh pr review auto-merge/rel-9-2/${{github.run_number}} --approve
gh pr merge auto-merge/rel-9-2/${{github.run_number}} --merge --auto --delete-branch
gh pr review auto-merge/rel-9-3/${{github.run_number}} --approve
gh pr merge auto-merge/rel-9-3/${{github.run_number}} --merge --auto --delete-branch

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

@ -48,7 +48,7 @@ permissions:
jobs:
build-test:
runs-on: ubuntu-22.04
timeout-minutes: 40
timeout-minutes: 50
if: ${{ !github.event.pull_request.draft }}
steps:
- uses: actions/checkout@v2

140
Directory.Packages.props

@ -18,10 +18,10 @@
<PackageVersion Include="BunnyCDN.Net.Storage" Version="1.0.4" />
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.19.0" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.24.0" />
<PackageVersion Include="Blazorise" Version="1.7.6" />
<PackageVersion Include="Blazorise.Components" Version="1.7.6" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.7.6" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.7.6" />
<PackageVersion Include="Blazorise" Version="1.7.7" />
<PackageVersion Include="Blazorise.Components" Version="1.7.7" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.7.7" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.7.7" />
<PackageVersion Include="Castle.Core" Version="5.1.1" />
<PackageVersion Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
<PackageVersion Include="CommonMark.NET" Version="0.15.1" />
@ -29,14 +29,14 @@
<PackageVersion Include="Dapper" Version="2.1.66" />
<PackageVersion Include="Dapr.AspNetCore" Version="1.15.4" />
<PackageVersion Include="Dapr.Client" Version="1.15.4" />
<PackageVersion Include="DeviceDetector.NET" Version="6.4.2" />
<PackageVersion Include="MyCSharp.HttpUserAgentParser" Version="3.0.25" />
<PackageVersion Include="Devart.Data.Oracle.EFCore" Version="10.4.235.9" />
<PackageVersion Include="DistributedLock.Core" Version="1.0.8" />
<PackageVersion Include="DistributedLock.Redis" Version="1.0.3" />
<PackageVersion Include="DeepL.net" Version="1.15.0" />
<PackageVersion Include="itext.bouncy-castle-adapter" Version="9.1.0" />
<PackageVersion Include="itext7" Version="9.1.0" />
<PackageVersion Include="itext7.pdfhtml" Version="6.1.0" />
<PackageVersion Include="itext.bouncy-castle-adapter" Version="9.2.0" />
<PackageVersion Include="itext7" Version="9.2.0" />
<PackageVersion Include="itext7.pdfhtml" Version="6.2.0" />
<PackageVersion Include="MongoSandbox.Core" Version="2.0.0" />
<PackageVersion Include="MongoSandbox8.runtime.linux-x64" Version="2.0.0" />
<PackageVersion Include="MongoSandbox8.runtime.osx-arm64" Version="2.0.0" />
@ -55,68 +55,68 @@
<PackageVersion Include="Magick.NET-Q16-AnyCPU" Version="13.4.0" />
<PackageVersion Include="MailKit" Version="4.12.0" />
<PackageVersion Include="Markdig.Signed" Version="0.41.1" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.60" />
<PackageVersion Include="Microsoft.Maui.Controls" Version="9.0.60" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.4" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.5" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.5" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.4" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.4.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Configuration.CommandLine" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Composite" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Localization" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Configuration.CommandLine" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Composite" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Localization" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.5" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.9.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.9.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.9.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.9.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.12.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.12.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.12.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.12.0" />
<PackageVersion Include="Minio" Version="6.0.4" />
<PackageVersion Include="MongoDB.Driver" Version="3.3.0" />
<PackageVersion Include="NEST" Version="7.17.5" />
@ -128,11 +128,11 @@
<PackageVersion Include="NUglify" Version="1.21.15" />
<PackageVersion Include="Nullable" Version="1.3.1" />
<PackageVersion Include="Octokit" Version="14.0.0" />
<PackageVersion Include="OpenIddict.Abstractions" Version="6.2.1" />
<PackageVersion Include="OpenIddict.Core" Version="6.2.1" />
<PackageVersion Include="OpenIddict.Server.AspNetCore" Version="6.2.1" />
<PackageVersion Include="OpenIddict.Validation.AspNetCore" Version="6.2.1" />
<PackageVersion Include="OpenIddict.Validation.ServerIntegration" Version="6.2.1" />
<PackageVersion Include="OpenIddict.Abstractions" Version="6.4.0" />
<PackageVersion Include="OpenIddict.Core" Version="6.4.0" />
<PackageVersion Include="OpenIddict.Server.AspNetCore" Version="6.4.0" />
<PackageVersion Include="OpenIddict.Validation.AspNetCore" Version="6.4.0" />
<PackageVersion Include="OpenIddict.Validation.ServerIntegration" Version="6.4.0" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.80" />
<PackageVersion Include="Polly" Version="8.5.2" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
@ -163,17 +163,17 @@
<PackageVersion Include="Spectre.Console" Version="0.50.0" />
<PackageVersion Include="StackExchange.Redis" Version="2.8.31" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="8.1.1" />
<PackageVersion Include="System.Collections.Immutable" Version="9.0.4" />
<PackageVersion Include="System.Collections.Immutable" Version="9.0.5" />
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.2" />
<PackageVersion Include="System.Linq.Queryable" Version="4.3.0" />
<PackageVersion Include="System.Runtime.Loader" Version="4.3.0" />
<PackageVersion Include="System.Security.Permissions" Version="9.0.4" />
<PackageVersion Include="System.Security.Permissions" Version="9.0.5" />
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.4" />
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.4" />
<PackageVersion Include="System.Text.Json" Version="9.0.4" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.5" />
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.5" />
<PackageVersion Include="System.Text.Json" Version="9.0.5" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
<PackageVersion Include="TencentCloudSDK.Sms" Version="3.0.1231" />
<PackageVersion Include="TimeZoneConverter" Version="7.0.0" />

2
README.md

@ -27,7 +27,7 @@ The following diagram contains the core components of the **ABP Platform** and s
ABP offers a complete architectural model to build modern enterprise software solutions. Here, the fundamental architectural structures offered and first-class supported by ABP:
* [Domain Driven Design](https://abp.io/docs/latest/framework/architecture/domain-driven-design)
* [Domain Driven Design (DDD)](https://abp.io/docs/latest/framework/architecture/domain-driven-design)
* [Microservices](https://abp.io/docs/latest/framework/architecture/microservices)
* [Modularity](https://abp.io/docs/latest/framework/architecture/modularity/basics)
* [Multi-Tenancy](https://abp.io/docs/latest/framework/architecture/multi-tenancy)

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

@ -718,6 +718,10 @@
"CacheVersionCount": "Cache Version Count",
"CacheVersionCountInfo": "Gets or sets the number of versions to cache",
"CacheLatestVersionsInfo": "This caches the latest versions of the NuGet packages",
"NuGetApiKey": "NuGet API key",
"QuestionCount": "Question Count",
"MakeAnnouncement": "Make Announcement",
"MakeAnnouncementInfo": "Check it if you want to make an announcement for this post",
"Menu:TelemetryReports": "Telemetry Reports",
"TelemetryReports:DailyStudioOpen": "Daily Studio Opens",
"TelemetryReports:MonthlyStudioOpen": "Monthly Studio Opens",

5
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json

@ -1425,7 +1425,7 @@
"TotalDevelopers": "Total {0} developer(s)",
"CustomPurchaseExplanation": "Tailored to your specific needs",
"WhereDidYouHearAboutUs": "Where did you hear about us?",
"Twitter": "Twitter",
"Twitter": "Twitter (X)",
"Facebook": "Facebook",
"Youtube": "YouTube",
"Google": "Google",
@ -1890,6 +1890,7 @@
"BiographyContainsUrlValidationMessage": "Biography cannot contain URL.",
"CreatePostSEOTitleInfo": "SEO URL is a clean, readable, keyword-rich URL that helps both users and search engines understand what this post is about. Keep it short with 60 characters. SEO titles over 60 characters will be truncated. Use hyphens (-) to separate words (not underscores). Include target keywords near the start. Lowercase only. No stop words unless needed (e.g: \"and\", \"or\", \"the\").",
"SEOTitle": "SEO URL",
"InvalidYouTubeUrl": "The URL you entered is not a valid YouTube video link. Please make sure it points to a specific video and try again."
"InvalidYouTubeUrl": "The URL you entered is not a valid YouTube video link. Please make sure it points to a specific video and try again.",
"SelectAnOption": "Select an option"
}
}

4
common.props

@ -1,8 +1,8 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>9.3.0-preview</Version>
<LeptonXVersion>4.3.0-preview</LeptonXVersion>
<Version>10.0.0-preview</Version>
<LeptonXVersion>5.0.0-preview</LeptonXVersion>
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io/</PackageProjectUrl>

86
docs/en/Blog-Posts/2025-06-02 v9_2_Release_Stable/POST.md

@ -0,0 +1,86 @@
# ABP.IO Platform 9.2 Final Has Been Released!
We are glad to announce that [ABP](https://abp.io/) 9.2 stable version has been released today.
## What's New With Version 9.2?
All the new features were explained in detail in the [9.2 RC Announcement Post](https://abp.io/community/articles/abp-platform-9.2-rc-has-been-released-jpq072nh), so there is no need to review them again. You can check it out for more details.
## Getting Started with 9.2
### Creating New Solutions
You can check the [Get Started page](https://abp.io/get-started) to see how to get started with ABP. You can either download [ABP Studio](https://abp.io/get-started#abp-studio-tab) (**recommended**, if you prefer a user-friendly GUI application - desktop application) or use the [ABP CLI](https://abp.io/docs/latest/cli) to create new solutions.
### How to Upgrade an Existing Solution
You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:
### Upgrading via ABP Studio
If you are already using the ABP Studio, you can upgrade it to the latest version. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See [the documentation](https://abp.io/docs/latest/studio/installation#upgrading) for more info.
After upgrading the ABP Studio, then you can open your solution in the application, and simply click the **Upgrade ABP Packages** action button to instantly upgrade your solution:
![](upgrade-abp-packages.png)
### Upgrading via ABP CLI
Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.
If you haven't installed it yet, you can run the following command:
```bash
dotnet tool install -g Volo.Abp.Studio.Cli
```
Or to update the existing CLI, you can run the following command:
```bash
dotnet tool update -g Volo.Abp.Studio.Cli
```
After installing/updating the ABP CLI, you can use the [`update` command](https://abp.io/docs/latest/CLI#update) to update all the ABP related NuGet and NPM packages in your solution as follows:
```bash
abp update
```
You can run this command in the root folder of your solution to update all ABP related packages.
## Migration Guides
There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v9.x: [ABP Version 9.2 Migration Guide](https://abp.io/docs/9.2/release-info/migration-guides/abp-9-2)
## Community News
### New ABP Community Articles
As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:
* [Liming Ma](https://github.com/maliming) has published 3 new articles:
* [Integrating .NET AI Chat Template with ABP Framework](https://abp.io/community/articles/integrating-.net-ai-chat-template-with-abp-framework-qavb5p2j)
* [Resolving Tenant from Route in ABP Framework](https://abp.io/community/articles/resolving-tenant-from-route-in-abp-framework-ah7oru97)
* [Common Errors in JWT Bearer Authentication](https://abp.io/community/articles/common-errors-in-jwt-bearer-authentication-4u3wrbs5)
* [Engincan Veske](https://engincanveske.substack.com/) has published 3 new articles:
* [Understanding HttpApi.Client Project & Remote Services in an ABP Based Application](https://abp.io/community/articles/http-api-client-and-remote-services-in-abp-based-application-xkknsp6m)
* [Using Elsa 3 with the ABP Framework: A Comprehensive Guide](https://abp.io/community/articles/using-elsa-3-workflow-with-abp-framework-usqk8afg)
* [Implementing Custom Tenant Logo Feature in ABP Framework: A Step-by-Step Guide](https://abp.io/community/articles/implementing-custom-tenant-logo-feature-in-abp-framework-a-stepbystep-guide-sba96ac9)
* [Berkan Şaşmaz](https://berkansasmaz.com/) has published 2 new articles:
* [Understanding the Domain and Application Layers in ABP Framework](https://abp.io/community/articles/understanding-the-domain-and-application-layers-in-abp-1fipc4x4)
* [How Do We Maintain Code Quality and Technical Debt in Our .NET Codebase?](https://abp.io/community/articles/how-do-we-maintain-code-quality-and-technical-debt-in-our-.net-codebase-z7glpya1)
* [Enis Necipoğlu](https://github.com/enisn) has published 2 new articles:
* [White Labeling in ABP Framework](https://abp.io/community/articles/white-labeling-in-abp-framework-5trwmrfm) by [Enis Necipoğlu](https://github.com/enisn)
* [You do it wrong! Customizing ABP Login Page Correctly](https://abp.io/community/articles/you-do-it-wrong-customizing-abp-login-page-correctly-bna7wzt5)
* [Ariful Islam](https://abp.io/community/members/arif) has published 2 new articles:
* [Multi-Workspace Management for ABP Applications](https://abp.io/community/articles/multiworkspace-management-for-abp-applications-eghgty3j)
* [Using Semantic Kernel in the ABP Framework](https://abp.io/community/articles/using-semantic-kernel-in-the-abp-framework-qo5cnuzs)
* [Guide to Add Custom Modules in ABP.IO App](https://abp.io/community/articles/guide-to-add-custom-modules-in-abp.io-app-sttetffa) by [Harsh Gupta](https://abp.io/community/members/harshgupta)
* [Debugging NuGet Packages in ABP.IO: A Complete Guide](https://abp.io/community/articles/debugging-nuget-packages-in-abp.io-a-complete-guide-h13y2033) by [Suhaib Mousa](https://suhaibmousa.com/)
* [Using Microsoft AI Extensions Library and OpenAI to Summarize User Comments](https://abp.io/community/articles/using-microsoft-ai-extensions-library-and-openai-to-summarize-user-comments-gj1lusg7) by [Halil Ibrahim Kalkan](https://twitter.com/hibrahimkalkan)
Thanks to the ABP Community for all the content they have published. You can also [post your ABP related (text or video) content](https://abp.io/community/posts/create) to the ABP Community.
## About the Next Version
The next feature version will be 9.3. You can follow the [release planning here](https://github.com/abpframework/abp/milestones). Please [submit an issue](https://github.com/abpframework/abp/issues/new) if you have any problems with this version.

BIN
docs/en/Blog-Posts/2025-06-02 v9_2_Release_Stable/cover-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

BIN
docs/en/Blog-Posts/2025-06-02 v9_2_Release_Stable/upgrade-abp-packages.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

97
docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/POST.md

@ -0,0 +1,97 @@
# Announcing ABP Studio 1.0 General Availability 🚀
It's the moment you've been waiting for! We are thrilled to announce the stable release of ABP Studio v1.0. This milestone marks a significant step forward in our mission to provide a first-class, integrated development environment for ABP developers. Paired with the recently released [ABP v9.2](https://abp.io/community/articles/announcing-abp-9-2-stable-release-061qmtzb), ABP Studio v1.0 brings new features and improvements that will make your development work faster and more efficient.
For the past several months, our core ABP team has been hard at work, focusing on the features that matter most to you, our community of developers. This release is the peak of that effort, bringing a host of improvements and new capabilities to the forefront. Let's dive in and explore what's new in ABP Studio v1.0.
## What's New with ABP Studio v1.0?
ABP Studio v1.0 is all about enhancing your development experience, from project creation to deployment. Here, we'll walk you through some of the latest features we've implemented, along with other key enhancements that make this release truly special.
### ❤️ Solution Runner with Ready/Health Checks
ABP Studio's Solution Runner now provides visual health monitoring that makes tracking your applications' status easily. When you start an application, a spinner indicates it's "starting", then in the *Overall* tab, you can see the application's health (✅ for healthy, ⚠️ for unhealthy) that displays real-time health status:
![Health Checks](health-checks.png)
With [pre-configured health checks](https://abp.io/docs/9.2/solution-templates/layered-web-application/health-check-configuration) in ABP solution templates including database connectivity tests, you get instant feedback on your applications' health.
When health check UI is configured, you can access comprehensive health dashboards with a dedicated "Browse Health UI" command or see the last health response from the "Show Latest Health Check Response" command:
![SaaS Health Check](saas-health-check.png)
When you restart applications that are open in your browser, ABP Studio automatically refreshes the pages for you.
### 🎨 Theme Style Selection on Project Creation
When creating a new solution, you can now choose your theme, theme style, and layout right from the project creation wizard instead of having to configure these settings later. ABP Studio lets you pick from [ABP's officially provided themes including Basic, LeptonX Lite, and LeptonX](https://abp.io/docs/latest/ui-themes).
![Theme Style Selection LeptonX](theme-style-selection-leptonx.png)
If you select Basic or LeptonX Lite themes, only the theme will be changed. However, if you select the LeptonX theme, you'll get additional options to fine-tune your setup:
- **Theme Style Configuration** - Pick from **System, Light, Dim, or Dark** styles to match how you like your development environment
- **Layout Options** - **Sidebar menu** / **Top menu**
### 📦 "Container" Application Type for Solution Runner
ABP Studio v1.0 introduces a dedicated "Container" application type that gives you better control over your Docker containers directly from the Solution Runner. Instead of managing all your containers through PowerShell scripts or running them all together, you can now see and control each container individually in the Solution Runner panel.
![Container Application Type](containers-type.png)
This new feature replaces the previous _Infrastructure_ folder approach with a cleaner, more intuitive container section. You can now:
- **Start and stop containers individually** - No more starting all containers at once when you only need specific services
- **Monitor container status** - See which containers are running, stopped, or have issues directly in the UI
- **Manage container dependencies** - Control the order and timing of container startup based on your application needs
Whether you're working with databases, message brokers, or other containerized services, the new Container application type makes it much easier to manage your development environment. This is especially useful for microservice architectures where you might want to run only specific services during development or testing.
### ⚙️ Handle Multiple DbContexts When Adding/Removing/Applying Migrations
When working with ABP solutions that have multiple DbContexts (such as when using the separate tenant database option), ABP Studio now intelligently prompts you to select the appropriate DbContext for migration operations. This enhancement ensures you're always working with the correct database context and helps prevent common mistakes when managing multiple databases.
![EF Core Migration Context Selection](new-migration-added.gif)
The context selection dialog appears automatically when you perform any of these Entity Framework operations:
- **Adding a new migration** - Choose which DbContext the new migration should target
- **Removing an existing migration** - Select the DbContext from which to remove the migration
- **Updating the database** - Specify which database context should be updated
## Get Started with ABP Studio v1.0 Today!
ABP Studio v1.0 is built on the solid foundation of the [latest version of ABP Framework, which is v9.2](https://abp.io/community/articles/announcing-abp-9-2-stable-release-061qmtzb). This means that when you create a new project with ABP Studio, you're getting all the latest features, performance improvements, and bug fixes that come with v9.2. This includes updates to dependencies, enhancements to the core framework, and improvements to application modules.
We are incredibly excited for you to get your hands on ABP Studio v1.0. We believe these new features will make a real difference in your day-to-day development workflow.
### ⬇️ Download ABP Studio 1.0
Ready to get started? You can download the stable v1.0 release right now from the official ABP Studio website: **[https://abp.io/studio](https://abp.io/studio)**
![ABP Studio 1.0 Download](abp-studio-download.png)
If you are an existing ABP Studio user, it's even easier. You don't need to download the installer again. Simply launch ABP Studio, and it will prompt you to update to the latest version directly from the UI.
> Alternatively, you can click to the *Help -> Check for Updates* context menu item to check for updates and install the latest version:
>
> ![ABP Studio 1.0 Check for Updates](abp-studio-check-for-updates.png)
### 🔮 What's Next?
ABP Studio v1.0 represents just the beginning of our journey. We're committed to continuously evolving the platform, adding features that directly address real-world development challenges and enhance your workflow. Our goal is to make ABP Studio the go-to development environment for .NET and ABP Framework developers.
We will keep releasing new versions with exciting features based on our roadmap and your valuable feedback. To give you a sneak peek into what's planned for future releases, you can expect to see:
- **Environment Variable Management:** A dedicated UI to easily manage environment variables for your solutions.
- **OpenTelemetry Integration:** We'll be integrating OpenTelemetry support directly into the startup templates, making distributed tracing and observability a seamless part of your application from day one.
- **LeptonX Theme Builder**: Allowing users to determine styling, colour palette and easily override their project's theme styles.
- **Monitor dashboards of the tools used in the solution (e.g. Kubernetes, Redis, Grafana, etc...)**
- **Pre-configured .NET Aspire for the Microservice Startup Template**
- **and more...**
We are incredibly excited about the future of ABP Studio and can't wait to share the next set of features with you. Your comments and suggestions are invaluable to us. If you have any feedback, please drop a comment below.
Thank you for being part of our community and happy coding!
**The Volosoft Team**

BIN
docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/abp-studio-check-for-updates.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/abp-studio-download.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

BIN
docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/containers-type.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/cover-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 KiB

BIN
docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/health-checks.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

BIN
docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/new-migration-added.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 KiB

BIN
docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/saas-health-check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/theme-style-selection-leptonx.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

4
docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/post.md

@ -119,7 +119,7 @@ For example, if your filename is `zh.hans.json`, ABP will generate the following
[File] [/MyModule/Volo/Abp/MyModule/Localization/zh/hans.json]
```
Microsoft provides the `Microsoft.Extensions.FileProviders.Manifest` library to solve this problem.
Microsoft provides the `Microsoft.Extensions.FileProviders.Embedded` library to solve this problem.
We need to add this package dependency and set `<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>` in our project:
@ -136,7 +136,7 @@ We need to add this package dependency and set `<GenerateEmbeddedFilesManifest>t
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Volo.Abp.VirtualFileSystem" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Manifest" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.0" />
</ItemGroup>
<ItemGroup>

134
docs/en/Community-Articles/2025-05-11-AI-Chat/POST.md

@ -0,0 +1,134 @@
# Integrating .NET AI Chat Template with ABP Framework
This article demonstrates how to integrate the [.NET AI Chat Template](https://devblogs.microsoft.com/dotnet/announcing-dotnet-ai-template-preview2/) into an ABP Framework application, enabling powerful AI chat capabilities in your ABP-based solution.
![cover](cover.png)
## Step 1: Create a New ABP Project
First, let's create a new [single-layer Blazor Server project](https://abp.io/docs/latest/solution-templates/single-layer-web-application/overview) named `AbpAiChat` using ABP Studio, You can also use the following ABP CLI command to create the project:
```bash
abp new AbpAiChat -t app-nolayers --ui-framework blazor-server --use-open-source-template
```
## Step 2: Integrate AI Chat Template
The integration process involves copying and adapting the .NET AI Chat Template code into our ABP project. The template code is already included in our sample project, so you don't need to install it separately.
### 2.1 Project Structure Changes
1. Copy Blazor components to the `Components` folder
2. Copy AI service classes to the `Services` folder
3. Add required entities(`IngestedDocument`, `IngestedRecord`) to the `AbpAiChatDbContext` and add new migration
4. Copy frontend resources to the `wwwroot` folder
5. Adjust some styles to capatible with the ABP theme
### 2.2 Required NuGet Packages
Add the following packages to `AbpAiChat.csproj`:
```xml
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.4.3-preview.1.25230.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.AI" Version="9.4.3-preview.1.25230.7" />
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.47.0" />
<PackageReference Include="PdfPig" Version="0.1.9" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
```
### 2.3 Configure AI Services
Add the following configuration to `AbpAiChatModule.cs`:
```csharp
private void ConfigureAi(ServiceConfigurationContext context)
{
var credential = new ApiKeyCredential(context.Services.GetConfiguration()["GitHubToken"] ?? throw new InvalidOperationException("Missing configuration: GitHubToken. See the README for details."));
var openAiOptions = new OpenAIClientOptions()
{
Endpoint = new Uri("https://models.inference.ai.azure.com")
};
var ghModelsClient = new OpenAIClient(credential, openAiOptions);
var chatClient = ghModelsClient.GetChatClient("gpt-4o-mini").AsIChatClient();
var embeddingGenerator = ghModelsClient.GetEmbeddingClient("text-embedding-3-small").AsIEmbeddingGenerator();
var vectorStore = new JsonVectorStore(Path.Combine(AppContext.BaseDirectory, "vector-store"));
context.Services.AddSingleton<IVectorStore>(vectorStore);
context.Services.AddScoped<DataIngestor>();
context.Services.AddSingleton<SemanticSearch>();
context.Services.AddChatClient(chatClient).UseFunctionInvocation().UseLogging();
context.Services.AddEmbeddingGenerator(embeddingGenerator);
context.Services.Configure<AbpAspNetCoreContentOptions>(options =>
{
options.ContentTypeMaps.Add(".mjs", "application/javascript");
});
}
```
The `ConfigureAi` method is called in the `ConfigureServices` method of `AbpAiChatModule`. It sets up the AI services, including the OpenAI client, chat client, embedding generator, and vector store.
### 2.4 Configure GitHub Token
Add your GitHub Personal Access Token to `appsettings.json`:
```json
{
"GitHubToken": "your-github-token"
}
```
You can obtain your token from [GitHub Personal Access Tokens](https://github.com/settings/personal-access-tokens).
## Step 3: Add Custom AI Functionality
Let's add a custom AI function to retrieve the current user's information. Update the `Chat.razor` component:
```csharp
chatOptions.Tools =
[
AIFunctionFactory.Create(SearchAsync),
AIFunctionFactory.Create(GetWeather),
AIFunctionFactory.Create(GetCurrentUserInfo)
];
[Description("Get current user information")]
private Task<string> GetCurrentUserInfo()
{
return Task.FromResult(CurrentUser.IsAuthenticated ?
$"UserId: {CurrentUser.Id}, Name: {CurrentUser.UserName}, Email: {CurrentUser.Email}, Roles: {string.Join(", ", CurrentUser.Roles)}" :
"No user information available.");
}
```
## Step 4: Add Navigation
Add a `Chat` menu item in `AbpAiChatMenuContributor` to navigate to the AI Chat component.
## Running the Application
After completing the integration, you can run the application and access the AI chat functionality. The chat interface allows you to:
- Get weather information
- Ask questions about PDF content
- Retrieve current user information
- And more!
![AI Chat Interface](ai-chat.png)
## Conclusion
This integration demonstrates how to leverage the power of AI in your ABP Framework applications. The .NET AI Chat Template provides a solid foundation for building intelligent chat interfaces, and ABP Framework makes it more powerful.
## References
- [ABP Single Layer Solution](https://abp.io/docs/latest/solution-templates/single-layer-web-application/overview)
- [.NET AI Template Documentation](https://devblogs.microsoft.com/dotnet/announcing-dotnet-ai-template-preview1/)
- [GitHub Personal Access Tokens Guide](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)
## Source Code
- [AbpAiChat Source Code](https://github.com/abpframework/abp-samples/tree/master/AIChat)

BIN
docs/en/Community-Articles/2025-05-11-AI-Chat/ai-chat.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

BIN
docs/en/Community-Articles/2025-05-11-AI-Chat/cover.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

38
docs/en/Community-Articles/2025-06-10-bootcamp/post.md

@ -0,0 +1,38 @@
You Asked, We Delivered: Introducing Our New Training Bootcamp\!
Over the past months we’ve heard from countless freelancers, individuals and small teams asking for a focused, high-impact training without the overwhelming commitment or high costs of traditional bootcamps. So here we are\!
We're excited to introduce **ABP Bootcamp: Mastering Infrastructure & Features**, a full training experience designed to help developers level up fast with practical knowledge they can apply immediately.
This live, instructor-led program delivers four days of in-depth, real-world training on ABP’s infrastructure and core features.
**Bootcamp Details**
Dates: July 1,2,3,4 2025
Time: 17:00-19:00 (UTC) each day
Format: Live, online sessions
Price: $399 (discounted $799)
Seats: Limited to a small group
### **What You’ll Learn**
By the end of this bootcamp, you’ll have:
\-A solid understanding of ABP’s infrastructure, systems, and design patterns
\-Practical experience with features like dependency injection, configuration, logging, and localization
\-The ability to implement email templating, event bus, caching, and custom settings in real applications
\-Knowledge of best practices for building scalable, maintainable, and high-performance solutions with ABP
### **Who It’s For**
This bootcamp is ideal for:
\-Developers and freelancers using ABP in their daily work
\-Teams looking to speed up onboarding and improve development efficiency
\-Anyone who wants practical ABP knowledge without committing to long-format training programs
### **Reserve Your Spot**
Seats are limited to keep the experience interactive and personalized. With the current 50% discount, this is the most accessible way to gain hands-on ABP experience with expert guidance.
**Registration is now open. Secure your place today and master the infrastructure that powers ABP. Register here: [https://docs.google.com/forms/d/e/1FAIpQLSckMZChdqIJIuWiEvg\_KsSDmrxagRdc5WBYceAemHLrmT1oiA/viewform](https://docs.google.com/forms/d/e/1FAIpQLSckMZChdqIJIuWiEvg_KsSDmrxagRdc5WBYceAemHLrmT1oiA/viewform)**

BIN
docs/en/Community-Articles/2025-06-12-fix-mongodb-guid-abp-v9.2.0-upgrade/mongo-db.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

93
docs/en/Community-Articles/2025-06-12-fix-mongodb-guid-abp-v9.2.0-upgrade/post.md

@ -0,0 +1,93 @@
# Solving MongoDB GUID Issues After an ABP Framework Upgrade
So, you've just upgraded your ABP Framework application to a newer version (like v9.2.0+) and suddenly, your application can't read data from its MongoDB database. You're seeing strange deserialization errors, especially related to `Guid` types. What's going on?
You've likely run into a classic compatibility issue with the MongoDB .NET driver.
### The Problem: Legacy vs. Standard GUIDs
Here's the short version:
* **Old MongoDB Drivers** (used in older ABP versions) stored `Guid` values in a format called `CSharpLegacy`.
* **New MongoDB Drivers** (v3.0+), now default to a universal `Standard` format.
When your newly upgraded app tries to read old data, the new driver expects the `Standard` format but finds `CSharpLegacy`. The byte orders don't match, and... boom. Deserialization fails.
The ABP Framework team has an excellent official guide covering this topic in detail. We highly recommend reading their **[MongoDB Driver 2 to 3 Migration Guide](https://abp.io/docs/latest/release-info/migration-guides/MongoDB-Driver-2-to-3)** for a full understanding.
Our tip below serves as a fast, application-level fix if you need to get your system back online quickly without performing a full data migration.
### The Quick Fix: Tell the Driver to Use the Old Format
Instead of changing your data, you can simply tell the new driver to continue using the old `CSharpLegacy` format for all `Guid` and `Guid?` properties. This provides immediate backward compatibility without touching your database.
It’s a simple, two-step process.
#### Step 1: Create a Custom Convention
First, create this class in your `.MongoDb` project. It tells the serializer how to handle `Guid` types.
```csharp
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Bson.Serialization.Serializers;
using System;
public class LegacyGuidConvention : ConventionBase, IMemberMapConvention
{
public void Apply(BsonMemberMap memberMap)
{
if (memberMap.MemberType == typeof(Guid))
{
memberMap.SetSerializer(new GuidSerializer(GuidRepresentation.CSharpLegacy));
}
else if (memberMap.MemberType == typeof(Guid?))
{
var guidSerializer = new GuidSerializer(GuidRepresentation.CSharpLegacy);
var nullableGuidSerializer = new NullableSerializer<Guid>(guidSerializer);
memberMap.SetSerializer(nullableGuidSerializer);
}
}
}
```
#### Step 2: Register the Convention at Startup
Now, register this convention in your `YourProjectMongoDbModule.cs` file. Add this code to the top of the `ConfigureServices` method. This ensures your rule is applied globally as soon as the application starts.
```csharp
using Volo.Abp.Modularity;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
public class YourProjectMongoDbModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Fix Start
var conventionPack = new ConventionPack { new LegacyGuidConvention() };
ConventionRegistry.Register(
"LegacyGuidConvention",
conventionPack,
t => true); // Apply to all types
// Fix End
// ... Your existing ConfigureServices code
}
}
```
### An Alternative to Full Data Migration
It's important to note that the method described here is an **application-level fix**. It's a fantastic alternative to performing a full data migration, which involves writing scripts to convert every legacy GUID in your database.
If you are interested in the more permanent, data-centric approach, the ABP.IO community has a detailed guide on [**Migrating MongoDB GUIDs from Legacy to Standard Format**](https://abp.io/community/articles/migrating-mongodb-guids-from-legacy-to-standard-format-mongodb-v2-to-v3-dqwybdtw).
Our quick fix is ideal for getting a system back online fast or when a database migration is too complex. The full migration is better for long-term standards compliance. Choose the path that best fits your project's needs!
### That's It!
Restart your application, and the errors should be gone. Your app can now correctly read its old `Guid` data, and it will continue to write new data in the same legacy format, ensuring consistency.
This approach is a lifesaver for existing projects, saving you from a risky and time-consuming data migration. For brand-new projects, you might consider starting with the `Standard` representation, but for everything else, this is a clean and effective fix. Happy coding!

113
docs/en/Community-Articles/2025-06-13-modular-docker-container-management/modular-docker-container-management.md

@ -0,0 +1,113 @@
# 🚀 New in ABP Studio: Modular Docker Container Management
We're excited to announce a new improvement to Docker integration in **ABP Studio**!
With the latest update, you can now manage Docker containers **individually**, add or remove them dynamically, and launch them **either separately or collectively** — all within the Studio.
![solution-runner-containers](solution-runner-containers.png)
------
## 🔄 What Has Changed?
In previous versions, Docker dependencies were handled using a **single `docker-compose.override.yml` file**, which was **automatically generated** when creating a new solution if it is needed.
By default, this file included common development dependencies like PostgreSQL, Redis, RabbitMQ, etc., and was executed through a predefined script named `docker-dependencies` in the Solution Runner.
While this approach worked well for simple setups, it had some limitations:
- All services were bundled into a **single compose file**.
- Adding or removing services required modifying this central file.
- It wasn't possible to **start or stop individual containers independently**.
------
## ✅ What's New?
With the latest ABP Studio update:
- Each Docker container can now be defined in its **own `docker-compose` file**.
- Compose files can be **added or removed** from the Studio UI.
- Containers can be:
- **Started or stopped individually**.
- **Started/stopped in bulk**.
- The **Solution Runner** recognizes Docker containers and can run them alongside application projects.
------
## ⚠️ Important Notes Before You Start
### Required: Use `container_name` for Docker Service Matching
When working with the new modular Docker system in ABP Studio, each service **must define a `container_name`**.
This name is used by ABP Studio to **identify and map** Docker containers to their corresponding service entries in the Studio UI.
#### Why is this mandatory?
- ABP Studio relies on `container_name` to:
- Detect whether the service is stopped or continue running.
- Perform **start**, **stop**, and **status check** operations reliably.
- Match Studio UI entries with actual running Docker containers.
- Without a `container_name`, container discovery may fail and **service management features might not work as expected**.
#### Example:
```
services:
redis:
container_name: redis
image: redis:7
ports:
- "6379:6379"
networks:
- my-network
```
> If you do **not** define `container_name`, ABP Studio will be unable to track or control the service properly.
------
## 📥 Migrating from the Old System
If you're using the old method with a centralized compose file:
Before:
```
docker-compose -f docker-compose.override.yml up -d
```
Now:
1. Create separate `docker-compose` files for each service
(e.g., `docker-compose.postgres.yml`, `docker-compose.redis.yml`).
2. Go to the **Solution Runner tab in ABP Studio**.
3. Use the **“Add Docker Service”** option in Studio to register each file.
4. Optionally remove or archive the old monolithic compose file.
> If your original `docker-compose.override.yml` contains multiple services, you can split it into individual files — Docker Compose and ABP Studio both support modular composition.
------
## ⚙️ Advanced: Shared Network
If your containers need to communicate over a shared network, you can define an external network in each compose file like this:
```
networks:
my-network:
external: true
```
ABP Studio automatically creates the network if they do no exist. But if you like you can create the network once using:
```
docker network create my-network
```
------
## 📚 Additional Resources
- Docker Compose Documentation
- ABP Studio Documentation (link to updated docs when ready)
- [Report Issues on GitHub](https://github.com/abpframework/abp/issues)

BIN
docs/en/Community-Articles/2025-06-13-modular-docker-container-management/solution-runner-containers.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

6
docs/en/cli/index.md

@ -161,7 +161,7 @@ For more samples, go to [ABP CLI Create Solution Samples](new-command-samples.md
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
* `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True` for EF Core and it is configured to use the SQL Server. If you want to use the EF Core, but need to change the DBMS, you can change it as [described here](../framework/data/entity-framework-core/other-dbms.md) (after creating the solution).
* `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True` for EF Core and it is configured to use the SQL Server. If you want to use the EF Core, but need to change the DBMS, you can change it as [described here](../framework/data/entity-framework-core/other-dbms.md) (after creating the solution). **Note:** When specifying the connection string, make sure to enclose it in double quotes, for example: `--connection-string "Server=localhost;Database=MyProjectName;Trusted_Connection=True"`.
* `--skip-migrations` or `-sm`: Skips the creating initial database migration step.
* `--skip-migrator` or `-smr`: Skips the run database migrator step.
* `--public-website`: Public Website is a front-facing website for describing your project, listing your products and doing SEO for marketing purposes. Users can login and register on your website with this website. This option is only included in PRO templates.
@ -183,7 +183,7 @@ For more samples, go to [ABP CLI Create Solution Samples](new-command-samples.md
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
* `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True` for EF Core and it is configured to use the SQL Server. If you want to use the EF Core, but need to change the DBMS, you can change it as [described here](../framework/data/entity-framework-core/other-dbms.md) (after creating the solution).
* `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True` for EF Core and it is configured to use the SQL Server. If you want to use the EF Core, but need to change the DBMS, you can change it as [described here](../framework/data/entity-framework-core/other-dbms.md) (after creating the solution). **Note:** When specifying the connection string, make sure to enclose it in double quotes, for example: `--connection-string "Server=localhost;Database=MyProjectName;Trusted_Connection=True"`.
* `--skip-migrations` or `-sm`: Skips the creating initial database migration step.
* `--skip-migrator` or `-smr`: Skips the run database migrator step.
* `--sample-crud-page` or `-scp`: It adds the [BookStore](./../tutorials/book-store/index.md) sample to your solution.
@ -295,7 +295,7 @@ abp new-package --name Acme.BookStore.Domain --template lib.domain
* `lib.ef`
* `--include-migrations`: Allows migration operations on this package.
* `--connection-string-name`: Default value is the last part of the package's namespace (or package name simply).
* `--connection-string`: Connection string value. Defaut value is null. You can set it alter.
* `--connection-string`: Connection string value. Defaut value is null. You can set it alter. **Note:** When specifying the connection string, make sure to enclose it in double quotes, for example: `--connection-string "Server=localhost;Database=MyProjectName;Trusted_Connection=True"`.
* `lib.mongodb`
* `lib.http-api`
* `lib.http-api-client`

19
docs/en/framework/infrastructure/background-jobs/index.md

@ -134,6 +134,25 @@ namespace MyProject
}
```
##### Custom Job Name
You can configure `GetBackgroundJobName` delegate of the `AbpBackgroundJobOptions` to change the default job name.
```csharp
Configure<AbpBackgroundJobOptions>(options =>
{
options.GetBackgroundJobName = (jobType) =>
{
if (jobTyep == typeof(EmailSendingArgs))
{
return "emails";
}
return BackgroundJobNameAttribute.GetName(jobType);
};
});
```
### Queue a Job Item
Now, you can queue an email sending job using the `IBackgroundJobManager` service:

3
docs/en/framework/infrastructure/background-workers/index.md

@ -41,6 +41,8 @@ Start your worker in the `StartAsync` (which is called when the application begi
Assume that we want to make a user passive, if the user has not logged in to the application in last 30 days. `AsyncPeriodicBackgroundWorkerBase` class simplifies to create periodic workers, so we will use it for the example below:
> You can use `CronExpression` property to set the cron expression for the background worker if you will use the [Hangfire Background Worker Manager](./hangfire.md) or [Quartz Background Worker Manager](./quartz.md).
````csharp
public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase
{
@ -52,6 +54,7 @@ public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase
serviceScopeFactory)
{
Timer.Period = 600000; //10 minutes
//CronExpression = "0 0/10 * * * ?"; //Run every 10 minutes, Only for Quartz or Hangfire integration.
}
protected async override Task DoWorkAsync(

4
docs/en/framework/infrastructure/simple-state-checker.md

@ -11,11 +11,11 @@ public class MyObject : IHasSimpleStateCheckers<MyObject>
{
public int Id { get; set; }
public List<ISimpleStateChecker<MyObject>> SimpleStateCheckers { get; }
public List<ISimpleStateChecker<MyObject>> StateCheckers { get; }
public MyObject()
{
SimpleStateCheckers = new List<ISimpleStateChecker<MyObject>>();
StateCheckers = new List<ISimpleStateChecker<MyObject>>();
}
}
````

4
docs/en/get-started/maui.md

@ -13,9 +13,11 @@ dotnet tool install -g Volo.Abp.Studio.Cli
Then use the `abp new` command in an empty folder to create a new solution:
````bash
abp new Acme.MyMauiApp -t maui
abp new Acme.MyMauiApp -t maui --old
````
> **Note**: Since this startup template is not provided by the new ABP Studio Templates yet, you need to pass the `--old` parameter at the end of the command to use the old CLI & templating system for this startup template.
`Acme.MyMauiApp` is the solution name, like *YourCompany.YourProduct*. You can use single level, two-levels or three-levels naming.
## Solution Structure

BIN
docs/en/images/azure-deploy-create-web-app-2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 141 KiB

BIN
docs/en/images/azure-deploy-create-web-app-6.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 183 KiB

40
docs/en/modules/audit-logging-pro.md

@ -9,6 +9,8 @@ This module implements the Audit Logging system of an application;
* See all changes of entities and filter entity change logs.
* View details of an entity change.
* View all changes of an entity.
* Export audit logs and entity changes to Excel.
* Receive email notifications for completed or failed exports.
* This module also defines reusable "Average Execution Duration Per Day" and "Error Rate" widgets.
* Periodic clean up of audit logs.
@ -56,6 +58,10 @@ You can view details of an audit log by clicking the magnifier icon on each audi
* **Actions:** This tab shows list of actions (controller actions and application service method calls with their parameters) executed during a web request.
* **Changes:** This tab shows changed entities during the web request.
##### Export to Excel
You can export audit logs to Excel by clicking the "Export to Excel" button in the toolbar. If the result set is small (less than a configurable threshold), the file will be generated and downloaded immediately. For larger result sets, the export will be processed as a background job and you'll receive an email with a download link once the export is completed.
#### Entity Changes
Entity changes tab is used to list, view and filter entity change logs.
@ -80,6 +86,10 @@ You can view details of all changes of an entity by clicking the "Full Change Hi
![audit-logging-module-full-entity-change-details-modal](../images/audit-logging-module-full-entity-change-details-modal.png)
##### Export to Excel
You can export entity changes to Excel by clicking the "Export to Excel" button in the toolbar. Similar to audit logs export, for large datasets the export will be processed as a background job and you'll receive an email notification once completed.
#### Audit Log Settings
The *Audit Log* settings tab is used to configure audit log settings. You can enable or disable the clean up service system wide. This way, you can shut down the clean up service for all tenants and host. If the system wide clean up service is enabled, you can configure the global *Expired Item Deletion Period* for all tenants and host.
@ -121,11 +131,32 @@ To see `AbpAuditingOptions` properties, please see its [documentation](../framew
Configure<ExpiredAuditLogDeleterOptions>(options =>
{
options.Period = (int)TimeSpan.FromSeconds(30).TotalMilliseconds;
options.CronExpression = "0 23 * * *"; // This Cron expression only works if Hangfire or Quartz is used for background workers.
});
```
The *Period* doesn't mean the *Expired Item Deletion Period*. It's the period of the worker to run clean up service system wide. The default value is 1 day.
### AuditLogExcelFileOptions
`AuditLogExcelFileOptions` can be configured in the UI layer, within the `ConfigureServices` method of your [module](../framework/architecture/modularity/basics.md). Example:
```csharp
Configure<AuditLogExcelFileOptions>(options =>
{
options.FileRetentionHours = 24; // How long to keep files before cleanup (default: 24 hours)
options.DownloadBaseUrl = "https://yourdomain.com"; // Base URL for download links in emails
options.ExcelFileCleanupOptions.Period = (int)TimeSpan.FromHours(24).TotalMilliseconds; // Interval of the cleanup worker (default: 24 hours)
options.ExcelFileCleanupOptions.CronExpression = "0 23 * * *"; // This Cron expression only works if Hangfire or Quartz is used for background workers.
});
```
> Note: The `FileRetentionHours` value determines when files become eligible for deletion, but actual deletion depends on when the cleanup worker runs. If the worker hasn't run after the retention period expires, files will remain accessible. Therefore, `FileRetentionHours` represents the minimum intended retention time, but the actual retention time might be longer depending on the worker's execution schedule.
These settings control where Excel export files are stored, how long they are kept before automatic cleanup, and what base URL is used in email download links.
> You must use a valid [BLOB Storage Provider](https://abp.io/docs/latest/framework/infrastructure/blob-storing#blob-storage-providers) to use this feature.
## Internals
### Domain layer
@ -156,6 +187,15 @@ Following custom repositories are defined for this module:
* `AuditLogsAppService` (implements `IAuditLogsAppService`): Implements the use cases of the audit logs management UI.
#### Email Templates
The module provides email templates for notifications:
* `AuditLogExportCompleted`: Sent when an audit log export is successfully completed, including a download link.
* `AuditLogExportFailed`: Sent when an audit log export fails, including error details.
* `EntityChangeExportCompleted`: Sent when an entity change export is successfully completed, including a download link.
* `EntityChangeExportFailed`: Sent when an entity change export fails, including error details.
### Database providers
#### Common

4
docs/en/release-info/release-notes.md

@ -8,9 +8,9 @@ Also see the following notes about ABP releases:
* [Change logs for ABP pro packages](https://abp.io/pro-releases)
## 9.2 (2025-03-25)
## 9.2 (2025-06-02)
This is currently a RC (release-candidate) and you can see the detailed **[blog post / announcement](https://abp.io/community/articles/abp-platform-9.2-rc-has-been-released-jpq072nh)** for the v9.2 release.
See the detailed **[blog post / announcement](https://abp.io/community/articles/announcing-abp-9-2-stable-release-061qmtzb)** for the v9.2 release.
* Added `ApplicationName` Property to Isolate Background Jobs & Background Workers
* Docs Module: Added "Alternative Words" to Filter Items

2
docs/en/solution-templates/layered-web-application/deployment/azure-deployment/azure-deployment.md

@ -3,7 +3,7 @@
````json
//[doc-params]
{
"UI": ["MVC", "Blazor", "BlazorServer", "NG"],
"UI": ["MVC", "Blazor", "BlazorServer", "BlazorWebApp", "NG"],
"DB": ["EF", "Mongo"],
"Tiered": ["Yes", "No"]
}

114
docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step1-create-azure-resources.md

@ -1,7 +1,7 @@
````json
//[doc-params]
{
"UI": ["MVC", "Blazor", "BlazorServer", "NG"],
"UI": ["MVC", "Blazor", "BlazorServer", "BlazorWebApp", "NG"],
"DB": ["EF", "Mongo"],
"Tiered": ["Yes", "No"]
}
@ -15,7 +15,9 @@ To create a new Azure Web App Service, choose one of the following options:
- [Create a new Azure Web App Service using the Terraform Template](terraform-web-app-service.md) (If you have experience with Terraform)
{{ if UI == "MVC" && Tiered == "No" }}
{{ if Tiered == "No" }}
{{ if UI == "BlazorServer" || UI == "MVC" || UI == "BlazorWebApp"}}
### Create a new Azure Web App service using the Azure Portal
@ -39,9 +41,102 @@ To create a new Azure Web App Service, choose one of the following options:
![Create Web App](../../../../images/azure-deploy-create-web-app-3.png)
{{else}}
{{else if UI == 'NG'}}
### Create a new Azure Static Web App for Angular using the Azure Portal
1. Log in to the [Azure Portal](https://portal.azure.com/).
2. Click the **Create a resource** button.
3. Search for **Static Web App** and select **Static Web App** from the results.
![Create a resource angular](../../../../images/azure-deploy-create-a-resource-angular.png)
4. Click the **Create** button.
5. Fill in the required fields and click the **Review + create** button.
6. Click the **Create** button.
![Create Web App](../../../../images/azure-deploy-create-web-app-4.png)
7. Wait for the deployment to complete.
![Create Web App](../../../../images/azure-deploy-create-web-app-5.png)
### Create a new Azure Web App Service for API application
1. You can create a new Azure Web App Service for an API application in the same resource group.
2. Click the **Create** button on the top of the resource group page.
3. Search for **Web App** and select **Web App** from the results.
![Create a resource](../../../../images/azure-deploy-create-a-resource.png)
4. Click the **Create** button.
5. Fill in the required fields and click the **Review + create** button.
6. Click the **Create** button.
![Create Web App](../../../../images/azure-deploy-create-web-app-6.png)
7. Wait for the deployment to complete.
![Create Web App](../../../../images/azure-deploy-create-web-app-3.png)
{{ else }}
### Create a new Azure Static Web App for Blazor using the Azure Portal
1. Log in to the [Azure Portal](https://portal.azure.com/).
2. Click the **Create a resource** button.
3. Search for **Static Web App** and select **Static Web App** from the results.
![Create a resource blazor](../../../../images/azure-deploy-create-a-resource-angular.png)
4. Click the **Create** button.
5. Fill in the required fields and click the **Review + create** button.
6. Click the **Create** button.
![Create Web App](../../../../images/azure-deploy-create-web-app-7.png)
7. Wait for the deployment to complete.
![Create Web App](../../../../images/azure-deploy-create-web-app-8.png)
### Create a new Azure Web App Service for API application
1. You can create a new Azure Web App Service for an API application in the same resource group.
2. Click the **Create** button on the top of the resource group page.
3. Search for **Web App** and select **Web App** from the results.
![Create a resource](../../../../images/azure-deploy-create-a-resource.png)
4. Click the **Create** button.
5. Fill in the required fields and click the **Review + create** button.
6. Click the **Create** button.
![Create Web App](../../../../images/azure-deploy-create-web-app-6.png)
7. Wait for the deployment to complete.
![Create Web App](../../../../images/azure-deploy-create-web-app-3.png)
{{end}}
{{ else }}
{{ if UI == "BlazorServer" || UI == "MVC" }}
{{ if UI == "BlazorServer" || UI == "MVC" || UI == "BlazorWebApp"}}
### Create a new Azure Web App service using the Azure Portal
@ -65,8 +160,7 @@ To create a new Azure Web App Service, choose one of the following options:
![Create Web App](../../../../images/azure-deploy-create-web-app-3.png)
{{ else if UI == 'NG' }}
{{ else if UI == 'NG'}}
### Create a new Azure Static Web App for Angular using the Azure Portal
1. Log in to the [Azure Portal](https://portal.azure.com/).
@ -89,7 +183,7 @@ To create a new Azure Web App Service, choose one of the following options:
![Create Web App](../../../../images/azure-deploy-create-web-app-5.png)
{{else}}
{{ else }}
### Create a new Azure Static Web App for Blazor using the Azure Portal
@ -111,8 +205,6 @@ To create a new Azure Web App Service, choose one of the following options:
7. Wait for the deployment to complete.
![Create Web App](../../../../images/azure-deploy-create-web-app-8.png)
{{end}}
### Create a new Azure Web App Service for API application
@ -137,8 +229,6 @@ To create a new Azure Web App Service, choose one of the following options:
![Create Web App](../../../../images/azure-deploy-create-web-app-3.png)
{{ if Tiered == "Yes" && (UI == "MVC" || UI == "BlazorServer")}}
### Create a new Azure Web App Service for AuthServer application
Similar to the API application, you can create a new Azure Web App Service for an AuthServer application in the same resource group.
@ -167,8 +257,6 @@ Same as above, but you only need to modify the name of the web app service to **
{{ end }}
{{ end }}
## What's next?
- [Customizing the Azure Web App Service](step2-configuration-application.md)

216
docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step2-configuration-application.md

@ -1,7 +1,7 @@
````json
//[doc-params]
{
"UI": ["MVC", "Blazor", "BlazorServer", "NG"],
"UI": ["MVC", "Blazor", "BlazorServer", "BlazorWebApp", "NG"],
"DB": ["EF", "Mongo"],
"Tiered": ["Yes", "No"]
}
@ -9,17 +9,26 @@
## Step 2: Customizing the Configuration of the ABP Application
- To customize the configuration of your ABP application, modify the `ConnectionString` values in every location throughout your project. The `ConnectionString` values are stored in the `appsettings.json` files.
#### To customize the configuration of your ABP application
This includes the following files:
{{ if UI == "MVC" && Tiered == "No" }}
**./src/yourapp.DbMigrator/appsettings.json** and **./src/yourapp.Web/appsettings.json**
{{else}}
**./src/yourapp.DbMigrator/appsettings.json** and **./src/yourapp.HttpApi.Host/appsettings.json**
{{end}}
{{if Tiered == "Yes"}}
- Modify the `ConnectionString` values in every location throughout your project. The `ConnectionString` values are stored in the `appsettings.json` files.
* This includes the following files:
**./src/yourapp.DbMigrator/appsettings.json**
{{ if Tiered == "No" }}
{{ if UI == "MVC" }}
**./src/yourapp.Web/appsettings.json**
{{ else if UI == "Blazor" || UI == "BlazorServer" || UI == "BlazorWebApp" }}
and **./src/yourapp.Blazor/appsettings.json**
{{ end }}
{{ if UI == "Blazor"|| UI == "NG" }}
**./src/yourapp.HttpApi.Host/appsettings.json**
{{ end }}
{{ else }}
**./src/yourapp.HttpApi.Host/appsettings.json**
**./src/yourapp.AuthServer/appsettings.json**
{{end}}
{{ end }}
```json
"ConnectionStrings": {
@ -27,35 +36,42 @@
}
```
{{ if UI == "MVC" }}
{{if Tiered == "No"}}
- Modify the **yourapp.Web** URL in every location throughout your project, especially within the **./src/yourapp.Web/appsettings.json** and **./src/yourapp.DbMigrator/appsettings.json** files, to match your Azure Web App Service URL.
* This includes the following files:
**./src/yourapp.DbMigrator/appsettings.json**
{{ if Tiered == "No" }}
{{ if UI == "MVC" }}
**./src/yourapp.Web/appsettings.json**
{{ else if UI == "Blazor" || UI == "BlazorServer" || UI == "BlazorWebApp" }}
and **./src/yourapp.Blazor/appsettings.json**
{{ else }}
Modify the **`localhost:4200`** in every location throughout your project.
**./angular/src/environments/environment.prod.ts**
{{ end }}
{{ if UI == "Blazor"|| UI == "NG" }}
**./src/yourapp.HttpApi.Host/appsettings.json**
{{ end }}
{{ else }}
{{ if UI == "MVC" }}
**./src/yourapp.Web/appsettings.json**
{{ else if UI == "Blazor" || UI == "BlazorServer" || UI == "BlazorWebApp" }}
**./src/yourapp.Blazor/appsettings.json**
{{ else }}
Modify the **`localhost:4200`** in every location throughout your project.
**./angular/src/environments/environment.prod.ts**
{{ end }}
**./src/yourapp.AuthServer/appsettings.json**
```json
"App": {
"SelfUrl": "https://yourapp.azurewebsites.net"
}
```
{{else}}
- Modify the **yourapp.Web** URL in every location throughout your project.
This includes the following files:
**./src/yourapp.Web/appsettings.json** , **./src/yourapp.DbMigrator/appsettings.json** , **./src/yourapp.HttpApi.Host/appsettings.json** and **./src/yourapp.AuthServer/appsettings.json**
```json
"App": {
"SelfUrl": "https://yourapp.azurewebsites.net"
}
```
- Modify the **yourapp.ApiHost** URL in every location throughout your project.
This includes the following files:
* This includes the following files:
**./src/yourapp.HttpApi.Host/appsettings.json** , **./src/yourapp.Web/appsettings.json** , **./src/yourapp.DbMigrator/appsettings.json** and **./src/yourapp.AuthServer/appsettings.json**
@ -67,7 +83,7 @@
- Modify the **yourapp.AuthServer** URL in every location throughout your project.
This includes the following files:
* This includes the following files:
**./src/yourapp.Web/appsettings.json** , **./src/yourapp.AuthServer/appsettings.json** , **./src/yourapp.DbMigrator/appsettings.json** and **./src/yourapp.HttpApi.Host/appsettings.json**
@ -79,139 +95,7 @@
- Modify the **Redis__Configuration** URL in every location throughout your project.
This includes the following files:
**./src/yourapp.Web/appsettings.json** , **./src/yourapp.AuthServer/appsettings.json** , **./src/yourapp.DbMigrator/appsettings.json** and **./src/yourapp.HttpApi.Host/appsettings.json**
```json
"Redis": {
"Configuration": "redis-abpdemo.redis.cache.windows.net:6380,password={yourpassword},ssl=true,abortConnect=False"
},
```
{{end}}
{{ else if UI == "NG" }}
- Modify the **`localhost:4200`** in every location throughout your project.
This includes the following files:
**./angular/src/environments/environment.prod.ts** , **./aspnet-core/src/yourapp.DbMigrator/appsettings.json** and **./aspnet-core/src/yourapp.HttpApi.Host/appsettings.json**
```typescript
application: {
baseUrl: 'https://yourapp.azurestaticapps.net'
}
```
- Modify the **yourapp.HttpApi.Host** URL in every location throughout your project.
This includes the following files:
**./angular/src/environments/environment.prod.ts** , **./aspnet-core/src/yourapp.DbMigrator/appsettings.json** and **./aspnet-core/src/yourapp.HttpApi.Host/appsettings.json**
```json
"App": {
"SelfUrl": "https://yourApiHost.azurewebsites.net"
}
```
{{ else if UI == "Blazor" }}
- Modify the **yourapp.Blazor** URL in every location throughout your project.
This includes the following files:
**./src/yourapp.Blazor/appsettings.json** , **./src/yourapp.DbMigrator/appsettings.json** and **./src/yourapp.HttpApi.Host/appsettings.json**
```json
"App": {
"SelfUrl": "https://yourapp.azurewebsites.net"
}
```
- Modify the **yourapp.HttpApi.Host** URL in every location throughout your project.
This includes the following files:
**./src/yourapp.Blazor/appsettings.json** , **./src/yourapp.DbMigrator/appsettings.json** and **./src/yourapp.HttpApi.Host/appsettings.json**
```json
"App": {
"SelfUrl": "https://yourApiHost.azurewebsites.net"
}
```
{{ else }}
{{if Tiered == "No"}}
- Modify the **yourapp.Web** URL in every location throughout your project.
This includes the following files:
**./src/yourapp.Blazor/appsettings.json** , **./src/yourapp.DbMigrator/appsettings.json** and **./src/yourapp.HttpApi.Host/appsettings.json**
```json
"App": {
"SelfUrl": "https://yourapp.azurewebsites.net"
}
```
- Modify the **yourapp.ApiHost** URL in every location throughout your project.
This includes the following files:
**./src/yourapp.HttpApi.Host/appsettings.json** , **./src/yourapp.Blazor/appsettings.json** and **./src/yourapp.DbMigrator/appsettings.json**
```json
"App": {
"SelfUrl": "https://yourapp-apihost.azurewebsites.net"
}
```
{{else}}
- Modify the **yourapp.Web** URL in every location throughout your project.
This includes the following files:
**./src/yourapp.Blazor/appsettings.json** , **./src/yourapp.DbMigrator/appsettings.json** , **./src/yourapp.HttpApi.Host/appsettings.json** and **./src/yourapp.AuthServer/appsettings.json**
```json
"App": {
"SelfUrl": "https://yourapp.azurewebsites.net"
}
```
- Modify the **yourapp.ApiHost** URL in every location throughout your project.
This includes the following files:
**./src/yourapp.HttpApi.Host/appsettings.json** , **./src/yourapp.Blazor/appsettings.json** , **./src/yourapp.DbMigrator/appsettings.json** and **./src/yourapp.AuthServer/appsettings.json**
```json
"App": {
"SelfUrl": "https://yourapp-apihost.azurewebsites.net"
}
```
- Modify the **yourapp.AuthServer** URL in every location throughout your project.
This includes the following files:
**./src/yourapp.Blazor/appsettings.json** , **./src/yourapp.AuthServer/appsettings.json** , **./src/yourapp.DbMigrator/appsettings.json** and **./src/yourapp.HttpApi.Host/appsettings.json**
```json
"App": {
"SelfUrl": "https://yourapp-authserver.azurewebsites.net"
}
```
- Modify the **Redis__Configuration** URL in every location throughout your project.
This includes the following files:
* This includes the following files:
**./src/yourapp.Blazor/appsettings.json** , **./src/yourapp.AuthServer/appsettings.json** , **./src/yourapp.DbMigrator/appsettings.json** and **./src/yourapp.HttpApi.Host/appsettings.json**
@ -220,11 +104,7 @@
"Configuration": "redis-abpdemo.redis.cache.windows.net:6380,password={yourpassword},ssl=true,abortConnect=False"
},
```
{{end}}
{{end}}
{{ end }}
## What's next?

263
docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md

@ -1,7 +1,7 @@
````json
//[doc-params]
{
"UI": ["MVC", "Blazor", "BlazorServer", "NG"],
"UI": ["MVC", "Blazor", "BlazorServer", "BlazorWebApp", "NG"],
"DB": ["EF", "Mongo"],
"Tiered": ["Yes", "No"]
}
@ -70,8 +70,8 @@ jobs:
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/apihost
working-directory: ./aspnet-core/src/Demo.AzureAppsAngular.HttpApi.Host # Replace with your project name
- name: Generate authserver.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/apihost/authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: Generate openiddict.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/apihost/openiddict.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: Upload artifact for apihost
uses: actions/upload-artifact@v4
@ -170,8 +170,8 @@ jobs:
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/apihost
working-directory: ./src/demo.BlazorNonTierEfCore.HttpApi.Host # Replace with your project name
- name: Generate authserver.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/apihost/authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: Generate openiddict.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/apihost/openiddict.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: Upload artifact for apihost
uses: actions/upload-artifact@v4
@ -234,6 +234,96 @@ jobs:
name: Build and deploy ASP.Net Core with BlazorServer to Azure Web App
on:
push:
branches:
- main
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read #This is required for actions/checkout
steps:
- uses: actions/checkout@v4
- name: Set up .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.x'
- name: Install ABP CLI
run: |
dotnet tool install -g Volo.Abp.Cli
abp install-libs
shell: bash
- name: Build with dotnet
run: dotnet build --configuration Release
- name: Run migrations
run: dotnet run
working-directory: ./src/BlazorServer.NonTiered.DbMigrator
- name: dotnet publish
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp
working-directory: './src/BlazorServer.NonTiered.Blazor'
- name: Generate openiddict.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/myapp/openiddict.pfx -p 7015b85e-89fc-4346-bfd0-a67d81de824e # Replace with your password
- name: Upload artifact for deployment job
uses: actions/upload-artifact@v4
with:
name: .net-app
path: ${{env.DOTNET_ROOT}}/myapp
deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
permissions:
id-token: write #This is required for requesting the JWT
contents: read #This is required for actions/checkout
steps:
- name: Download artifact from build job
uses: actions/download-artifact@v4
with:
name: .net-app
- name: Login to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_E74C791E153A4F38A50107C6B5341809 }}
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_7EC2684BA1FB43F1B563AD3832D0A5AC }}
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_81FB702F1C8F43388B145043765CC189 }}
- name: Deploy to Azure Web App
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: 'abp-blazor-webapp-layered'
slot-name: 'Production'
package: .
```
}%}
{{ else }}
{%{
```yaml
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions
name: Build and deploy ASP.Net Core with BlazorServer to Azure Web App
on:
push:
branches:
@ -266,17 +356,27 @@ jobs:
run: dotnet run -- "${{ secrets.CONNECTION_STRING }}" # Set your connection string as a secret in your repository settings
working-directory: ./src/blazorservertierdemo.DbMigrator # Replace with your project name
- name: dotnet publish authserver
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/authserver
working-directory: ./src/blazorservertierdemo.AuthServer # Replace with your project name
- name: Generate openiddict.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/authserver/openiddict.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: dotnet publish apihost
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/apihost
working-directory: ./src/blazorservertierdemo.HttpApi.Host # Replace with your project name
- name: Generate authserver.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/apihost/authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: dotnet publish webapp
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/webapp
working-directory: ./src/blazorservertierdemo.Blazor # Replace with your project name
- name: Upload artifact for authserver
uses: actions/upload-artifact@v4
with:
name: .net-authserver
path: ${{env.DOTNET_ROOT}}/authserver
- name: Upload artifact for apihost
uses: actions/upload-artifact@v4
with:
@ -295,7 +395,23 @@ jobs:
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp-3.outputs.webapp-url }}
steps:
- name: Download artifact from authserver
uses: actions/download-artifact@v4
with:
name: .net-authserver
path: ./authserver
- name: Deploy authserver
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: 'authserver-blazorserver' # Replace with your app name
slot-name: 'Production'
publish-profile: ${{ secrets.authserverblazorserverPublishSettings }} # Set your Azure Web App to publish your profile as a secret in your repository settings
package: ./authserver
- name: Download artifact from apihost
uses: actions/download-artifact@v4
with:
@ -323,8 +439,101 @@ jobs:
with:
app-name: 'webapp-blazorserver' # Replace with your app name
slot-name: 'Production'
publish-profile: ${{ secrets.webappblazorserverPublishSettings }} # Set your Azure Web App to publish your profile as a secret in your repository settings
package: ./webapp
publish-profile: ${{ secrets.webappblazorserverPublishSettings }} # Set your Azure Web App publish your profile as a secret in your repository settings
```
}%}
{{end}}
{{ else if UI == "BlazorWebApp" }}
{{ if Tiered == "No" }}
{%{
```yaml
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions
name: Build and deploy ASP.Net Core app to Azure Web App - abp-blazor-webapp-layered
on:
push:
branches:
- main
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read #This is required for actions/checkout
steps:
- uses: actions/checkout@v4
- name: Set up .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.x'
- name: Install ABP CLI
run: |
dotnet tool install -g Volo.Abp.Cli
abp install-libs
shell: bash
- name: Build with dotnet
run: dotnet build --configuration Release
- name: Run migrations
run: dotnet run
working-directory: ./src/BlzWapp.NonTiered.DbMigrator
- name: dotnet publish
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp
working-directory: './src/BlzWapp.NonTiered.Blazor'
- name: Generate openiddict.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/myapp/openiddict.pfx -p 7015b85e-89fc-4346-bfd0-a67d81de824e # Replace with your password
- name: Upload artifact for deployment job
uses: actions/upload-artifact@v4
with:
name: .net-app
path: ${{env.DOTNET_ROOT}}/myapp
deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
permissions:
id-token: write #This is required for requesting the JWT
contents: read #This is required for actions/checkout
steps:
- name: Download artifact from build job
uses: actions/download-artifact@v4
with:
name: .net-app
- name: Login to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_E74C791E153A4F38A50107C6B5341809 }}
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_7EC2684BA1FB43F1B563AD3832D0A5AC }}
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_81FB702F1C8F43388B145043765CC189 }}
- name: Deploy to Azure Web App
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: 'abp-blazor-webapp-layered'
slot-name: 'Production'
package: .
```
}%}
@ -337,7 +546,7 @@ jobs:
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions
name: Build and deploy ASP.Net Core with BlazorServer to Azure Web App
name: Build and deploy ASP.Net Core with BlazorWebApp to Azure Web App
on:
push:
@ -369,22 +578,22 @@ jobs:
- name: Run migrations
run: dotnet run -- "${{ secrets.CONNECTION_STRING }}" # Set your connection string as a secret in your repository settings
working-directory: ./src/blazorservertierdemo.DbMigrator # Replace with your project name
working-directory: ./src/BlzWapp.DbMigrator # Replace with your project name
- name: dotnet publish authserver
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/authserver
working-directory: ./src/blazorservertierdemo.AuthServer # Replace with your project name
working-directory: ./src/BlzWapp.AuthServer # Replace with your project name
- name: Generate authserver.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/authserver/authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: Generate openiddict.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/authserver/openiddict.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: dotnet publish apihost
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/apihost
working-directory: ./src/blazorservertierdemo.HttpApi.Host # Replace with your project name
working-directory: ./src/BlzWapp.HttpApi.Host # Replace with your project name
- name: dotnet publish webapp
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/webapp
working-directory: ./src/blazorservertierdemo.Blazor # Replace with your project name
working-directory: ./src/BlzWapp.Blazor # Replace with your project name
- name: Upload artifact for authserver
uses: actions/upload-artifact@v4
@ -422,9 +631,9 @@ jobs:
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: 'authserver-blazorserver' # Replace with your app name
app-name: 'authserver-blazorwebapp' # Replace with your app name
slot-name: 'Production'
publish-profile: ${{ secrets.authserverblazorserverPublishSettings }} # Set your Azure Web App to publish your profile as a secret in your repository settings
publish-profile: ${{ secrets.authserverblazorwebappPublishSettings }} # Set your Azure Web App to publish your profile as a secret in your repository settings
package: ./authserver
- name: Download artifact from apihost
@ -437,9 +646,9 @@ jobs:
id: deploy-to-webapp-2
uses: azure/webapps-deploy@v3
with:
app-name: 'apihost-blazorserver' # Replace with your app name
app-name: 'apihost-blazorwebapp' # Replace with your app name
slot-name: 'Production'
publish-profile: ${{ secrets.apihostblazorserverPublishSettings }} # Set your Azure Web App to publish your profile as a secret in your repository settings
publish-profile: ${{ secrets.apihostblazorwebappPublishSettings }} # Set your Azure Web App to publish your profile as a secret in your repository settings
package: ./apihost
- name: Download artifact from webapp
@ -452,9 +661,9 @@ jobs:
id: deploy-to-webapp-3
uses: azure/webapps-deploy@v3
with:
app-name: 'webapp-blazorserver' # Replace with your app name
app-name: 'webapp-blazorwebapp' # Replace with your app name
slot-name: 'Production'
publish-profile: ${{ secrets.webappblazorserverPublishSettings }} # Set your Azure Web App publish your profile as a secret in your repository settings
publish-profile: ${{ secrets.webappblazorwebappPublishSettings }} # Set your Azure Web App publish your profile as a secret in your repository settings
```
}%}
@ -509,8 +718,8 @@ jobs:
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp
working-directory: ./src/yourapp.Web # Replace with your project name
- name: Generate authserver.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/myapp/authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: Generate openiddict.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/myapp/openiddict.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: Upload artifact for deployment job
uses: actions/upload-artifact@v4
@ -589,8 +798,8 @@ jobs:
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/authserver
working-directory: ./src/mvctierdemo.AuthServer # Replace with your project name
- name: Generate authserver.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/authserver/authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: Generate openiddict.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}/authserver/openiddict.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED # Replace with your password
- name: dotnet publish apihost
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/apihost

138
docs/en/solution-templates/layered-web-application/deployment/azure-deployment/terraform-web-app-service.md

@ -3,7 +3,7 @@
````json
//[doc-params]
{
"UI": ["MVC", "Blazor", "BlazorServer", "NG"],
"UI": ["MVC", "Blazor", "BlazorServer", "BlazorWebApp", "NG"],
"DB": ["EF", "Mongo"],
"Tiered": ["Yes", "No"]
}
@ -224,6 +224,53 @@ resource "azurerm_service_plan" "appserviceplan" {
sku_name = "B3"
}
resource "azurerm_linux_web_app" "webapp" {
name = "webapp-blazorserver"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
service_plan_id = azurerm_service_plan.appserviceplan.id
https_only = true
site_config {
application_stack {
dotnet_version = "6.0"
}
minimum_tls_version = "1.2"
}
}
```
{{ else }}
```terraform
# Configure the Azure provider
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0.0"
}
}
required_version = ">= 0.14.9"
}
provider "azurerm" {
features {}
}
# Create the resource group
resource "azurerm_resource_group" "rg" {
name = "blazorserver-app-tier-rg"
location = "westeurope"
}
# Create the Linux App Service Plan
resource "azurerm_service_plan" "appserviceplan" {
name = "blazorserver-app-tier-plan"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
os_type = "Linux"
sku_name = "B3"
}
# Create the web app, pass in the App Service Plan ID
resource "azurerm_linux_web_app" "authserver" {
name = "authserver-blazorserver"
@ -237,6 +284,9 @@ resource "azurerm_linux_web_app" "authserver" {
}
minimum_tls_version = "1.2"
}
app_settings = {
"Redis__Configuration" = azurerm_redis_cache.redis.primary_connection_string
}
}
resource "azurerm_linux_web_app" "apihost" {
name = "apihost-blazorserver"
@ -250,6 +300,9 @@ resource "azurerm_linux_web_app" "apihost" {
}
minimum_tls_version = "1.2"
}
app_settings = {
"Redis__Configuration" = azurerm_redis_cache.redis.primary_connection_string
}
}
resource "azurerm_linux_web_app" "webapp" {
name = "webapp-blazorserver"
@ -263,6 +316,77 @@ resource "azurerm_linux_web_app" "webapp" {
}
minimum_tls_version = "1.2"
}
app_settings = {
"Redis__Configuration" = azurerm_redis_cache.redis.primary_connection_string
}
}
resource "azurerm_redis_cache" "redis" {
name = "redis-blazorserver"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
capacity = 0
family = "C"
sku_name = "Basic"
enable_non_ssl_port = false
minimum_tls_version = "1.2"
redis_configuration {
maxmemory_reserved = 2
maxmemory_delta = 2
maxmemory_policy = "volatile-lru"
}
}
```
{{end}}
{{ else if UI == "BlazorWebApp" }}
{{if Tiered == "No"}}
```terraform
# Configure the Azure provider
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0.0"
}
}
required_version = ">= 0.14.9"
}
provider "azurerm" {
features {}
}
# Create the resource group
resource "azurerm_resource_group" "rg" {
name = "blazorwebapp-app-nontier-rg"
location = "westeurope"
}
# Create the Linux App Service Plan
resource "azurerm_service_plan" "appserviceplan" {
name = "blazorwebapp-app-nontier-plan"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
os_type = "Linux"
sku_name = "B3"
}
resource "azurerm_linux_web_app" "webapp" {
name = "webapp-blazorwebapp"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
service_plan_id = azurerm_service_plan.appserviceplan.id
https_only = true
site_config {
application_stack {
dotnet_version = "6.0"
}
minimum_tls_version = "1.2"
}
}
```
@ -285,13 +409,13 @@ provider "azurerm" {
# Create the resource group
resource "azurerm_resource_group" "rg" {
name = "blazorserver-app-tier-rg"
name = "blazorwebapp-app-tier-rg"
location = "westeurope"
}
# Create the Linux App Service Plan
resource "azurerm_service_plan" "appserviceplan" {
name = "blazorserver-app-tier-plan"
name = "blazorwebapp-app-tier-plan"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
os_type = "Linux"
@ -300,7 +424,7 @@ resource "azurerm_service_plan" "appserviceplan" {
# Create the web app, pass in the App Service Plan ID
resource "azurerm_linux_web_app" "authserver" {
name = "authserver-blazorserver"
name = "authserver-blazorwebapp"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
service_plan_id = azurerm_service_plan.appserviceplan.id
@ -316,7 +440,7 @@ resource "azurerm_linux_web_app" "authserver" {
}
}
resource "azurerm_linux_web_app" "apihost" {
name = "apihost-blazorserver"
name = "apihost-blazorwebapp"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
service_plan_id = azurerm_service_plan.appserviceplan.id
@ -332,7 +456,7 @@ resource "azurerm_linux_web_app" "apihost" {
}
}
resource "azurerm_linux_web_app" "webapp" {
name = "webapp-blazorserver"
name = "webapp-blazorwebapp"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
service_plan_id = azurerm_service_plan.appserviceplan.id
@ -349,7 +473,7 @@ resource "azurerm_linux_web_app" "webapp" {
}
resource "azurerm_redis_cache" "redis" {
name = "redis-blazorserver"
name = "redis-blazorwebapp"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
capacity = 0

4
docs/en/tutorials/microservice/part-01.md

@ -21,7 +21,7 @@ Follow the *[Get Started](../../get-started/microservice.md)* guide to create a
You can select the other options based on your preference.
> **Please complete the *[Get Started](../../get-started/layered-web-application.md)* guide and run the web application before going further.** You can skip the sections after the *Running the Solution* section, if you don't prefer to complete all.
> **Please complete the *[Get Started](../../get-started/microservice.md)* guide and run the applications before going further.** You can skip the sections after the *Running the Solution* section, if you don't prefer to complete all.
The initial solution structure should be like the following in ABP Studio's *[Solution Explorer](../../studio/solution-explorer.md)*:
@ -37,4 +37,4 @@ You can see the *[Microservice Solution Template](../../solution-templates/micro
## Summary
In this part, you've created the initial microservice solution, which already contains a few infrastructure services. We will create our first business service in the [next part](part-02.md).
In this part, you've created the initial microservice solution, which already contains a few infrastructure services. We will create our first business service in the [next part](part-02.md).

4
framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs

@ -85,10 +85,10 @@ public abstract class AbpComponentBase : OwningComponentBase
if (ObjectMapperContext == null)
{
return LazyGetRequiredService(ref _objectMapper)!;
return LazyGetNonScopedRequiredService(ref _objectMapper)!;
}
return LazyGetRequiredService(
return LazyGetNonScopedRequiredService(
typeof(IObjectMapper<>).MakeGenericType(ObjectMapperContext),
ref _objectMapper
)!;

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

@ -104,6 +104,7 @@ $.validator.defaults.ignore = ''; //TODO: Would be better if we can apply only f
}
}
var focusElement = null;
if (!options.focusElement) {
//focuses first element if it's a typeable input.
var $firstVisibleInput = _$modal.find('input:not([type=hidden]):first');
@ -120,11 +121,18 @@ $.validator.defaults.ignore = ''; //TODO: Would be better if we can apply only f
}
$firstVisibleInput.focus();
focusElement = $firstVisibleInput;
} else if (typeof options.focusElement === 'function') {
var focusElement = options.focusElement();
focusElement = options.focusElement();
focusElement.focus();
} else if (typeof options.focusElement === 'string') {
$(options.focusElement).focus();
focusElement = $(options.focusElement);
focusElement.focus();
}
if (focusElement && $(focusElement).is('input')) {
var value = focusElement.val();
focusElement[0].setSelectionRange(value.length, value.length);
}
});

7
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/date-range-picker/date-range-picker-extensions.js

@ -438,6 +438,13 @@
date = date.local();
}
if (options.isIso) {
if(abp.clock.supportsMultipleTimezone()){
var timeZone = abp.clock.timeZone();
if(timeZone){
return abp.clock.normalizeToString(date.toDate());
}
}
return date.toISOString();
}
return date.locale('en').format(options.inputDateFormat)

1
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs

@ -4,7 +4,6 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DeviceDetectorNET;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.DependencyInjection;

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

@ -31,7 +31,7 @@
<ProjectReference Include="..\Volo.Abp.VirtualFileSystem\Volo.Abp.VirtualFileSystem.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />
<PackageReference Include="IdentityModel" />
<PackageReference Include="DeviceDetector.NET" />
<PackageReference Include="MyCSharp.HttpUserAgentParser" />
</ItemGroup>
</Project>

3
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs

@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Hosting.StaticWebAssets;
using Microsoft.AspNetCore.RequestLocalization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using MyCSharp.HttpUserAgentParser.DependencyInjection;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.AspNetCore.VirtualFileSystem;
using Volo.Abp.Auditing;
@ -59,6 +60,8 @@ public class AbpAspNetCoreModule : AbpModule
context.Services.AddAbpDynamicOptions<RequestLocalizationOptions, AbpRequestLocalizationOptionsManager>();
StaticWebAssetsLoader.UseStaticWebAssets(context.Services.GetHostingEnvironment(), context.Services.GetConfiguration());
context.Services.AddHttpUserAgentCachedParser();
}
private static void AddAspNetServices(IServiceCollection services)

36
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/HttpContextWebClientInfoProvider.cs

@ -1,7 +1,8 @@
using System;
using DeviceDetectorNET;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using MyCSharp.HttpUserAgentParser;
using MyCSharp.HttpUserAgentParser.Providers;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.WebClientInfo;
@ -11,13 +12,16 @@ public class HttpContextWebClientInfoProvider : IWebClientInfoProvider, ITransie
{
protected ILogger<HttpContextWebClientInfoProvider> Logger { get; }
protected IHttpContextAccessor HttpContextAccessor { get; }
protected IHttpUserAgentParserProvider HttpUserAgentParser { get; }
public HttpContextWebClientInfoProvider(
ILogger<HttpContextWebClientInfoProvider> logger,
IHttpContextAccessor httpContextAccessor)
IHttpContextAccessor httpContextAccessor,
IHttpUserAgentParserProvider httpUserAgentParser)
{
Logger = logger;
HttpContextAccessor = httpContextAccessor;
HttpUserAgentParser = httpUserAgentParser;
}
public string? BrowserInfo => GetBrowserInfo();
@ -46,27 +50,21 @@ public class HttpContextWebClientInfoProvider : IWebClientInfoProvider, ITransie
protected virtual string? GetDeviceInfo()
{
string? deviceInfo = null;
var deviceDetector = new DeviceDetector(GetBrowserInfo());
deviceDetector.Parse();
if (!deviceDetector.IsParsed())
var browserInfo = GetBrowserInfo();
if (browserInfo.IsNullOrWhiteSpace())
{
return deviceInfo;
}
var osInfo = deviceDetector.GetOs();
if (osInfo.Success)
{
deviceInfo = osInfo.Match.Name;
return null;
}
var clientInfo = deviceDetector.GetClient();
if (clientInfo.Success)
var httpUserAgentInformation = HttpUserAgentParser.Parse(browserInfo);
switch (httpUserAgentInformation.Type)
{
deviceInfo = deviceInfo.IsNullOrWhiteSpace() ? clientInfo.Match.Name : deviceInfo + " " + clientInfo.Match.Name;
case HttpUserAgentType.Browser:
case HttpUserAgentType.Robot:
return (httpUserAgentInformation.Platform.HasValue ? httpUserAgentInformation.Platform.Value.Name + " " : string.Empty) + httpUserAgentInformation.Name;
case HttpUserAgentType.Unknown:
default:
return httpUserAgentInformation.UserAgent;
}
return deviceInfo;
}
}

9
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs

@ -14,10 +14,17 @@ public class AbpBackgroundJobOptions
/// </summary>
public bool IsJobExecutionEnabled { get; set; } = true;
/// <summary>
/// The delegate to get the name of a background job.
/// Default: <see cref="BackgroundJobNameAttribute.GetName"/>.
/// </summary>
public Func<Type, string> GetBackgroundJobName { get; set; }
public AbpBackgroundJobOptions()
{
_jobConfigurationsByArgsType = new Dictionary<Type, BackgroundJobConfiguration>();
_jobConfigurationsByName = new Dictionary<string, BackgroundJobConfiguration>();
GetBackgroundJobName = BackgroundJobNameAttribute.GetName;
}
public BackgroundJobConfiguration GetJob<TArgs>()
@ -61,7 +68,7 @@ public class AbpBackgroundJobOptions
public void AddJob(Type jobType)
{
AddJob(new BackgroundJobConfiguration(jobType));
AddJob(new BackgroundJobConfiguration(jobType, GetBackgroundJobName(BackgroundJobArgsHelper.GetJobArgsType(jobType))));
}
public void AddJob(BackgroundJobConfiguration jobConfiguration)

4
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobConfiguration.cs

@ -10,10 +10,10 @@ public class BackgroundJobConfiguration
public string JobName { get; }
public BackgroundJobConfiguration(Type jobType)
public BackgroundJobConfiguration(Type jobType, string jobName)
{
JobType = jobType;
ArgsType = BackgroundJobArgsHelper.GetJobArgsType(jobType);
JobName = BackgroundJobNameAttribute.GetName(ArgsType);
JobName = jobName;
}
}

5
framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs

@ -18,6 +18,7 @@ public class DefaultBackgroundJobManager : IBackgroundJobManager, ITransientDepe
protected IBackgroundJobSerializer Serializer { get; }
protected IGuidGenerator GuidGenerator { get; }
protected IBackgroundJobStore Store { get; }
protected IOptions<AbpBackgroundJobOptions> BackgroundJobOptions { get; }
protected IOptions<AbpBackgroundJobWorkerOptions> BackgroundJobWorkerOptions { get; }
public DefaultBackgroundJobManager(
@ -25,18 +26,20 @@ public class DefaultBackgroundJobManager : IBackgroundJobManager, ITransientDepe
IBackgroundJobSerializer serializer,
IBackgroundJobStore store,
IGuidGenerator guidGenerator,
IOptions<AbpBackgroundJobOptions> backgroundJobOptions,
IOptions<AbpBackgroundJobWorkerOptions> backgroundJobWorkerOptions)
{
Clock = clock;
Serializer = serializer;
GuidGenerator = guidGenerator;
BackgroundJobOptions = backgroundJobOptions;
BackgroundJobWorkerOptions = backgroundJobWorkerOptions;
Store = store;
}
public virtual async Task<string> EnqueueAsync<TArgs>(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null)
{
var jobName = BackgroundJobNameAttribute.GetName<TArgs>();
var jobName = BackgroundJobOptions.Value.GetBackgroundJobName(typeof(TArgs));
var jobId = await EnqueueAsync(jobName, args!, priority, delay);
return jobId.ToString();
}

74
framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs

@ -1,14 +1,15 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Hangfire;
using Hangfire.Common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Hangfire;
using Volo.Abp.Threading;
namespace Volo.Abp.BackgroundWorkers.Hangfire;
@ -55,40 +56,40 @@ public class HangfireBackgroundWorkerManager : BackgroundWorkerManager, ISinglet
}
case AsyncPeriodicBackgroundWorkerBase or PeriodicBackgroundWorkerBase:
{
var timer = worker.GetType().GetProperty("Timer", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(worker);
var period = worker is AsyncPeriodicBackgroundWorkerBase ? ((AbpAsyncTimer?)timer)?.Period : ((AbpTimer?)timer)?.Period;
int? period = null;
string? CronExpression = null;
if (period == null)
if (worker is AsyncPeriodicBackgroundWorkerBase asyncPeriodicBackgroundWorkerBase)
{
return;
period = asyncPeriodicBackgroundWorkerBase.Period;
CronExpression = asyncPeriodicBackgroundWorkerBase.CronExpression;
}
var adapterType = typeof(HangfirePeriodicBackgroundWorkerAdapter<>).MakeGenericType(ProxyHelper.GetUnProxiedType(worker));
var workerAdapter = (Activator.CreateInstance(adapterType) as IHangfireBackgroundWorker)!;
if (workerAdapter.RecurringJobId.IsNullOrWhiteSpace())
else if (worker is PeriodicBackgroundWorkerBase periodicBackgroundWorkerBase)
{
RecurringJob.AddOrUpdate(
() => workerAdapter.DoWorkAsync(cancellationToken),
GetCron(period.Value),
workerAdapter.TimeZone ,
workerAdapter.Queue.IsNullOrWhiteSpace() ? defaultQueue : defaultQueuePrefix + workerAdapter.Queue);
period = periodicBackgroundWorkerBase.Period;
CronExpression = periodicBackgroundWorkerBase.CronExpression;
}
else
{
RecurringJob.AddOrUpdate(
workerAdapter.RecurringJobId,
workerAdapter.Queue.IsNullOrWhiteSpace() ? defaultQueue : defaultQueuePrefix + workerAdapter.Queue,
() => workerAdapter.DoWorkAsync(cancellationToken),
GetCron(period.Value),
new RecurringJobOptions
{
TimeZone = workerAdapter.TimeZone
});
if (period == null && CronExpression.IsNullOrWhiteSpace())
{
return;
}
var adapterType = typeof(HangfirePeriodicBackgroundWorkerAdapter<>).MakeGenericType(ProxyHelper.GetUnProxiedType(worker));
var workerAdapter = (Activator.CreateInstance(adapterType) as IHangfireBackgroundWorker)!;
Expression<Func<Task>> methodCall = () => workerAdapter.DoWorkAsync(cancellationToken);
var recurringJobId = !workerAdapter.RecurringJobId.IsNullOrWhiteSpace() ? workerAdapter.RecurringJobId : GetRecurringJobId(worker, methodCall);
RecurringJob.AddOrUpdate(
recurringJobId,
workerAdapter.Queue.IsNullOrWhiteSpace() ? defaultQueue : defaultQueuePrefix + workerAdapter.Queue,
methodCall,
CronExpression ?? GetCron(period!.Value),
new RecurringJobOptions
{
TimeZone = workerAdapter.TimeZone
});
break;
}
default:
@ -97,6 +98,24 @@ public class HangfireBackgroundWorkerManager : BackgroundWorkerManager, ISinglet
}
}
private readonly static MethodInfo? GetRecurringJobIdMethodInfo = typeof(RecurringJob).GetMethod("GetRecurringJobId", BindingFlags.NonPublic | BindingFlags.Static);
protected virtual string? GetRecurringJobId(IBackgroundWorker worker, Expression<Func<Task>> methodCall)
{
string? recurringJobId = null;
if (GetRecurringJobIdMethodInfo != null)
{
var job = Job.FromExpression(methodCall);
recurringJobId = (string)GetRecurringJobIdMethodInfo.Invoke(null, [job])!;
}
if (recurringJobId.IsNullOrWhiteSpace())
{
recurringJobId = $"HangfirePeriodicBackgroundWorkerAdapter<{worker.GetType().Name}>.DoWorkAsync";
}
return recurringJobId;
}
protected virtual string GetCron(int period)
{
var time = TimeSpan.FromMilliseconds(period);
@ -120,8 +139,7 @@ public class HangfireBackgroundWorkerManager : BackgroundWorkerManager, ISinglet
}
else
{
throw new AbpException(
$"Cannot convert period: {period} to cron expression, use HangfireBackgroundWorkerBase to define worker");
throw new AbpException($"Cannot convert period: {period} to cron expression, use HangfireBackgroundWorkerBase to define worker");
}
return cron;

46
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzPeriodicBackgroundWorkerAdapter.cs

@ -3,8 +3,6 @@ using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Threading;
namespace Volo.Abp.BackgroundWorkers.Quartz;
@ -25,28 +23,23 @@ public class QuartzPeriodicBackgroundWorkerAdapter<TWorker> : QuartzBackgroundWo
}
public void BuildWorker(IBackgroundWorker worker)
public virtual void BuildWorker(IBackgroundWorker worker)
{
int? period;
var workerType = ProxyHelper.GetUnProxiedType(worker);
int? period = null;
string? CronExpression = null;
if (worker is AsyncPeriodicBackgroundWorkerBase or PeriodicBackgroundWorkerBase)
if (worker is AsyncPeriodicBackgroundWorkerBase asyncPeriodicBackgroundWorkerBase)
{
if (typeof(TWorker) != workerType)
{
throw new ArgumentException($"{nameof(worker)} type is different from the generic type");
}
var timer = workerType.GetProperty("Timer", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(worker);
period = worker is AsyncPeriodicBackgroundWorkerBase ? ((AbpAsyncTimer?)timer)?.Period : ((AbpTimer?)timer)?.Period;
period = asyncPeriodicBackgroundWorkerBase.Period;
CronExpression = asyncPeriodicBackgroundWorkerBase.CronExpression;
}
else
else if (worker is PeriodicBackgroundWorkerBase periodicBackgroundWorkerBase)
{
return;
period = periodicBackgroundWorkerBase.Period;
CronExpression = periodicBackgroundWorkerBase.CronExpression;
}
if (period == null)
if (period == null && CronExpression.IsNullOrWhiteSpace())
{
return;
}
@ -55,10 +48,21 @@ public class QuartzPeriodicBackgroundWorkerAdapter<TWorker> : QuartzBackgroundWo
.Create<QuartzPeriodicBackgroundWorkerAdapter<TWorker>>()
.WithIdentity(BackgroundWorkerNameAttribute.GetName<TWorker>())
.Build();
Trigger = TriggerBuilder.Create()
.WithIdentity(BackgroundWorkerNameAttribute.GetName<TWorker>())
.WithSimpleSchedule(builder => builder.WithInterval(TimeSpan.FromMilliseconds(period.Value)).RepeatForever())
.Build();
var triggerBuilder = TriggerBuilder.Create()
.ForJob(JobDetail)
.WithIdentity(BackgroundWorkerNameAttribute.GetName<TWorker>());
if (!CronExpression.IsNullOrWhiteSpace())
{
triggerBuilder.WithCronSchedule(CronExpression);
}
else
{
triggerBuilder.WithSimpleSchedule(builder => builder.WithInterval(TimeSpan.FromMilliseconds(period!.Value)).RepeatForever());
}
Trigger = triggerBuilder.Build();
}
public async override Task Execute(IJobExecutionContext context)

5
framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AsyncPeriodicBackgroundWorkerBase.cs

@ -13,6 +13,11 @@ public abstract class AsyncPeriodicBackgroundWorkerBase : BackgroundWorkerBase
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected AbpAsyncTimer Timer { get; }
protected CancellationToken StartCancellationToken { get; set; }
public int Period => Timer.Period;
/// <summary>
/// CronExpression has high priority over Period.
/// </summary>
public string? CronExpression { get; protected set; }
protected AsyncPeriodicBackgroundWorkerBase(
AbpAsyncTimer timer,

9
framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs

@ -15,6 +15,11 @@ public abstract class PeriodicBackgroundWorkerBase : BackgroundWorkerBase
{
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected AbpTimer Timer { get; }
public int Period => Timer.Period;
/// <summary>
/// CronExpression has high priority over Period.
/// </summary>
public string? CronExpression { get; protected set; }
protected PeriodicBackgroundWorkerBase(
AbpTimer timer,
@ -25,13 +30,13 @@ public abstract class PeriodicBackgroundWorkerBase : BackgroundWorkerBase
Timer.Elapsed += Timer_Elapsed;
}
public override async Task StartAsync(CancellationToken cancellationToken = default)
public async override Task StartAsync(CancellationToken cancellationToken = default)
{
await base.StartAsync(cancellationToken);
Timer.Start(cancellationToken);
}
public override async Task StopAsync(CancellationToken cancellationToken = default)
public async override Task StopAsync(CancellationToken cancellationToken = default)
{
Timer.Stop(cancellationToken);
await base.StopAsync(cancellationToken);

9
framework/src/Volo.Abp.BlazoriseUI/wwwroot/volo.abp.blazoriseui.css

@ -61,4 +61,11 @@
.table-responsive .dropdown-menu-position-strategy-fixed {
position: fixed !important;
}
}
/*
blazorise picker workaround for https://github.com/Megabit/Blazorise/issues/4917
*/
.card .flatpickr-calendar.static {
top: unset !important;
}

7
framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj

@ -16,23 +16,26 @@
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" />
<PackageReference Include="SharpZipLib" />
<PackageReference Include="NuGet.Versioning" />
<PackageReference Include="System.Text.Encoding.CodePages" />
<PackageReference Include="System.Security.Permissions" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
<PackageReference Include="Polly" />
<PackageReference Include="Polly.Extensions.Http" />
<PackageReference Include="LibGit2Sharp" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="StackExchange.Redis" />
<PackageReference Include="DeepL.net" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1'">
<PackageReference Include="System.Text.Encoding.CodePages" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
<ProjectReference Include="..\Volo.Abp.Http\Volo.Abp.Http.csproj" />
<ProjectReference Include="..\Volo.Abp.IdentityModel\Volo.Abp.IdentityModel.csproj" />
<ProjectReference Include="..\Volo.Abp.Json\Volo.Abp.Json.csproj" />
<ProjectReference Include="..\Volo.Abp.Localization\Volo.Abp.Localization.csproj" />
<PackageReference Include="Newtonsoft.Json" />
</ItemGroup>
</Project>

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Version/PackageVersionCheckerService.cs

@ -247,7 +247,7 @@ public class PackageVersionCheckerService : ITransientDependency
var content = await responseMessage.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<List<LatestStableVersionResult>>(content);
return result.OrderBy(q => q.Type.ToLowerInvariant() == "stable").ThenBy(q => SemanticVersion.Parse(q.Version)).ToList();
return result.Where(q => q.Type.ToLowerInvariant() == "stable").OrderByDescending(q => SemanticVersion.Parse(q.Version)).ToList();
}
}
catch

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

@ -15,7 +15,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" />
<PackageReference Include="Serilog.Extensions.Logging" />
<PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Serilog.Sinks.Console" />

4
framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLoggingExtensions.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Logging;
namespace Microsoft.Extensions.DependencyInjection;
@ -7,6 +8,7 @@ public static class ServiceCollectionLoggingExtensions
{
public static ILogger<T> GetInitLogger<T>(this IServiceCollection services)
{
return services.GetSingletonInstance<IInitLoggerFactory>().Create<T>();
var loggerFactory = services.GetSingletonInstanceOrNull<IInitLoggerFactory>();
return loggerFactory == null ? NullLogger<T>.Instance : loggerFactory.Create<T>();
}
}

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

@ -27,19 +27,10 @@
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="System.Text.Encodings.Web" />
<PackageReference Include="System.Runtime.Loader" />
<PackageReference Include="System.Linq.Dynamic.Core" />
<PackageReference Include="System.Linq.Queryable" />
<PackageReference Include="JetBrains.Annotations" />
<PackageReference Include="Nito.AsyncEx.Context" />
</ItemGroup>
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
<PackageReference Include="System.Management" />
</ItemGroup>
<PropertyGroup Condition="'$(OS)' == 'Windows_NT'">
<DefineConstants>$(DefineConstants);WINDOWS</DefineConstants>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Threading.Tasks.Extensions" />
</ItemGroup>
@ -47,10 +38,13 @@
<PackageReference Include="System.ComponentModel.Annotations" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETStandard' And $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '2.0')) ">
<PackageReference Include="System.Collections.Immutable" />
<PackageReference Include="System.Text.Encodings.Web" />
<PackageReference Include="System.Runtime.Loader" />
<PackageReference Include="System.Linq.Queryable" />
<PackageReference Include="Nullable">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

33
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ConventionalRegistrarBase.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp.Reflection;
namespace Volo.Abp.DependencyInjection;
@ -11,14 +12,30 @@ public abstract class ConventionalRegistrarBase : IConventionalRegistrar
{
public virtual void AddAssembly(IServiceCollection services, Assembly assembly)
{
var types = AssemblyHelper
.GetAllTypes(assembly)
.Where(
type => type != null &&
type.IsClass &&
!type.IsAbstract &&
!type.IsGenericType
).ToArray();
var logger = services.GetInitLogger<ConventionalRegistrarBase>();
var types = Array.Empty<Type>();
try
{
types = AssemblyHelper
.GetAllTypes(assembly)
.Where(
type => type != null &&
type.IsClass &&
!type.IsAbstract &&
!type.IsGenericType
).ToArray();
}
catch (ReflectionTypeLoadException e)
{
types = e.Types.Select(x => x!).ToArray();
logger.LogException(e);
}
catch (Exception e)
{
//TODO: Trigger a global event?
logger.LogException(e);
}
AddTypes(services, types);
}

4
framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs

@ -23,7 +23,6 @@ internal static class InternalServiceCollectionExtensions
{
var moduleLoader = new ModuleLoader();
var assemblyFinder = new AssemblyFinder(abpApplication);
var typeFinder = new TypeFinder(assemblyFinder);
if (!services.IsAdded<IConfiguration>())
{
@ -36,8 +35,9 @@ internal static class InternalServiceCollectionExtensions
services.TryAddSingleton<IModuleLoader>(moduleLoader);
services.TryAddSingleton<IAssemblyFinder>(assemblyFinder);
services.TryAddSingleton<ITypeFinder>(typeFinder);
services.TryAddSingleton<IInitLoggerFactory>(new DefaultInitLoggerFactory());
var typeFinder = new TypeFinder(services.GetInitLogger<TypeFinder>(), assemblyFinder);
services.TryAddSingleton<ITypeFinder>(typeFinder);
services.AddAssemblyOf<IAbpApplication>();

9
framework/src/Volo.Abp.Core/Volo/Abp/Reflection/AssemblyHelper.cs

@ -25,13 +25,6 @@ internal static class AssemblyHelper
public static IReadOnlyList<Type> GetAllTypes(Assembly assembly)
{
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
return ex.Types!;
}
return assembly.GetTypes();
}
}

15
framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeFinder.cs

@ -1,19 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.Reflection;
public class TypeFinder : ITypeFinder
{
private readonly ILogger<TypeFinder> _logger;
private readonly IAssemblyFinder _assemblyFinder;
private readonly Lazy<IReadOnlyList<Type>> _types;
public TypeFinder(IAssemblyFinder assemblyFinder)
public TypeFinder(ILogger<TypeFinder> logger, IAssemblyFinder assemblyFinder)
{
_assemblyFinder = assemblyFinder;
_logger = logger;
_types = new Lazy<IReadOnlyList<Type>>(FindAll, LazyThreadSafetyMode.ExecutionAndPublication);
}
@ -37,9 +42,15 @@ public class TypeFinder : ITypeFinder
allTypes.AddRange(typesInThisAssembly.Where(type => type != null));
}
catch
catch (ReflectionTypeLoadException e)
{
allTypes = e.Types.Select(x => x!).ToList();
_logger.LogException(e);
}
catch (Exception e)
{
//TODO: Trigger a global event?
_logger.LogException(e);
}
}

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

@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using Volo.Abp.Modularity;
using Volo.Abp.RabbitMQ;
@ -12,8 +14,8 @@ public class AbpEventBusRabbitMqModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpRabbitMqEventBusOptions>(configuration.GetSection("RabbitMQ:EventBus"));
context.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<AbpRabbitMqEventBusOptions>, PostConfigureAbpRabbitMqEventBusOptions>());
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)

54
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/PostConfigureAbpRabbitMqEventBusOptions.cs

@ -0,0 +1,54 @@
using System.Collections.Generic;
using Microsoft.Extensions.Options;
namespace Volo.Abp.EventBus.RabbitMq;
public class PostConfigureAbpRabbitMqEventBusOptions : IPostConfigureOptions<AbpRabbitMqEventBusOptions>
{
private readonly HashSet<string> _uint64QueueArguments =
[
"x-delivery-limit",
"x-expires",
"x-message-ttl",
"x-max-length",
"x-max-length-bytes",
"x-quorum-initial-group-size",
"x-quorum-target-group-size",
"x-stream-filter-size-bytes",
"x-stream-max-segment-size-bytes",
];
private readonly HashSet<string> _boolQueueArguments =
[
"x-single-active-consumer"
];
public virtual void PostConfigure(string? name, AbpRabbitMqEventBusOptions options)
{
ParseBoolQueueArguments(options);
ParseIntegerQueueArguments(options);
}
protected virtual void ParseBoolQueueArguments(AbpRabbitMqEventBusOptions options)
{
foreach (var argument in _boolQueueArguments)
{
if (options.QueueArguments.TryGetValue(argument, out var value) && value is string stringValue && bool.TryParse(stringValue, out var boolValue))
{
options.QueueArguments[argument] = boolValue;
}
}
}
protected virtual void ParseIntegerQueueArguments(AbpRabbitMqEventBusOptions options)
{
foreach (var argument in _uint64QueueArguments)
{
if (options.QueueArguments.TryGetValue(argument, out var value) && value is string stringValue && int.TryParse(stringValue, out var intValue))
{
options.QueueArguments[argument] = intValue;
}
}
}
}

37
framework/src/Volo.Abp.Features/Volo/Abp/Features/EditionFeatureValueProvider.cs

@ -1,5 +1,7 @@
using System.Security.Principal;
using System;
using System.Security.Principal;
using System.Threading.Tasks;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.Features;
@ -12,15 +14,25 @@ public class EditionFeatureValueProvider : FeatureValueProvider
protected ICurrentPrincipalAccessor PrincipalAccessor;
public EditionFeatureValueProvider(IFeatureStore featureStore, ICurrentPrincipalAccessor principalAccessor)
protected ITenantStore TenantStore { get; }
protected ICurrentTenant CurrentTenant { get; }
public EditionFeatureValueProvider(
IFeatureStore featureStore,
ICurrentPrincipalAccessor principalAccessor,
ITenantStore tenantStore,
ICurrentTenant currentTenant)
: base(featureStore)
{
PrincipalAccessor = principalAccessor;
TenantStore = tenantStore;
CurrentTenant = currentTenant;
}
public override async Task<string?> GetOrNullAsync(FeatureDefinition feature)
public async override Task<string?> GetOrNullAsync(FeatureDefinition feature)
{
var editionId = PrincipalAccessor.Principal?.FindEditionId();
var editionId = await FindEditionIdAsync();
if (editionId == null)
{
return null;
@ -28,4 +40,21 @@ public class EditionFeatureValueProvider : FeatureValueProvider
return await FeatureStore.GetOrNullAsync(feature.Name, Name, editionId.Value.ToString());
}
protected virtual async Task<Guid?> FindEditionIdAsync()
{
var editionId = PrincipalAccessor.Principal?.FindEditionId();
if (editionId != null)
{
return editionId;
}
if (CurrentTenant.Id == null)
{
return null;
}
var tenant = await TenantStore.FindAsync(CurrentTenant.Id.Value);
return tenant?.EditionId;
}
}

4
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs

@ -119,8 +119,8 @@ public class ClientProxyUrlBuilder : ITransientDependency
if (path != null)
{
urlBuilder = urlBuilder.Replace($"{{{pathParameter.Name}}}", path);
continue;
}
continue;
}
}
@ -164,8 +164,8 @@ public class ClientProxyUrlBuilder : ITransientDependency
urlBuilder.Append(isFirstParam ? "?" : "&");
urlBuilder.Append(queryString);
isFirstParam = false;
continue;
}
continue;
}
}

3
framework/src/Volo.Abp.Json.SystemTextJson/Volo.Abp.Json.SystemTextJson.csproj

@ -20,6 +20,9 @@
<ProjectReference Include="..\Volo.Abp.Json.Abstractions\Volo.Abp.Json.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" />
<ProjectReference Include="..\Volo.Abp.Data\Volo.Abp.Data.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1'">
<PackageReference Include="System.Text.Json" />
</ItemGroup>

44
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/AbpMongoClientFactory.cs

@ -0,0 +1,44 @@
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.MongoDB.Clients;
public class AbpMongoClientFactory : IAbpMongoClientFactory, ISingletonDependency
{
protected ConcurrentDictionary<string, MongoClient> ClientCache { get; }
protected AbpMongoDbContextOptions Options { get; }
public AbpMongoClientFactory(IOptions<AbpMongoDbContextOptions> options)
{
Options = options.Value;
ClientCache = new ConcurrentDictionary<string, MongoClient>();
}
public virtual Task<MongoClient> GetAsync(MongoUrl mongoUrl)
{
Check.NotNull(mongoUrl, nameof(mongoUrl));
return Task.FromResult(
ClientCache.GetOrAdd(mongoUrl.ToString(), _ =>
{
var mongoClientSettings = MongoClientSettings.FromUrl(mongoUrl);
Options.MongoClientSettingsConfigurer?.Invoke(mongoClientSettings);
return new MongoClient(mongoClientSettings);
}));
}
public virtual MongoClient Get(MongoUrl mongoUrl)
{
Check.NotNull(mongoUrl, nameof(mongoUrl));
return ClientCache.GetOrAdd(mongoUrl.ToString(), _ =>
{
var mongoClientSettings = MongoClientSettings.FromUrl(mongoUrl);
Options.MongoClientSettingsConfigurer?.Invoke(mongoClientSettings);
return new MongoClient(mongoClientSettings);
});
}
}

13
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/IAbpMongoClientFactory.cs

@ -0,0 +1,13 @@
using System;
using System.Threading.Tasks;
using MongoDB.Driver;
namespace Volo.Abp.MongoDB.Clients;
public interface IAbpMongoClientFactory
{
Task<MongoClient> GetAsync(MongoUrl mongoUrl);
[Obsolete("Use GetAsync method")]
MongoClient Get(MongoUrl mongoUrl);
}

28
framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs

@ -4,11 +4,11 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.Abp.MongoDB.Clients;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
@ -24,23 +24,23 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
protected readonly IConnectionStringResolver ConnectionStringResolver;
protected readonly ICancellationTokenProvider CancellationTokenProvider;
protected readonly ICurrentTenant CurrentTenant;
protected readonly AbpMongoDbContextOptions Options;
protected readonly IMongoDbContextTypeProvider DbContextTypeProvider;
protected readonly IAbpMongoClientFactory MongoClientFactory;
public UnitOfWorkMongoDbContextProvider(
IUnitOfWorkManager unitOfWorkManager,
IConnectionStringResolver connectionStringResolver,
ICancellationTokenProvider cancellationTokenProvider,
ICurrentTenant currentTenant,
IOptions<AbpMongoDbContextOptions> options,
IMongoDbContextTypeProvider dbContextTypeProvider)
IMongoDbContextTypeProvider dbContextTypeProvider,
IAbpMongoClientFactory mongoClientFactory)
{
UnitOfWorkManager = unitOfWorkManager;
ConnectionStringResolver = connectionStringResolver;
CancellationTokenProvider = cancellationTokenProvider;
CurrentTenant = currentTenant;
DbContextTypeProvider = dbContextTypeProvider;
Options = options.Value;
MongoClientFactory = mongoClientFactory;
Logger = NullLogger<UnitOfWorkMongoDbContextProvider<TMongoDbContext>>.Instance;
}
@ -77,12 +77,11 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
databaseName = ConnectionStringNameAttribute.GetConnStringName(targetDbContextType);
}
//TODO: Create only single MongoDbClient per connection string in an application (extract MongoClientCache for example).
var databaseApi = unitOfWork.GetOrAddDatabaseApi(
dbContextKey,
() => new MongoDbDatabaseApi(CreateDbContext(unitOfWork, mongoUrl, databaseName)));
return (TMongoDbContext)((MongoDbDatabaseApi) databaseApi).DbContext;
return (TMongoDbContext)((MongoDbDatabaseApi)databaseApi).DbContext;
}
public virtual async Task<TMongoDbContext> GetDbContextAsync(CancellationToken cancellationToken = default)
@ -121,14 +120,13 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
}
return (TMongoDbContext)((MongoDbDatabaseApi) databaseApi).DbContext;
return (TMongoDbContext)((MongoDbDatabaseApi)databaseApi).DbContext;
}
[Obsolete("Use CreateDbContextAsync")]
private TMongoDbContext CreateDbContext(IUnitOfWork unitOfWork, MongoUrl mongoUrl, string databaseName)
{
var client = CreateMongoClient(mongoUrl);
var client = MongoClientFactory.Get(mongoUrl);
var database = client.GetDatabase(databaseName);
if (unitOfWork.Options.IsTransactional)
@ -148,7 +146,7 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
string databaseName,
CancellationToken cancellationToken = default)
{
var client = CreateMongoClient(mongoUrl);
var client = await MongoClientFactory.GetAsync(mongoUrl);
var database = client.GetDatabase(databaseName);
if (unitOfWork.Options.IsTransactional)
@ -297,14 +295,6 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
return ConnectionStringResolver.Resolve(dbContextType);
}
protected virtual MongoClient CreateMongoClient(MongoUrl mongoUrl)
{
var mongoClientSettings = MongoClientSettings.FromUrl(mongoUrl);
Options.MongoClientSettingsConfigurer?.Invoke(mongoClientSettings);
return new MongoClient(mongoClientSettings);
}
protected virtual CancellationToken GetCancellationToken(CancellationToken preferredValue = default)
{
return CancellationTokenProvider.FallbackToProvider(preferredValue);

5
framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantConfiguration.cs

@ -17,6 +17,8 @@ public class TenantConfiguration
public bool IsActive { get; set; }
public Guid? EditionId { get; set; }
public TenantConfiguration()
{
IsActive = true;
@ -33,11 +35,12 @@ public class TenantConfiguration
ConnectionStrings = new ConnectionStrings();
}
public TenantConfiguration(Guid id, [NotNull] string name, [NotNull] string normalizedName)
public TenantConfiguration(Guid id, [NotNull] string name, [NotNull] string normalizedName, Guid? editionId = null)
: this(id, name)
{
Check.NotNull(normalizedName, nameof(normalizedName));
NormalizedName = normalizedName;
EditionId = editionId;
}
}

2
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/FakeAuthenticationScheme.cs

@ -1,8 +1,8 @@
using System;
using System.Linq;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using DeviceDetectorNET;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

8
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderDto.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Mvc.WebClientInfoProvider;
public class WebClientInfoProviderDto
{
public string BrowserInfo { get; set; }
public string DeviceInfo { get; set; }
}

26
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController.cs

@ -0,0 +1,26 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.WebClientInfo;
namespace Volo.Abp.AspNetCore.Mvc.WebClientInfoProvider;
[Route("api/web-client-info")]
public class WebClientInfoProviderTestController : AbpController
{
private IWebClientInfoProvider _webClientInfoProvider { get; }
public WebClientInfoProviderTestController(IWebClientInfoProvider webClientInfoProvider)
{
_webClientInfoProvider = webClientInfoProvider;
}
[HttpGet]
public Task<WebClientInfoProviderDto> GetAsync()
{
return Task.FromResult(new WebClientInfoProviderDto
{
BrowserInfo = _webClientInfoProvider.BrowserInfo,
DeviceInfo = _webClientInfoProvider.DeviceInfo
});
}
}

36
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/WebClientInfoProvider/WebClientInfoProviderTestController_Tests.cs

@ -0,0 +1,36 @@
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.WebClientInfoProvider;
public class WebClientInfoProviderTestController_Tests : AspNetCoreMvcTestBase
{
[Theory]
[InlineData("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", "Windows 10 Chrome")]
[InlineData("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", "Mac OS X Chrome")]
[InlineData("PostmanRuntime/7.43.4", "PostmanRuntime/7.43.4")]
[InlineData("curl/7.64.1", "curl/7.64.1")]
[InlineData("Mozilla/5.0 (compatible; MojeekBot/0.11; +mojeek.com/bot.html)", "MojeekBot")]
public async Task TestAsync(string ua, string device)
{
var clientInfo = await GetWebClientInfoAsync(ua);
clientInfo.ShouldNotBeNull();
clientInfo.BrowserInfo.ShouldBe(ua);
clientInfo.DeviceInfo.ShouldBe(device);
}
private async Task<WebClientInfoProviderDto> GetWebClientInfoAsync(string userAgent )
{
using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, "api/web-client-info"))
{
requestMessage.Headers.Add("User-Agent", userAgent);
var response = await Client.SendAsync(requestMessage);
response.StatusCode.ShouldBe(HttpStatusCode.OK);
return JsonSerializer.Deserialize<WebClientInfoProviderDto>(await response.Content.ReadAsStringAsync(), JsonSerializerOptions.Web);
}
}
}

3
framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/TypeFinder_Tests.cs

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
using Shouldly;
using Xunit;
@ -21,7 +22,7 @@ public class TypeFinder_Tests
//Act
var typeFinder = new TypeFinder(fakeAssemblyFinder);
var typeFinder = new TypeFinder(NullLogger<TypeFinder>.Instance, fakeAssemblyFinder);
//Assert

3
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToPath.cs

@ -15,9 +15,8 @@ public class TestObjectToPath : IObjectToPath<int>, ITransientDependency
{
value = 888;
}
return Task.FromResult(value.ToString());
}
return Task.FromResult<string>(null);
return Task.FromResult(value.ToString());
}
}

79
framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Clients/MongoClient_Factory_Tests.cs

@ -0,0 +1,79 @@
using System;
using System.Threading.Tasks;
using MongoDB.Driver;
using Xunit;
namespace Volo.Abp.MongoDB.Clients;
[Collection(MongoTestCollection.Name)]
public class MongoClient_Factory_Tests : MongoDbTestBase
{
private readonly IAbpMongoClientFactory _factory;
public MongoClient_Factory_Tests()
{
_factory = GetRequiredService<IAbpMongoClientFactory>();
}
[Fact]
public async Task Should_Return_Same_Instance_For_Same_ConnectionString()
{
// Arrange
var mongoUrl = new MongoUrl("mongodb://localhost:27017/my-db");
// Act
var client1 = await _factory.GetAsync(mongoUrl);
var client2 = await _factory.GetAsync(mongoUrl);
// Assert
Assert.Same(client1, client2);
}
[Fact]
public async Task Should_Return_Different_Instances_For_Different_ConnectionStrings()
{
// Arrange
var mongoUrl1 = new MongoUrl("mongodb://localhost:27017/db1");
var mongoUrl2 = new MongoUrl("mongodb://localhost:27017/db2");
// Act
var client1 = await _factory.GetAsync(mongoUrl1);
var client2 = await _factory.GetAsync(mongoUrl2);
// Assert
Assert.NotSame(client1, client2);
}
[Fact]
public async Task Should_Not_Throw_For_Valid_But_Unreachable_Connection()
{
// Arrange
var mongoUrl = new MongoUrl("mongodb://unreachablehost:12345/any");
// Act
var client = await _factory.GetAsync(mongoUrl);
// Assert
Assert.NotNull(client); // Even though it's not connectable now, the instance can be created
}
[Fact]
public async Task Should_Be_ThreadSafe_When_Accessed_Concurrently()
{
var mongoUrl = new MongoUrl("mongodb://localhost:27017/threadsafe");
var results = new MongoClient[100];
await Parallel.ForAsync(0, 100, async (i, _) =>
{
results[i] = await _factory.GetAsync(mongoUrl);
});
Assert.All(results, client => Assert.Same(results[0], client));
}
[Fact]
public async Task Should_Throw_If_ConnectionString_Is_Null_Or_Empty()
{
await Assert.ThrowsAsync<ArgumentNullException>(() => _factory.GetAsync(null!));
}
}

12
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs

@ -5,7 +5,9 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.TestApp.Domain;
using Volo.Abp.Domain.Repositories;
@ -17,10 +19,11 @@ namespace Volo.Abp.TestApp.Application;
public class PeopleAppService : CrudAppService<Person, PersonDto, Guid>, IPeopleAppService
{
public PeopleAppService(IRepository<Person, Guid> repository)
private readonly IHttpContextAccessor _httpContextAccessor;
public PeopleAppService(IRepository<Person, Guid> repository, IHttpContextAccessor httpContextAccessor)
: base(repository)
{
_httpContextAccessor = httpContextAccessor;
}
public async Task<ListResultDto<PhoneDto>> GetPhones(Guid id, GetPersonPhonesFilter filter)
@ -129,6 +132,11 @@ public class PeopleAppService : CrudAppService<Person, PersonDto, Guid>, IPeople
public Task<string> GetParamsFromQueryAsync([FromQuery] GetParamsInput input)
{
if (_httpContextAccessor.HttpContext != null)
{
_httpContextAccessor.HttpContext.Request.QueryString.ToString().ShouldNotContain("ExtraProperties=Volo.Abp.Data.ExtraPropertyDictionary");
}
return Task.FromResult(input.NameValues?.FirstOrDefault()?.Name + "-" + input.NameValues?.FirstOrDefault()?.Value + ":" +
input.NameValues?.FirstOrDefault()?.ExtraProperties["TestPropertyInList"] + ":" +
input.NameValues?.LastOrDefault()?.Name + "-" + input.NameValues?.LastOrDefault()?.Value + ":" +

3
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestAppModule.cs

@ -27,7 +27,8 @@ public class TestAppModule : AbpModule
{
ConfigureAutoMapper();
ConfigureDistributedEventBus();
context.Services.AddHttpContextAccessor();
context.Services.Replace(ServiceDescriptor.Singleton<IDistributedCache, TestMemoryDistributedCache>());
context.Services.AddEntityCache<Product, Guid>();
context.Services.AddEntityCache<Product, ProductCacheItem, Guid>();

9
latest-versions.json

@ -1,4 +1,13 @@
[
{
"version": "9.2.0",
"releaseDate": "",
"type": "stable",
"message": "",
"leptonx": {
"version": "4.2.0"
}
},
{
"version": "9.1.3",
"releaseDate": "",

1
modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj

@ -17,7 +17,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Text.Encodings.Web" />
<ProjectReference Include="..\Volo.Abp.Account.Application.Contracts\Volo.Abp.Account.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\identity\src\Volo.Abp.Identity.Application\Volo.Abp.Identity.Application.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.UI.Navigation\Volo.Abp.UI.Navigation.csproj" />

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

@ -33,10 +33,6 @@
<ProjectReference Include="..\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Security.Principal.Windows" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" />
</ItemGroup>

9
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogExcelFileConsts.cs

@ -0,0 +1,9 @@
namespace Volo.Abp.AuditLogging;
public static class AuditLogExcelFileConsts
{
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxFileNameLength { get; set; } = 256;
}

36
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/ar.json

@ -79,9 +79,39 @@
"DurationMs": "المدة (مللي ثانية)",
"StartDate": "تاريخ البدء",
"EndDate": "تاريخ الانتهاء",
"Feature:AuditLoggingGroup": "تدوين التسجيل",
"Feature:AuditLoggingGroup": "تسجيل التدقيق",
"Feature:AuditLoggingEnable": "تمكين صفحة تسجيل التدقيق",
"Feature:AuditLoggingEnableDescription": "تفعيل صفحة تسجيل التدقيق في التطبيق.",
"TenantId": "معرف المستأجر"
"Feature:AuditLoggingEnableDescription": "تمكين صفحة تسجيل التدقيق في التطبيق.",
"Feature:AuditLoggingSettingManagementEnable": "تمكين إدارة إعدادات تسجيل التدقيق",
"Feature:AuditLoggingSettingManagementEnableDescription": "تمكين تكوين إدارة إعدادات تسجيل التدقيق داخل التطبيق.",
"InvalidAuditLogDeletionSettings": "إعدادات حذف سجل التدقيق غير صالحة. إذا تم تمكين الحذف، يجب أن تكون الفترة أكبر من 0 أيام.",
"AuditLogSettingsGeneral": "عام",
"AuditLogSettingsGlobal": "عالمي",
"DisplayName:IsPeriodicDeleterEnabled": "تمكين خدمة التنظيف على مستوى النظام",
"Description:IsPeriodicDeleterEnabled": "إذا تم تعطيل هذا الخيار، فلن يعمل المحذف الدوري. لن يتم حذف سجلات التدقيق تلقائياً.",
"DisplayName:GlobalIsExpiredDeleterEnabled": "تمكين خدمة التنظيف لجميع المستأجرين والمضيف",
"Description:GlobalIsExpiredDeleterEnabled": "إذا تم تمكين هذا الخيار، سيتم حذف جميع العناصر المنتهية الصلاحية للمستأجرين والمضيف تلقائياً ما لم يكن لديها إعداد محدد.",
"DisplayName:IsExpiredDeleterEnabled": "تمكين خدمة التنظيف",
"Description:IsExpiredDeleterEnabled": "إذا تم تمكين هذا الخيار، سيتم حذف العناصر المنتهية الصلاحية تلقائياً.",
"DisplayName:ExpiredDeleterPeriod": "فترة حذف العناصر المنتهية الصلاحية",
"Description:ExpiredDeleterPeriod": "حدد عدد الأيام التي سيتم بعدها حذف العناصر المنتهية الصلاحية تلقائياً.",
"ExpiredDeleterPeriodUnit": "يوم (أيام)",
"AuditLogsBeforeXWillBeDeleted": "سيتم حذف سجلات التدقيق قبل {0}.",
"TenantId": "معرف المستأجر",
"Permission:Export": "تصدير سجلات التدقيق",
"ExportToExcel": "تصدير إلى Excel",
"Exporting": "جاري التصدير",
"ExportCompleted": "تم إكمال التصدير بنجاح",
"ExportFailed": "فشل التصدير",
"ExportJobQueued": "تم وضع مهمة التصدير في الطابور لـ {0} سجل. ستتلقى بريداً إلكترونياً عند اكتمال التصدير.",
"ExportReady": "تم إكمال التصدير لـ {0} سجل وجاهز للتنزيل.",
"FileName": "اسم الملف",
"TotalRecords": "إجمالي السجلات",
"ExcelFileAttachedMessage": "ملف Excel مرفق بهذا البريد الإلكتروني.",
"EntityChangeExportCompletedSubject": "تم إكمال تصدير تغييرات الكيان",
"AuditLogExportCompletedSubject": "تم إكمال تصدير سجل التدقيق",
"DownloadLinkExplanation": "يمكنك تنزيل الملف باستخدام الرابط أدناه حتى {0} (UTC)",
"DownloadNow": "تنزيل الآن",
"TryAgainMessage": "الرجاء المحاولة مرة أخرى. إذا استمرت المشكلة، يرجى الاتصال بالدعم."
}
}

32
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/cs.json

@ -82,6 +82,36 @@
"Feature:AuditLoggingGroup": "Protokolování auditu",
"Feature:AuditLoggingEnable": "Povolena stránka protokolování auditu",
"Feature:AuditLoggingEnableDescription": "Povolit stránku protokolování auditu v aplikaci.",
"TenantId": "ID nájemce"
"Feature:AuditLoggingSettingManagementEnable": "Povolit správu nastavení protokolování auditu",
"Feature:AuditLoggingSettingManagementEnableDescription": "Povolit konfiguraci správy nastavení protokolování auditu v aplikaci.",
"InvalidAuditLogDeletionSettings": "Neplatná nastavení mazání protokolu auditu. Pokud je mazání povoleno, období by mělo být větší než 0 dní.",
"AuditLogSettingsGeneral": "Obecné",
"AuditLogSettingsGlobal": "Globální",
"DisplayName:IsPeriodicDeleterEnabled": "Povolit službu čištění v celém systému",
"Description:IsPeriodicDeleterEnabled": "Pokud je tato možnost zakázána, periodický mazač nebude fungovat. Protokoly auditu nebudou automaticky mazány.",
"DisplayName:GlobalIsExpiredDeleterEnabled": "Povolit službu čištění pro všechny nájemce a hostitele",
"Description:GlobalIsExpiredDeleterEnabled": "Pokud je tato možnost povolena, všechny prošlé položky nájemců a hostitele budou automaticky smazány, pokud nemají specifické nastavení.",
"DisplayName:IsExpiredDeleterEnabled": "Povolit službu čištění",
"Description:IsExpiredDeleterEnabled": "Pokud je tato možnost povolena, prošlé položky budou automaticky smazány.",
"DisplayName:ExpiredDeleterPeriod": "Období mazání prošlých položek",
"Description:ExpiredDeleterPeriod": "Nastavte počet dní, po kterých budou prošlé položky automaticky smazány.",
"ExpiredDeleterPeriodUnit": "den(dny)",
"AuditLogsBeforeXWillBeDeleted": "Protokoly auditu před {0} budou smazány.",
"TenantId": "ID nájemce",
"Permission:Export": "Exportovat protokoly auditu",
"ExportToExcel": "Exportovat do Excelu",
"Exporting": "Exportování",
"ExportCompleted": "Export úspěšně dokončen",
"ExportFailed": "Export se nezdařil",
"ExportJobQueued": "Úloha exportu byla zařazena do fronty pro {0} záznamů. Obdržíte e-mail, když bude export dokončen.",
"ExportReady": "Export dokončen pro {0} záznamů a připraven ke stažení.",
"FileName": "Název souboru",
"TotalRecords": "Celkem záznamů",
"ExcelFileAttachedMessage": "Soubor Excel je připojen k tomuto e-mailu.",
"EntityChangeExportCompletedSubject": "Export změn entit dokončen",
"AuditLogExportCompletedSubject": "Export protokolu auditu dokončen",
"DownloadLinkExplanation": "Soubor můžete stáhnout pomocí odkazu níže až do {0} (UTC)",
"DownloadNow": "Stáhnout nyní",
"TryAgainMessage": "Zkuste to prosím znovu. Pokud problém přetrvává, kontaktujte podporu."
}
}

5
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/de-DE.json

@ -81,6 +81,9 @@
"Feature:AuditLoggingGroup": "Audit-Protokollierung",
"Feature:AuditLoggingEnable": "Aktivieren Sie die Überwachungsprotokollseite",
"Feature:AuditLoggingEnableDescription": "Aktivieren Sie die Überwachungsprotokollseite in der App.",
"TenantId": "Mandanten-ID"
"TenantId": "Mandanten-ID",
"DownloadLinkExplanation": "Sie können die Datei über den Link unten bis {0} (UTC) herunterladen",
"DownloadNow": "Jetzt herunterladen",
"TryAgainMessage": "Bitte versuchen Sie es erneut. Wenn das Problem weiterhin besteht, wenden Sie sich bitte an den Support."
}
}

32
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/de.json

@ -82,6 +82,36 @@
"Feature:AuditLoggingGroup": "Audit-Protokollierung",
"Feature:AuditLoggingEnable": "Aktivierte Audit-Logging-Seite",
"Feature:AuditLoggingEnableDescription": "Aktivieren Sie die Audit-Logging-Seite in der Anwendung.",
"TenantId": "Mandanten-ID"
"Feature:AuditLoggingSettingManagementEnable": "Audit-Log-Einstellungsverwaltung aktivieren",
"Feature:AuditLoggingSettingManagementEnableDescription": "Aktivieren Sie die Konfiguration der Audit-Log-Einstellungsverwaltung in der Anwendung.",
"InvalidAuditLogDeletionSettings": "Ungültige Audit-Log-Löscheinstellungen. Wenn das Löschen aktiviert ist, sollte der Zeitraum größer als 0 Tage sein.",
"AuditLogSettingsGeneral": "Allgemein",
"AuditLogSettingsGlobal": "Global",
"DisplayName:IsPeriodicDeleterEnabled": "Bereinigungsdienst systemweit aktivieren",
"Description:IsPeriodicDeleterEnabled": "Wenn diese Option deaktiviert ist, funktioniert der periodische Löscher nicht. Audit-Logs werden nicht automatisch gelöscht.",
"DisplayName:GlobalIsExpiredDeleterEnabled": "Bereinigungsdienst für alle Mandanten und Host aktivieren",
"Description:GlobalIsExpiredDeleterEnabled": "Wenn diese Option aktiviert ist, werden alle abgelaufenen Elemente der Mandanten und des Hosts automatisch gelöscht, es sei denn, es gibt eine spezifische Einstellung.",
"DisplayName:IsExpiredDeleterEnabled": "Bereinigungsdienst aktivieren",
"Description:IsExpiredDeleterEnabled": "Wenn diese Option aktiviert ist, werden abgelaufene Elemente automatisch gelöscht.",
"DisplayName:ExpiredDeleterPeriod": "Löschzeitraum für abgelaufene Elemente",
"Description:ExpiredDeleterPeriod": "Legen Sie die Anzahl der Tage fest, nach denen abgelaufene Elemente automatisch gelöscht werden.",
"ExpiredDeleterPeriodUnit": "Tag(e)",
"AuditLogsBeforeXWillBeDeleted": "Audit-Logs vor {0} werden gelöscht.",
"TenantId": "Mandanten-ID",
"Permission:Export": "Audit-Logs exportieren",
"ExportToExcel": "Nach Excel exportieren",
"Exporting": "Exportieren",
"ExportCompleted": "Export erfolgreich abgeschlossen",
"ExportFailed": "Export fehlgeschlagen",
"ExportJobQueued": "Export-Job wurde für {0} Datensätze in die Warteschlange eingereiht. Sie erhalten eine E-Mail, wenn der Export abgeschlossen ist.",
"ExportReady": "Export für {0} Datensätze abgeschlossen und zum Download bereit.",
"FileName": "Dateiname",
"TotalRecords": "Gesamtdatensätze",
"ExcelFileAttachedMessage": "Die Excel-Datei ist an diese E-Mail angehängt.",
"EntityChangeExportCompletedSubject": "Entity-Änderungsexport abgeschlossen",
"AuditLogExportCompletedSubject": "Audit-Log-Export abgeschlossen",
"DownloadLinkExplanation": "Sie können die Datei über den Link unten bis {0} (UTC) herunterladen",
"DownloadNow": "Jetzt herunterladen",
"TryAgainMessage": "Bitte versuchen Sie es erneut. Wenn das Problem weiterhin besteht, wenden Sie sich bitte an den Support."
}
}

17
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/en.json

@ -97,6 +97,21 @@
"Description:ExpiredDeleterPeriod": "Set the number of days after which expired items will be automatically deleted.",
"ExpiredDeleterPeriodUnit": "day(s)",
"AuditLogsBeforeXWillBeDeleted": "Audit logs before {0} will be deleted.",
"TenantId": "Tenant Id"
"TenantId": "Tenant Id",
"Permission:Export": "Export audit logs",
"ExportToExcel": "Export to Excel",
"Exporting": "Exporting",
"ExportCompleted": "Export completed successfully",
"ExportFailed": "Export failed",
"ExportJobQueued": "Export job has been queued for {0} records. You will receive an email when the export is completed.",
"ExportReady": "Export completed for {0} records and is ready for download.",
"FileName": "File name",
"TotalRecords": "Total records",
"ExcelFileAttachedMessage": "The Excel file is attached to this email.",
"EntityChangeExportCompletedSubject": "Entity change export completed",
"AuditLogExportCompletedSubject": "Audit log export completed",
"DownloadLinkExplanation": "You can download the file using the link below until {0} (UTC)",
"DownloadNow": "Download now",
"TryAgainMessage": "Please try again. If the problem persists, please contact support."
}
}

32
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/es.json

@ -82,6 +82,36 @@
"Feature:AuditLoggingGroup": "Registros de auditoría",
"Feature:AuditLoggingEnable": "Habilitar la página de registro de auditoría",
"Feature:AuditLoggingEnableDescription": "Habilite la página de registro de auditoría en la aplicación.",
"TenantId": "ID de inquilino"
"Feature:AuditLoggingSettingManagementEnable": "Habilitar la gestión de configuración de registro de auditoría",
"Feature:AuditLoggingSettingManagementEnableDescription": "Habilite la configuración de la gestión de configuración de registro de auditoría dentro de la aplicación.",
"InvalidAuditLogDeletionSettings": "Configuración de eliminación de registro de auditoría no válida. Si la eliminación está habilitada, el período debe ser mayor a 0 días.",
"AuditLogSettingsGeneral": "General",
"AuditLogSettingsGlobal": "Global",
"DisplayName:IsPeriodicDeleterEnabled": "Habilitar servicio de limpieza en todo el sistema",
"Description:IsPeriodicDeleterEnabled": "Si esta opción está deshabilitada, el eliminador periódico no funcionará. Los registros de auditoría no se eliminarán automáticamente.",
"DisplayName:GlobalIsExpiredDeleterEnabled": "Habilitar servicio de limpieza para todos los inquilinos y host",
"Description:GlobalIsExpiredDeleterEnabled": "Si esta opción está habilitada, todos los elementos vencidos de los inquilinos y el host se eliminarán automáticamente a menos que tenga una configuración específica.",
"DisplayName:IsExpiredDeleterEnabled": "Habilitar servicio de limpieza",
"Description:IsExpiredDeleterEnabled": "Si esta opción está habilitada, los elementos vencidos se eliminarán automáticamente.",
"DisplayName:ExpiredDeleterPeriod": "Período de eliminación de elementos vencidos",
"Description:ExpiredDeleterPeriod": "Establezca el número de días después de los cuales los elementos vencidos se eliminarán automáticamente.",
"ExpiredDeleterPeriodUnit": "día(s)",
"AuditLogsBeforeXWillBeDeleted": "Los registros de auditoría antes de {0} serán eliminados.",
"TenantId": "ID de inquilino",
"Permission:Export": "Exportar registros de auditoría",
"ExportToExcel": "Exportar a Excel",
"Exporting": "Exportando",
"ExportCompleted": "Exportación completada exitosamente",
"ExportFailed": "Exportación fallida",
"ExportJobQueued": "El trabajo de exportación ha sido puesto en cola para {0} registros. Recibirá un correo electrónico cuando se complete la exportación.",
"ExportReady": "Exportación completada para {0} registros y lista para descarga.",
"FileName": "Nombre del archivo",
"TotalRecords": "Total de registros",
"ExcelFileAttachedMessage": "El archivo de Excel está adjunto a este correo electrónico.",
"EntityChangeExportCompletedSubject": "Exportación de cambios de entidad completada",
"AuditLogExportCompletedSubject": "Exportación de registro de auditoría completada",
"DownloadLinkExplanation": "Puede descargar el archivo usando el enlace a continuación hasta {0} (UTC)",
"DownloadNow": "Descargar ahora",
"TryAgainMessage": "Por favor, inténtelo de nuevo. Si el problema persiste, contacte con soporte."
}
}

38
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/fi.json

@ -79,9 +79,39 @@
"DurationMs": "Kesto (ms)",
"StartDate": "Aloituspäivämäärä",
"EndDate": "Päättymispäivä",
"Feature:AuditLoggingGroup": "Tarkastusten kirjaaminen",
"Feature:AuditLoggingEnable": "Aktivoi auditointilokisivu",
"Feature:AuditLoggingEnableDescription": "Ota käyttöön tarkastuksen kirjaussivu sovelluksessa.",
"TenantId": "Vuokralaisen tunnus"
"Feature:AuditLoggingGroup": "Tarkastuslokin kirjaaminen",
"Feature:AuditLoggingEnable": "Ota käyttöön tarkastuslokin kirjaussivu",
"Feature:AuditLoggingEnableDescription": "Ota käyttöön tarkastuslokin kirjaussivu sovelluksessa.",
"Feature:AuditLoggingSettingManagementEnable": "Ota käyttöön tarkastuslokin asetusten hallinta",
"Feature:AuditLoggingSettingManagementEnableDescription": "Ota käyttöön tarkastuslokin asetusten hallinnan konfigurointi sovelluksessa.",
"InvalidAuditLogDeletionSettings": "Virheelliset tarkastuslokin poisto-asetukset. Jos poistaminen on käytössä, ajanjakson tulee olla suurempi kuin 0 päivää.",
"AuditLogSettingsGeneral": "Yleinen",
"AuditLogSettingsGlobal": "Globaali",
"DisplayName:IsPeriodicDeleterEnabled": "Ota käyttöön siivouspalvelu koko järjestelmässä",
"Description:IsPeriodicDeleterEnabled": "Jos tämä vaihtoehto on poistettu käytöstä, jaksollinen poistaja ei toimi. Tarkastuslokeja ei poisteta automaattisesti.",
"DisplayName:GlobalIsExpiredDeleterEnabled": "Ota käyttöön siivouspalvelu kaikille vuokralaisille ja isännälle",
"Description:GlobalIsExpiredDeleterEnabled": "Jos tämä vaihtoehto on käytössä, kaikki vuokralaisten ja isännän vanhentuneet kohteet poistetaan automaattisesti, ellei niillä ole erityistä asetusta.",
"DisplayName:IsExpiredDeleterEnabled": "Ota käyttöön siivouspalvelu",
"Description:IsExpiredDeleterEnabled": "Jos tämä vaihtoehto on käytössä, vanhentuneet kohteet poistetaan automaattisesti.",
"DisplayName:ExpiredDeleterPeriod": "Vanhentuneiden kohteiden poistojakso",
"Description:ExpiredDeleterPeriod": "Aseta päivien määrä, jonka jälkeen vanhentuneet kohteet poistetaan automaattisesti.",
"ExpiredDeleterPeriodUnit": "päivä(ä)",
"AuditLogsBeforeXWillBeDeleted": "Tarkastuslokit ennen {0} poistetaan.",
"TenantId": "Vuokralaisen tunnus",
"Permission:Export": "Vie tarkastuslokit",
"ExportToExcel": "Vie Exceliin",
"Exporting": "Viedään",
"ExportCompleted": "Vienti suoritettu onnistuneesti",
"ExportFailed": "Vienti epäonnistui",
"ExportJobQueued": "Vientityö on asetettu jonoon {0} tietueelle. Saat sähköpostin, kun vienti on valmis.",
"ExportReady": "Vienti valmis {0} tietueelle ja valmis ladattavaksi.",
"FileName": "Tiedostonimi",
"TotalRecords": "Tietueiden kokonaismäärä",
"ExcelFileAttachedMessage": "Excel-tiedosto on liitetty tähän sähköpostiin.",
"EntityChangeExportCompletedSubject": "Entiteettimuutosten vienti valmis",
"AuditLogExportCompletedSubject": "Tarkastuslokin vienti valmis",
"DownloadLinkExplanation": "Voit ladata tiedoston alla olevan linkin kautta {0} (UTC) asti",
"DownloadNow": "Lataa nyt",
"TryAgainMessage": "Yritä uudelleen. Jos ongelma jatkuu, ota yhteyttä tukeen."
}
}

32
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/fr.json

@ -82,6 +82,36 @@
"Feature:AuditLoggingGroup": "Journalisation d'audit",
"Feature:AuditLoggingEnable": "Page de journalisation d'audit activée",
"Feature:AuditLoggingEnableDescription": "Activez la page de journalisation d'audit dans l'application.",
"TenantId": "ID du locataire"
"Feature:AuditLoggingSettingManagementEnable": "Activer la gestion des paramètres de journalisation d'audit",
"Feature:AuditLoggingSettingManagementEnableDescription": "Activez la configuration de la gestion des paramètres de journalisation d'audit dans l'application.",
"InvalidAuditLogDeletionSettings": "Paramètres de suppression de journal d'audit non valides. Si la suppression est activée, la période doit être supérieure à 0 jour.",
"AuditLogSettingsGeneral": "Général",
"AuditLogSettingsGlobal": "Global",
"DisplayName:IsPeriodicDeleterEnabled": "Activer le service de nettoyage à l'échelle du système",
"Description:IsPeriodicDeleterEnabled": "Si cette option est désactivée, le suppresseur périodique ne fonctionnera pas. Les journaux d'audit ne seront pas supprimés automatiquement.",
"DisplayName:GlobalIsExpiredDeleterEnabled": "Activer le service de nettoyage pour tous les locataires et l'hôte",
"Description:GlobalIsExpiredDeleterEnabled": "Si cette option est activée, tous les éléments expirés des locataires et de l'hôte seront supprimés automatiquement, sauf s'il y a un paramètre spécifique.",
"DisplayName:IsExpiredDeleterEnabled": "Activer le service de nettoyage",
"Description:IsExpiredDeleterEnabled": "Si cette option est activée, les éléments expirés seront supprimés automatiquement.",
"DisplayName:ExpiredDeleterPeriod": "Période de suppression des éléments expirés",
"Description:ExpiredDeleterPeriod": "Définissez le nombre de jours après lesquels les éléments expirés seront automatiquement supprimés.",
"ExpiredDeleterPeriodUnit": "jour(s)",
"AuditLogsBeforeXWillBeDeleted": "Les journaux d'audit avant {0} seront supprimés.",
"TenantId": "ID du locataire",
"Permission:Export": "Exporter les journaux d'audit",
"ExportToExcel": "Exporter vers Excel",
"Exporting": "Exportation",
"ExportCompleted": "Exportation terminée avec succès",
"ExportFailed": "Échec de l'exportation",
"ExportJobQueued": "Le travail d'exportation a été mis en file d'attente pour {0} enregistrements. Vous recevrez un e-mail lorsque l'exportation sera terminée.",
"ExportReady": "Exportation terminée pour {0} enregistrements et prête pour le téléchargement.",
"FileName": "Nom du fichier",
"TotalRecords": "Total des enregistrements",
"ExcelFileAttachedMessage": "Le fichier Excel est joint à cet e-mail.",
"EntityChangeExportCompletedSubject": "Exportation des changements d'entité terminée",
"AuditLogExportCompletedSubject": "Exportation du journal d'audit terminée",
"DownloadLinkExplanation": "Vous pouvez télécharger le fichier en utilisant le lien ci-dessous jusqu'au {0} (UTC)",
"DownloadNow": "Télécharger maintenant",
"TryAgainMessage": "Veuillez réessayer. Si le problème persiste, veuillez contacter le support."
}
}

5
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/hi.json

@ -82,6 +82,9 @@
"Feature:AuditLoggingGroup": "ऑडिट लॉगिंग",
"Feature:AuditLoggingEnable": "सक्षम ऑडिट लॉगिंग पृष्ठ",
"Feature:AuditLoggingEnableDescription": "एप्लिकेशन में ऑडिट लॉगिंग पृष्ठ सक्षम करें।",
"TenantId": "किरायेदार आईडी"
"TenantId": "किरायेदार आईडी",
"DownloadLinkExplanation": "आप {0} (UTC) तक नीचे दिए गए लिंक का उपयोग करके फ़ाइल डाउनलोड कर सकते हैं",
"DownloadNow": "अभी डाउनलोड करें",
"TryAgainMessage": "कृपया पुनः प्रयास करें। यदि समस्या बनी रहती है, तो कृपया सहायता से संपर्क करें।"
}
}

5
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/hr.json

@ -82,6 +82,9 @@
"Feature:AuditLoggingGroup": "Zapisivanje revizije",
"Feature:AuditLoggingEnable": "Omogućena stranica za bilježenje revizije",
"Feature:AuditLoggingEnableDescription": "Omogućite stranicu za bilježenje revizije u aplikaciji.",
"TenantId": "ID stanara"
"TenantId": "ID stanara",
"DownloadLinkExplanation": "Datoteku možete preuzeti pomoću veze u nastavku do {0} (UTC)",
"DownloadNow": "Preuzmi odmah",
"TryAgainMessage": "Molimo pokušajte ponovno. Ako se problem nastavi, obratite se podršci."
}
}

36
modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/Localization/hu.json

@ -80,8 +80,38 @@
"StartDate": "Kezdő dátum",
"EndDate": "Befejezés dátuma",
"Feature:AuditLoggingGroup": "Audit naplózás",
"Feature:AuditLoggingEnable": "Engedélyezett naplózási oldal",
"Feature:AuditLoggingEnableDescription": "Engedélyezze a naplózási oldalt az alkalmazásban.",
"TenantId": "Bérlő azonosító"
"Feature:AuditLoggingEnable": "Audit naplózási oldal engedélyezése",
"Feature:AuditLoggingEnableDescription": "Engedélyezze az audit naplózási oldalt az alkalmazásban.",
"Feature:AuditLoggingSettingManagementEnable": "Audit napló beállítások kezelésének engedélyezése",
"Feature:AuditLoggingSettingManagementEnableDescription": "Engedélyezze az audit napló beállítások kezelésének konfigurációját az alkalmazásban.",
"InvalidAuditLogDeletionSettings": "Érvénytelen audit napló törlési beállítások. Ha a törlés engedélyezett, az időszaknak nagyobbnak kell lennie 0 napnál.",
"AuditLogSettingsGeneral": "Általános",
"AuditLogSettingsGlobal": "Globális",
"DisplayName:IsPeriodicDeleterEnabled": "Takarítási szolgáltatás engedélyezése rendszerszinten",
"Description:IsPeriodicDeleterEnabled": "Ha ez az opció le van tiltva, a periodikus törlő nem fog működni. Az audit naplók nem lesznek automatikusan törölve.",
"DisplayName:GlobalIsExpiredDeleterEnabled": "Takarítási szolgáltatás engedélyezése minden bérlő és host számára",
"Description:GlobalIsExpiredDeleterEnabled": "Ha ez az opció engedélyezett, minden bérlő és host lejárt elemei automatikusan törölve lesznek, kivéve ha van specifikus beállítás.",
"DisplayName:IsExpiredDeleterEnabled": "Takarítási szolgáltatás engedélyezése",
"Description:IsExpiredDeleterEnabled": "Ha ez az opció engedélyezett, a lejárt elemek automatikusan törölve lesznek.",
"DisplayName:ExpiredDeleterPeriod": "Lejárt elemek törlési időszaka",
"Description:ExpiredDeleterPeriod": "Állítsa be a napok számát, amely után a lejárt elemek automatikusan törölve lesznek.",
"ExpiredDeleterPeriodUnit": "nap(ok)",
"AuditLogsBeforeXWillBeDeleted": "Az audit naplók {0} előtt törölve lesznek.",
"TenantId": "Bérlő azonosító",
"Permission:Export": "Audit naplók exportálása",
"ExportToExcel": "Exportálás Excelbe",
"Exporting": "Exportálás",
"ExportCompleted": "Exportálás sikeresen befejezve",
"ExportFailed": "Exportálás sikertelen",
"ExportJobQueued": "Exportálási feladat sorba állítva {0} rekordhoz. E-mailt fog kapni, amikor az exportálás befejeződött.",
"ExportReady": "Exportálás befejezve {0} rekordhoz és letöltésre kész.",
"FileName": "Fájlnév",
"TotalRecords": "Összes rekord",
"ExcelFileAttachedMessage": "Az Excel fájl csatolva van ehhez az e-mailhez.",
"EntityChangeExportCompletedSubject": "Entitás változás exportálás befejezve",
"AuditLogExportCompletedSubject": "Audit napló exportálás befejezve",
"DownloadLinkExplanation": "A fájlt letöltheti az alábbi linken keresztül {0} (UTC) időpontig",
"DownloadNow": "Letöltés most",
"TryAgainMessage": "Kérjük, próbálja újra. Ha a probléma továbbra is fennáll, forduljon az ügyfélszolgálathoz."
}
}

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

Loading…
Cancel
Save