Browse Source

Merge branch 'dev' into feature/data-collection

# Conflicts:
#	Directory.Packages.props
#	framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs
pull/23059/head
Emre KARA 1 year ago
parent
commit
5eaf090595
  1. 2
      .github/ISSUE_TEMPLATE/config.yml
  2. 4
      .github/workflows/angular.yml
  3. 20
      .github/workflows/auto-pr.yml
  4. 280
      Directory.Packages.props
  5. 29
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  6. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
  7. 27
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  8. 4
      common.props
  9. 2
      docs/en/Blog-Posts/2022-03-08 v5_2_Preview/POST.md
  10. 2
      docs/en/Blog-Posts/2024-02-16 v8_1_Preview/POST.md
  11. 2
      docs/en/Blog-Posts/2024-05-21 v8_2_Release/POST.md
  12. 2
      docs/en/Blog-Posts/2024-07-31 v8_3_Preview/post.md
  13. 2
      docs/en/Blog-Posts/2024-09-06 v8_3_Release_Stable/POST.md
  14. 2
      docs/en/Blog-Posts/2024-10-23 v9_0_Preview/POST.md
  15. 2
      docs/en/Blog-Posts/2024-11-19 v9_0_Release_Stable/post.md
  16. 2
      docs/en/Blog-Posts/2025-01-21 v9_1_Preview/POST.md
  17. 2
      docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/POST.md
  18. 195
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/POST.md
  19. BIN
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/after.png
  20. BIN
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/before.png
  21. BIN
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/configure-timezone.png
  22. BIN
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/cover-image.png
  23. BIN
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/require-email-verification-for-register.png
  24. BIN
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/select-account.png
  25. BIN
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/studio-switch-to-preview.png
  26. 2
      docs/en/Community-Articles/2022-03-09-abpio-platform-52-rc-has-been-published/post.md
  27. 2
      docs/en/Community-Articles/2024-02-19-abpio-platform-81-rc-has-been-published/post.md
  28. 2
      docs/en/Community-Articles/2024-05-24-abpio-platform-82-rc-has-been-published/post.md
  29. 2
      docs/en/Community-Articles/2024-08-01-abpio-platform-83-rc-has-been-published/post.md
  30. 2
      docs/en/Community-Articles/2024-09-12-abpio-platform-83-final-has-been-released/post.md
  31. 2
      docs/en/Community-Articles/2024-10-23-abpio-platform-90-rc-has-been-published/post.md
  32. 2
      docs/en/Community-Articles/2024-11-21-abpio-platform-90-has-been-released-based-on-net-90/post.md
  33. 2
      docs/en/Community-Articles/2025-01-21-abp-platform-91-rc-has-been-released/post.md
  34. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list-utc1.png
  35. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list-utc3.png
  36. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list.png
  37. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/berlin.png
  38. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-create.png
  39. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-edit.png
  40. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-list.png
  41. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-create.png
  42. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-edit.png
  43. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list-utc1.png
  44. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list-utc3.png
  45. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list.png
  46. BIN
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-post.png
  47. 781
      docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/post.md
  48. 8
      docs/en/Community-Articles/2025-03-18-Using-Vue-Components/post.md
  49. BIN
      docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/1.png
  50. BIN
      docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/2.png
  51. BIN
      docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/3.png
  52. 248
      docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/post.md
  53. 115
      docs/en/Community-Articles/2025-03-24-How-to-Change-the-CurrentUser-in-ABP/POST.md
  54. 38
      docs/en/Community-Articles/2025-03-27-Announcing-the-ABP-Contributor-Program/post.md
  55. 170
      docs/en/Community-Articles/2025-04-20-IDX10204/POST.md
  56. BIN
      docs/en/Community-Articles/2025-04-20-IDX10204/header.png
  57. BIN
      docs/en/Community-Articles/2025-04-20-IDX10204/payload.png
  58. 262
      docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/POST.md
  59. BIN
      docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/comment-example.png
  60. BIN
      docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/summary-card.png
  61. 2
      docs/en/cli/index.md
  62. 4
      docs/en/docs-nav.json
  63. 2
      docs/en/framework/api-development/static-csharp-clients.md
  64. 2
      docs/en/framework/api-development/swagger.md
  65. 6
      docs/en/framework/architecture/microservices/index.md
  66. 2
      docs/en/framework/architecture/modularity/basics.md
  67. 11
      docs/en/framework/architecture/modularity/extending/module-entity-extensions.md
  68. 12
      docs/en/framework/data/entity-framework-core/index.md
  69. 9
      docs/en/framework/data/entity-framework-core/postgresql.md
  70. 2
      docs/en/framework/fundamentals/caching.md
  71. 51
      docs/en/framework/fundamentals/configuration.md
  72. 2
      docs/en/framework/fundamentals/fluent-validation.md
  73. 11
      docs/en/framework/fundamentals/localization.md
  74. 20
      docs/en/framework/infrastructure/background-jobs/hangfire.md
  75. 2
      docs/en/framework/infrastructure/background-jobs/quartz.md
  76. 20
      docs/en/framework/infrastructure/background-workers/hangfire.md
  77. 2
      docs/en/framework/infrastructure/background-workers/quartz.md
  78. 2
      docs/en/framework/infrastructure/current-user.md
  79. 6
      docs/en/framework/infrastructure/event-bus/distributed/index.md
  80. 8
      docs/en/framework/infrastructure/image-manipulation.md
  81. 2
      docs/en/framework/infrastructure/settings.md
  82. 2
      docs/en/framework/infrastructure/sms-sending.md
  83. 8
      docs/en/framework/infrastructure/string-encryption.md
  84. 2
      docs/en/framework/infrastructure/text-templating/razor.md
  85. 2
      docs/en/framework/infrastructure/text-templating/scriban.md
  86. 100
      docs/en/framework/infrastructure/timing.md
  87. 2
      docs/en/framework/real-time/signalr.md
  88. 11
      docs/en/framework/ui/maui/index.md
  89. 5
      docs/en/framework/ui/mvc-razor-pages/customization-user-interface.md
  90. 2
      docs/en/get-started/empty-aspnet-core-application.md
  91. BIN
      docs/en/images/account-pro-external-login-settings.png
  92. BIN
      docs/en/images/account-pro-select-account-parameter.png
  93. BIN
      docs/en/images/account-pro-select-account.png
  94. BIN
      docs/en/images/account-pro-time-zone-setting.png
  95. BIN
      docs/en/images/create-aspnet-core-application.png
  96. BIN
      docs/en/images/create-new-aspnet-core-application-v2.png
  97. BIN
      docs/en/images/pen-test-alert-list-9.1.png
  98. BIN
      docs/en/images/samples-cms-kit.png
  99. BIN
      docs/en/images/samples-easycrm.png
  100. BIN
      docs/en/images/samples-eshoponabp.png

2
.github/ISSUE_TEMPLATE/config.yml

@ -1,4 +1,4 @@
blank_issues_enabled: true
blank_issues_enabled: false
contact_links:
- name: Issue with ABP Commercial
url: https://abp.io/support/questions

4
.github/workflows/angular.yml

@ -27,12 +27,12 @@ jobs:
with:
fetch-depth: 0
- uses: actions/cache@v2
- uses: actions/cache@v4
with:
path: 'npm/ng-packs/node_modules'
key: ${{ runner.os }}-${{ hashFiles('npm/ng-packs/yarn.lock') }}
- uses: actions/cache@v2
- uses: actions/cache@v4
with:
path: 'templates/app/angular/node_modules'
key: ${{ runner.os }}-${{ hashFiles('templates/app/angular/yarn.lock') }}

20
.github/workflows/auto-pr.yml

@ -1,13 +1,13 @@
name: Merge branch dev with rel-9.1
name: Merge branch dev with rel-9.2
on:
push:
branches:
- rel-9.1
- rel-9.2
permissions:
contents: read
jobs:
merge-dev-with-rel-9-1:
merge-dev-with-rel-9-2:
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.1:rel-9.1
git reset --hard rel-9.1
git fetch origin rel-9.2:rel-9.2
git reset --hard rel-9.2
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
branch: auto-merge/rel-9-1/${{github.run_number}}
title: Merge branch dev with rel-9.1
body: This PR generated automatically to merge dev with rel-9.1. Please review the changed files before merging to prevent any errors that may occur.
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.
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-1/${{github.run_number}} --approve
gh pr merge auto-merge/rel-9-1/${{github.run_number}} --merge --auto --delete-branch
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

280
Directory.Packages.props

@ -3,189 +3,183 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="3.0.0" />
<PackageVersion Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="4.0.0" />
<PackageVersion Include="aliyun-net-sdk-sts" Version="3.1.2" />
<PackageVersion Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageVersion Include="AsyncKeyedLock" Version="7.1.3" />
<PackageVersion Include="Autofac" Version="8.1.0" />
<PackageVersion Include="AsyncKeyedLock" Version="7.1.6" />
<PackageVersion Include="Autofac" Version="8.2.1" />
<PackageVersion Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageVersion Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />
<PackageVersion Include="AutoMapper" Version="13.0.1" />
<PackageVersion Include="AutoMapper" Version="14.0.0" />
<PackageVersion Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageVersion Include="AWSSDK.S3" Version="3.7.410.9" />
<PackageVersion Include="AWSSDK.SecurityToken" Version="3.7.401.16" />
<PackageVersion Include="AWSSDK.S3" Version="4.0.0" />
<PackageVersion Include="AWSSDK.SecurityToken" Version="4.0.0" />
<PackageVersion Include="BunnyCDN.Net.Storage" Version="1.0.4" />
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.18.1" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.22.1" />
<PackageVersion Include="Blazorise" Version="1.7.3" />
<PackageVersion Include="Blazorise.Components" Version="1.7.3" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.7.3" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.7.3" />
<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="Castle.Core" Version="5.1.1" />
<PackageVersion Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
<PackageVersion Include="CommonMark.NET" Version="0.15.1" />
<PackageVersion Include="Confluent.Kafka" Version="2.5.3" />
<PackageVersion Include="Dapper" Version="2.1.44" />
<PackageVersion Include="Dapr.AspNetCore" Version="1.14.0" />
<PackageVersion Include="Dapr.Client" Version="1.14.0" />
<PackageVersion Include="DeviceDetector.NET" Version="6.3.3" />
<PackageVersion Include="Devart.Data.Oracle.EFCore" Version="10.4.190.9" />
<PackageVersion Include="DistributedLock.Core" Version="1.0.7" />
<PackageVersion Include="Confluent.Kafka" Version="2.10.0" />
<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="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.10.0" />
<PackageVersion Include="EphemeralMongo.Core" Version="1.1.3" />
<PackageVersion Include="EphemeralMongo6.runtime.linux-x64" Version="1.1.3" />
<PackageVersion Include="EphemeralMongo6.runtime.osx-x64" Version="1.1.3" />
<PackageVersion Include="EphemeralMongo6.runtime.win-x64" Version="1.1.3" />
<PackageVersion Include="MongoSandbox.Core" Version="1.0.1" />
<PackageVersion Include="MongoSandbox6.runtime.linux-x64" Version="1.0.1" />
<PackageVersion Include="MongoSandbox6.runtime.osx-x64" Version="1.0.1" />
<PackageVersion Include="MongoSandbox6.runtime.win-x64" Version="1.0.1" />
<PackageVersion Include="FluentValidation" Version="11.10.0" />
<PackageVersion Include="Google.Cloud.Storage.V1" Version="4.10.0" />
<PackageVersion Include="Hangfire.AspNetCore" Version="1.8.17" />
<PackageVersion Include="Hangfire.SqlServer" Version="1.8.17" />
<PackageVersion Include="HtmlSanitizer" Version="8.1.870" />
<PackageVersion Include="DeepL.net" Version="1.15.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" />
<PackageVersion Include="MongoSandbox8.runtime.win-x64" Version="2.0.0" />
<PackageVersion Include="FluentValidation" Version="11.11.0" />
<PackageVersion Include="Google.Cloud.Storage.V1" Version="4.13.0" />
<PackageVersion Include="Hangfire.AspNetCore" Version="1.8.18" />
<PackageVersion Include="Hangfire.SqlServer" Version="1.8.18" />
<PackageVersion Include="HtmlSanitizer" Version="9.0.884" />
<PackageVersion Include="IdentityModel" Version="7.0.0" />
<PackageVersion Include="IdentityServer4" Version="4.1.2" />
<PackageVersion Include="IdentityServer4.AspNetIdentity" Version="4.1.2" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.2.0" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageVersion Include="LdapForNet" Version="2.7.15" />
<PackageVersion Include="LibGit2Sharp" Version="0.30.0" />
<PackageVersion Include="LibGit2Sharp" Version="0.31.0" />
<PackageVersion Include="Magick.NET-Q16-AnyCPU" Version="13.4.0" />
<PackageVersion Include="MailKit" Version="4.8.0" />
<PackageVersion Include="Markdig.Signed" Version="0.37.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.2" />
<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.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.Razor.Language" Version="6.0.36" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.2" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.2" />
<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.CodeAnalysis.CSharp" Version="4.5.0" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.2" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0-preview.7.24406.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.CommandLine" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Composite" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Localization" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.4" />
<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.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.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.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.6.0" />
<PackageVersion Include="Minio" Version="6.0.3" />
<PackageVersion Include="MongoDB.Driver" Version="3.1.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="Minio" Version="6.0.4" />
<PackageVersion Include="MongoDB.Driver" Version="3.3.0" />
<PackageVersion Include="NEST" Version="7.17.5" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Nito.AsyncEx.Context" Version="5.1.2" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="NuGet.Versioning" Version="6.11.1" />
<PackageVersion Include="NUglify" Version="1.21.9" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="NuGet.Versioning" Version="6.13.2" />
<PackageVersion Include="NUglify" Version="1.21.15" />
<PackageVersion Include="Nullable" Version="1.3.1" />
<PackageVersion Include="Octokit" Version="13.0.1" />
<PackageVersion Include="OpenIddict.Abstractions" Version="6.1.1" />
<PackageVersion Include="OpenIddict.Core" Version="6.1.1" />
<PackageVersion Include="OpenIddict.Server.AspNetCore" Version="6.1.1" />
<PackageVersion Include="OpenIddict.Validation.AspNetCore" Version="6.1.1" />
<PackageVersion Include="OpenIddict.Validation.ServerIntegration" Version="6.1.1" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.60" />
<PackageVersion Include="Polly" Version="8.4.2" />
<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="Oracle.EntityFrameworkCore" Version="9.23.80" />
<PackageVersion Include="Polly" Version="8.5.2" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-preview.3.efcore.9.0.0" />
<PackageVersion Include="Quartz" Version="3.13.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.13.0" />
<PackageVersion Include="Quartz.Plugins.TimeZoneConverter" Version="3.13.0" />
<PackageVersion Include="Quartz.Serialization.Json" Version="3.13.0" />
<PackageVersion Include="RabbitMQ.Client" Version="6.8.1" />
<PackageVersion Include="Rebus" Version="8.6.0" />
<PackageVersion Include="Rebus.ServiceProvider" Version="10.2.0" />
<PackageVersion Include="Scriban" Version="5.10.0" />
<PackageVersion Include="Serilog" Version="4.0.2" />
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.0.0" />
<PackageVersion Include="Quartz" Version="3.14.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.14.0" />
<PackageVersion Include="Quartz.Plugins.TimeZoneConverter" Version="3.14.0" />
<PackageVersion Include="Quartz.Serialization.Json" Version="3.14.0" />
<PackageVersion Include="RabbitMQ.Client" Version="7.1.2" />
<PackageVersion Include="Rebus" Version="8.8.0" />
<PackageVersion Include="Rebus.ServiceProvider" Version="10.3.0" />
<PackageVersion Include="Scriban" Version="6.2.1" />
<PackageVersion Include="Serilog" Version="4.2.0" />
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.1" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Shouldly" Version="4.2.1" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="2.1.4" />
<PackageVersion Include="Shouldly" Version="4.3.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.8" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="2.1.6" />
<PackageVersion Include="SkiaSharp" Version="2.88.8" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.8" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="2.88.8" />
<PackageVersion Include="Slugify.Core" Version="4.0.1" />
<PackageVersion Include="Spectre.Console" Version="0.49.1" />
<PackageVersion Include="StackExchange.Redis" Version="2.8.16" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.8.1" />
<PackageVersion Include="System.Collections.Immutable" Version="9.0.2" />
<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.ComponentModel.Annotations" Version="5.0.0" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.1" />
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.2" />
<PackageVersion Include="System.Linq.Queryable" Version="4.3.0" />
<PackageVersion Include="System.Management" Version="7.0.2" />
<PackageVersion Include="System.Runtime.Loader" Version="4.3.0" />
<PackageVersion Include="System.Security.Permissions" Version="9.0.2" />
<PackageVersion Include="System.Security.Permissions" Version="9.0.4" />
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.2" />
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.2" />
<PackageVersion Include="System.Text.Json" Version="9.0.2" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.0" />
<PackageVersion Include="TencentCloudSDK.Sms" Version="3.0.1142" />
<PackageVersion Include="TimeZoneConverter" Version="6.1.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.Threading.Tasks.Extensions" Version="4.6.3" />
<PackageVersion Include="TencentCloudSDK.Sms" Version="3.0.1231" />
<PackageVersion Include="TimeZoneConverter" Version="7.0.0" />
<PackageVersion Include="Unidecode.NET" Version="2.1.0" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.extensibility.execution" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.extensibility.execution" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" />
<PackageVersion Include="Fody" Version="6.8.2" />
<PackageVersion Include="CliWrap" Version="3.8.2"/>
<PackageVersion Include="System.IO.Abstractions" Version="22.0.14"/>
<PackageVersion Include="Microsoft.Win32.Registry" Version="4.0.0"/>
<PackageVersion Include="Fody" Version="6.9.2" />
</ItemGroup>
</Project>
</Project>

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

@ -690,6 +690,33 @@
"Permission:Campaigns": "Campaigns",
"Permission:Licenses": "License Settings",
"BlockUserPolicy": "User Block Policies",
"Permission:BlockUserPolicy": "User Block Policy"
"Permission:BlockUserPolicy": "User Block Policy",
"ManageImages" : "Manage Image",
"ModuleMainImage": "Module Main Image",
"ModuleImageInfo1": "Accepted file types : <strong> JPEG, JPG, PNG, SVG </strong>",
"ModuleImageInfo2": "Max file size : <strong> 1 MB </strong>",
"ModuleImageInfo3": "Image Proportion : <strong> 1:1 </strong>",
"ModuleImageInfo4": "<a href='/images/example-module-img.png' download> Download a sample cover image </a>",
"AreYouSureToDeleteImage": "Are you sure you want to delete this image?",
"CompactPercentage": "Compact Percentage",
"CompactPercentageDescription": "Percentage of cache entries to remove during compaction (1-100)",
"Settings": "Settings",
"MaxSizeOfInMemoryCache": "Max Size Of In Memory Cache",
"MaxSizeOfInMemoryCacheInfo": "Gets or sets the maximum size of the memory cache.",
"SizeOfTriggerAutoCompact": "Size Of Trigger Auto Compact",
"SizeOfTriggerAutoCompactInfo": "Gets or sets the size threshold that triggers auto-compaction. When the cache size exceeds this value, auto-compaction will be performed",
"AutoCompactPercentage": "Auto Compact Percentage",
"AutoCompactPercentageInfo": "Gets or sets the percentage of items to remove when auto-compaction is triggered (1-100)",
"ClearCaches": "This clears all NuGet content cache",
"CacheLatestVersions": "Cache Latest Versions",
"VersionCount": "Version Count",
"VersionCountInfo": "Gets or sets the number of versions to cache",
"IncludeLeptonX": "Include LeptonX",
"IncludeLeptonXInfo": "Gets or sets whether to include LeptonX in the cache",
"IncludePrerelease": "Include Prerelease",
"IncludePrereleaseInfo": "Gets or sets whether to include prerelease versions in the cache",
"CacheVersionCount": "Cache Version Count",
"CacheVersionCountInfo": "Gets or sets the number of versions to cache",
"CacheLatestVersionsInfo": "This caches the latest versions of the NuGet packages"
}
}

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

@ -264,6 +264,7 @@
"EditProfile": "Edit Profile",
"ConfirmEmailForPost": "To be able to post, you need to confirm your email. Go to account.abp.io/Account/Manage and verify your email in the Personal Info tab.",
"DailyPostCreateLimitation": "You have reached the daily post creation limit. You can create a new post in {0}.",
"YourAccountDisabled": "Your user account is disabled!"
"YourAccountDisabled": "Your user account is disabled!",
"PostCreationFailed": "An error occurred while creating the post. Please try again later."
}
}

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

@ -672,20 +672,20 @@
"ExternalPostUrl": "External Post Url",
"ThankYouForContribution": "Thank you for contributing to the ABP Community. We accept articles and video tutorials on ABP Framework, .NET, ASP.NET Core and general software development topics.",
"GithubPost": "GitHub Post",
"GithubPostSubmitStepOne": "<span class=\"font-weight-bold\">1.</span> Write a post on any public GitHub repository with the Markdown format. <a target=\"_blank\" class=\"fw-6 text-white text-decoration-underline\" href=\"https://github.com/abpframework/abp/blob/dev/docs/en/Community-Articles/2020-12-04-Event-Organizer/Post.md\">example</a>",
"GithubPostSubmitStepTwo": "<span class=\"font-weight-bold\">2.</span> Submit your post URL using the form.",
"GithubPostSubmitStepThree": "<span class=\"font-weight-bold\">3.</span> Your post will be rendered in this website.",
"GithubPostSubmitStepOne": "1. Write a post on any public GitHub repository with the Markdown format. <a target=\"_blank\" class=\"text-sub-title text-decoration-underline\" href=\"https://github.com/abpframework/abp/blob/dev/docs/en/Community-Articles/2020-12-04-Event-Organizer/Post.md\">example</a>",
"GithubPostSubmitStepTwo": "2. Submit your post URL using the form.",
"GithubPostSubmitStepThree": "3. Your post will be rendered in this website.",
"YoutubeVideo": "Youtube Video",
"YoutubeVideoSubmitStepOne": "<span class=\"font-weight-bold\">1.</span> Publish your video on YouTube.",
"YoutubeVideoSubmitStepTwo": "<span class=\"font-weight-bold\">2.</span> Submit the video URL using the form.",
"YoutubeVideoSubmitStepThree": "<span class=\"font-weight-bold\">3.</span> Visitors will be able to watch your video content directly on this website.",
"YoutubeVideoSubmitStepOne": "1. Publish your video on YouTube.",
"YoutubeVideoSubmitStepTwo": "2. Submit the video URL using the form.",
"YoutubeVideoSubmitStepThree": "3. Visitors will be able to watch your video content directly on this website.",
"ExternalContent": "External Content",
"ExternalContentSubmitStepOne": "<span class=\"font-weight-bold\">1.</span> Create a content on any public platform (Medium, your own blog or anywhere you like).",
"ExternalContentSubmitStepTwo": "<span class=\"font-weight-bold\">2.</span> Submit your content URL using the form.",
"ExternalContentSubmitStepThree": "<span class=\"font-weight-bold\">3.</span> Visitors are redirected to the content on the original website.",
"ExternalContentSubmitStepOne": "1. Create a content on any public platform (Medium, your own blog or anywhere you like).",
"ExternalContentSubmitStepTwo": "2. Submit your content URL using the form.",
"ExternalContentSubmitStepThree": "3. Visitors are redirected to the content on the original website.",
"ChooseYourContentType": "Please choose the way you want to add your content.",
"PostContentViaGithub": "I want to add my post with <span class=\"icon-with-text-github\"> GitHub</span> in accordance with the markdown rules.",
"PostContentViaYoutube": "I want to share my videos available on <span class=\"icon-with-text-youtube\"> Youtube</span> here.",
"PostContentViaGithub": "I want to add my post with <span class=\"icon-with-text-github text-nowrap\"> GitHub</span> in accordance with the markdown rules.",
"PostContentViaYoutube": "I want to share my videos available on <span class=\"icon-with-text-youtube text-nowrap\"> Youtube</span> here.",
"PostContentViaExternalSource": "I want to add the content I published on <b>another platform</b> here.",
"GitHubUserNameValidationMessage": "Your Github username can not include whitespace, please make sure your Github username is correct.",
"PersonalSiteUrlValidationMessage": "Your personal site URL can not include whitespace, please make sure your personal site URL is correct.",
@ -1886,6 +1886,9 @@
"FaqIyzicoPaymentIssuesExplanation5": "<strong>Credit/Debit Cards</strong>: Visa, MasterCard, American Express, Discover, Diners Club, JCB",
"FaqIyzicoPaymentIssuesExplanation6": "<strong>Digital Wallets</strong>: PayPal, AliPay, WebMoney",
"FaqIyzicoPaymentIssuesExplanation7": "Alternatively, you can send the license amount directly via bank wire transfer. For our bank account details, please visit: <a href='https://volosoft.com/bank-account'>Bank Account Information</a> (use USD currency).",
"FaqIyzicoPaymentIssuesExplanation8": "ABP website doesn't save or process your credit card. We use payment gateways for this and the entire transaction is handled by payment gateways. We have no authority to interfere with the payment process or fix the payment steps. If you have further questions or need additional support, feel free to contact us at <a href='https://abp.io/contact'>abp.io/contact</a>."
"FaqIyzicoPaymentIssuesExplanation8": "ABP website doesn't save or process your credit card. We use payment gateways for this and the entire transaction is handled by payment gateways. We have no authority to interfere with the payment process or fix the payment steps. If you have further questions or need additional support, feel free to contact us at <a href='https://abp.io/contact'>abp.io/contact</a>.",
"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"
}
}

4
common.props

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

2
docs/en/Blog-Posts/2022-03-08 v5_2_Preview/POST.md

@ -187,7 +187,7 @@ We organized the 3rd live [ABP Community Talks](https://community.abp.io/events)
* [Enis Necipoğlu](https://twitter.com/EnisNecipoglu) has also created [an article](https://community.abp.io/posts/using-autofilterer-with-abp-framework-uuqv81jm) to demonstrate how to use his own open source [AutoFilterer](https://github.com/enisn/AutoFilterer) library with the ABP Framework.
* [Jonathan Potts](https://github.com/jonathanpotts) has created his first ABP Community article that shows how to use Bootswatch themes with the ABP Framework. [See it here](https://community.abp.io/posts/customizing-the-abp-basic-theme-with-bootswatch-4luoqzr0).
Thanks to all the contributors. It is appreciated if you want to submit your post and share your knowledge with the ABP community: https://community.abp.io/posts/submit
Thanks to all the contributors. It is appreciated if you want to submit your post and share your knowledge with the ABP community: https://community.abp.io/posts/create
## Conclusion

2
docs/en/Blog-Posts/2024-02-16 v8_1_Preview/POST.md

@ -230,7 +230,7 @@ There are exciting articles contributed by the ABP community as always. I will h
* [A Best Practice for Designing Interfaces in .NET C#](https://community.abp.io/posts/a-best-practice-for-designing-interfaces-in-.net-c-9xqc4h8d)
* [Invariance, Covariance, and Contravariance in .NET C#](https://community.abp.io/posts/invariance-covariance-and-contravariance-in-.net-c-9blmuhme)
Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/submit) to the ABP Community.
Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/create) to the ABP Community.
## Conclusion

2
docs/en/Blog-Posts/2024-05-21 v8_2_Release/POST.md

@ -260,7 +260,7 @@ There are exciting articles contributed by the ABP community as always. I will h
* [Using FluentValidation with ABP Framework](https://community.abp.io/posts/using-fluentvalidation-with-abp-framework-2cxuwl70) by [Enes Döner](https://community.abp.io/members/Enes)
* [Using Blob Storage with ABP](https://community.abp.io/posts/using-blob-storage-with-abp-framework-jygtmhn4) by [Emre Kendirli](https://community.abp.io/members/emrekenderli)
Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/submit) to the ABP Community.
Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/create) to the ABP Community.
## Conclusion

2
docs/en/Blog-Posts/2024-07-31 v8_3_Preview/post.md

@ -168,7 +168,7 @@ There are exciting articles contributed by the ABP community as always. I will h
* [How to use .NET Aspire with ABP framework](https://abp.io/community/articles/how-to-use-.net-aspire-with-abp-framework-h29km4kk) by [Berkan Şaşmaz](https://twitter.com/berkansasmazz)
* [Exciting New Feature in ABP.IO CMS Kit: Marked Item System](https://abp.io/community/articles/exciting-new-feature-in-abp.io-cms-kit-marked-item-system.-2hvpq0me) by [Suhaib Mousa](https://abp.io/community/members/suhaibmousa032@gmail.com)
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/submit) to the ABP Community.
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.
## Conclusion

2
docs/en/Blog-Posts/2024-09-06 v8_3_Release_Stable/POST.md

@ -70,7 +70,7 @@ As always, exciting articles have been contributed by the ABP community. I will
* [Introducing the Google Cloud Storage BLOB Provider](https://abp.io/community/articles/introducing-the-google-cloud-storage-blob-provider-yrt6azc0) by [Engincan Veske](https://twitter.com/EngincanVeske)
* [Switching Between Organization Units](https://abp.io/community/articles/switching-between-organization-units-i5tokpzt) by [Liming Ma](https://github.com/maliming)
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/submit) to the ABP Community.
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

2
docs/en/Blog-Posts/2024-10-23 v9_0_Preview/POST.md

@ -214,7 +214,7 @@ There are exciting articles contributed by the ABP community as always. I will h
* [ABP-Powered Web App with Inertia.js, React, and Vite](https://abp.io/community/articles/abppowered-web-app-with-inertia.js-react-and-vite-j7cccvad) by [Anto Subash](https://antosubash.com/)
* [Multi-Tenancy Support in Angular Apps with ABP.IO](https://abp.io/community/articles/multitenancy-support-in-angular-apps-with-abp.io-lw9l36c5) by [HeadChannel Team](https://headchannel.co.uk/)
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/submit) to the ABP Community.
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.
## Conclusion

2
docs/en/Blog-Posts/2024-11-19 v9_0_Release_Stable/post.md

@ -78,7 +78,7 @@ In addition to [the articles to highlight .NET 9.0 features written by our team]
* [How to create your Own AI Bot on WhatsApp Using an ABP.io Template](https://abp.io/community/articles/how-to-create-your-own-ai-bot-on-whatsapp-using-the-abp-framework-c6jgvt9c) by [Michael Kokula](https://abp.io/community/members/Michal_Kokula)
* [ABP Now Supports .NET 9](https://abp.io/community/articles/abp-now-supports-.net-9-zpkznc4f) by [Alper Ebiçoğlu](https://x.com/alperebicoglu)
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/submit) to the ABP Community.
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.
### ABP Community Talks 2024.7: What’s New with .NET 9 & ABP 9?

2
docs/en/Blog-Posts/2025-01-21 v9_1_Preview/POST.md

@ -138,7 +138,7 @@ There are exciting articles contributed by the ABP community as always. I will h
* [The new Unit Test structure in ABP application](https://abp.io/community/articles/the-new-unit-test-structure-in-abp-application-4vvvp2oy) by [Liming Ma](https://github.com/maliming)
* [How to Use OpenAI API with ABP Framework](https://abp.io/community/articles/how-to-use-openai-api-with-abp-framework-rsfvihla) by [Berkan Şaşmaz](https://github.com/berkansasmaz)
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/submit) to the ABP Community.
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.
## Conclusion

2
docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/POST.md

@ -67,7 +67,7 @@ As always, exciting articles have been contributed by the ABP community. I will
* [Containerization: Blazor WASM + JWT Web API => Docker](https://abp.io/community/articles/containerization-blazor-wasm-jwt-web-api-docker-i3eirlsf) by [Bart Van Hoey](https://abp.io/community/members/bartvanhoey)
* [Configuring Post-Logout Redirect URI in ABP Based Blazor Applications with OpenIddict](https://abp.io/community/articles/configuring-postlogout-redirect-uri-in-abp-based-blazor-applications-with-openiddict-1t84suxg) by [Engincan Veske](https://github.com/EngincanV)
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/submit) to the ABP Community.
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.
### ABP Community Talks 2025.2: Real World Problems and Solutions with AI

195
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/POST.md

@ -0,0 +1,195 @@
# ABP Platform 9.2 RC Has Been Released
We are happy to release [ABP](https://abp.io) version **9.2 RC** (Release Candidate). This blog post introduces the new features and important changes in this new version.
Try this version and provide feedback for a more stable version of ABP v9.2! Thanks to you in advance.
## Get Started with the 9.2 RC
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).
By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI:
![studio-switch-to-preview.png](studio-switch-to-preview.png)
## Migration Guide
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 or earlier: [ABP Version 9.2 Migration Guide](https://abp.io/docs/9.2/release-info/migration-guides/abp-9-2)
## What's New with ABP v9.2?
In this section, I will introduce some major features released in this version.
Here is a brief list of titles explained in the next sections:
* Added `ApplicationName` Property to Isolate Background Jobs & Background Workers
* Docs Module: Added "Alternative Words" to Filter Items
* Introducing the Bunny BLOB Storage Provider
* Upgraded `MongoDB.Driver` to v3.1.0
* Using Timezone Settings to Display Datetime
* Identity Pro Module: Require Email Verification to Register
* Switching users during OAuth login
### Added ApplicationName Property to Isolate Background Jobs & Background Workers
ABP's [Background Jobs Module](https://abp.io/docs/latest/modules/background-jobs) has been enhanced with a new `ApplicationName` property that helps isolate jobs and workers across multiple applications sharing the same database.
Previously, when different applications used the BackgroundJobs module and shared a database, an application might encounter jobs that didn't belong to it. This would lead to failed processing attempts and marking jobs as `IsAbandoned = true` with a "Undefined background job for the job name" error, preventing these jobs from ever being executed.
With the new `ApplicationName` property, applications now properly filter jobs at the repository level, ensuring each application only processes job types it recognizes. This prevents the incorrect abandonment of jobs and ensures consistent behavior in multi-application scenarios.
You can set `ApplicationName` of `AbpBackgroundJobWorkerOptions` to your application name to isolate jobs and workers across multiple applications sharing the same database:
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpBackgroundJobWorkerOptions>(options =>
{
options.ApplicationName = context.Services.GetApplicationName()!;
});
}
```
> For more information, please refer to the [Background Jobs Module](https://abp.io/docs/latest/modules/background-jobs) documentation and the [PR](https://github.com/abpframework/abp/pull/22169) that added this feature.
### Docs Module: Added "Alternative Words" to Filter Items
[ABP's Docs Module](https://abp.io/docs/9.2/modules/docs) now supports "alternative words" to enhance the search functionality when filtering documentation items. This feature addresses a common user experience issue where users might search using terminology different from what appears in the documentation.
For example, when a user searches for "Error" in the documentation, they may actually be looking for content related to "Exception Handling." With this new feature, documentation items can now be configured with alternative keywords that are considered during filtering.
The implementation allows defining optional "keywords" for items in the navigation tree. For example:
```json
{
"text": "Exception Handling",
"path": "framework/fundamentals/exception-handling.md",
"keywords": ["Error", "Another Value"]
}
```
When users search or filter content, the system now considers both the original text and these alternative keywords, improving discoverability of relevant documentation sections. This enhancement makes the documentation more accessible and user-friendly, especially for newcomers who might not be familiar with the exact terminology used in the ABP documentation.
### Introducing the Bunny BLOB Storage Provider
ABP v9.2 RC introduces a new BLOB storage provider for [Bunny Storage](https://bunny.net/storage/), a global edge storage solution. This addition expands ABP's BLOB Storage options beyond the existing providers like Azure, AWS, and others.
The [Bunny BLOB Storage Provider](https://abp.io/docs/9.2/framework/infrastructure/blob-storing/bunny) allows ABP applications to seamlessly integrate with Bunny's CDN-backed storage service, which offers high-performance content delivery through its global network.
To use this new provider, you'll need to:
* Run `abp add-package Volo.Abp.BlobStoring.Bunny` command.
* And then configure the provider in your module's `ConfigureServices` method:
```csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.ConfigureDefault(container =>
{
container.UseBunny(bunny =>
{
bunny.StorageZoneName = "your-storage-zone";
bunny.ApiKey = "your-api-key";
bunny.Region = "your-region"; // de, ny, la, sg, or sy
});
});
});
```
This integration provides ABP applications with an efficient and globally distributed storage solution, particularly beneficial for applications requiring fast content delivery across different geographical regions. To use this new provider and make the related configurations, you can refer to the [Bunny Storage Provider](https://abp.io/docs/9.2/framework/infrastructure/blob-storing/bunny) documentation always.
> This new BLOB Storage provider is contributed by [@suhaib-mousa](https://github.com/suhaib-mousa). Thanks to him for his contribution!
> We are always happy to see the community contributing to the ABP Framework and encouraging them to contribute more.
### Upgraded `MongoDB.Driver` to `v3.1.0`
ABP v9.2 RC includes an upgrade to `MongoDB.Driver` version `3.1.0`. This significant version bump from previous releases brings several improvements and new features that benefit ABP applications using MongoDB as their database.
The upgrade provides:
* Async/Await Support: Write non-blocking, asynchronous code easily.
* Fluent API: Build queries and updates intuitively with Builders.
* LINQ Support: Use LINQ for querying MongoDB collections.
* and more ...
> For more information, please refer to the [MongoDB.Driver release notes](https://github.com/mongodb/mongo-csharp-driver/releases/tag/v3.1.0).
We have prepared a [migration guide](https://abp.io/docs/9.2/release-info/migration-guides/MongoDB-Driver-2-to-3) for this upgrade. Please refer to it to learn more about the changes and how to migrate your application.
### Using Timezone Settings to Display Datetime
A significant enhancement in ABP v9.2 is the ability to use timezone settings to display `DateTime` values according to the user's or application's configured timezone. Introduced in `v9.2.0-rc.2`, this feature addresses the common challenge of ensuring users see accurate time information, regardless of their geographical location.
Previously, `DateTime` values were often shown in the server's timezone or in UTC, which could cause confusion for users in different timezones. With this new feature, ABP applications can now respect the configured timezone and automatically convert and display datetime values accordingly.
**Before setting the timezone:**
Consider a scenario where you have a list of books, and the `CreationTime` property is displayed without any timezone consideration. It may appear in the server's default timezone:
![before.png](before.png)
*(Screenshot of a Books page showing the CreationTime in a default timezone.)*
**Setting the timezone:**
To set the timezone, start by configuring the `AbpClockOptions` in your module's `ConfigureServices` method:
```csharp
Configure<AbpClockOptions>(options =>
{
options.Kind = DateTimeKind.Utc;
});
```
> By setting the `Kind` property of `AbpClockOptions` to `DateTimeKind.Utc`, ABP will normalize all datetime values. Times stored in the database and returned to the frontend will be in UTC. Additionally, the `SupportsMultipleTimezone` property of the `IClock` service will be **true**, and you’ll be able to configure the timezone from the UI under the _Settings_ page.
After setting the `Kind` property, you can run your application and configure the timezone from the UI under the _Settings_ page. For example, set the timezone to "Asia/Tokyo (+09:00)":
![configure-timezone.png](configure-timezone.png)
*(Screenshot showing the configuration setting for Abp.Timing.TimeZone, set to "Asia/Tokyo (+09:00)".)*
> ABP provides the `Abp.Timing.TimeZone` setting, which allows you to configure the desired timezone at the application, tenant, or user level and this setting can be configured in the UI under the _Settings_ page, inside of the _Time Zone_ tab.
**After setting the timezone:**
Once the timezone is configured, ABP automatically handles the conversion and display of `DateTime` values. The `CreationTime` on the _Books_ page will now be shown in the **"Asia/Tokyo"** timezone.
![after.png](after.png)
*(Screenshot of the _Books_ page after setting the timezone, showing the `CreationTime` adjusted to the "Asia/Tokyo" timezone.)*
This feature utilizes the `IClock` service, which provides methods like `ConvertToUserTime` and `ConvertToUtc` to facilitate timezone conversions. By configuring the `Abp.Timing.TimeZone` setting, developers can ensure a consistent and user-friendly experience across applications with a global user base.
> For a more detailed guide on implementing and using this feature, refer to the article: [Developing a Multi-Timezone Application Using the ABP Framework](https://abp.io/community/articles/developing-a-multitimezone-application-using-the-abp-framework-zk7fnrdq). It offers step-by-step instructions and examples for handling multi-timezone scenarios in ABP applications.
### Identity Pro Module: Require Email Verification to Register
[ABP Identity Pro module](https://abp.io/docs/9.2/modules/identity-pro) has been enhanced with a new feature that allows administrators to require email verification during the registration process. This security improvement ensures that users must verify their email addresses before their registration is considered complete. Enabling this feature is especially important for applications that want to prevent spam registrations.
Administrators can enable or disable this feature through the **Identity management -> Identity Verification (tab)** settings page (by checking the `Enforce email verification to register` checkbox):
![require-email-verification.png](./require-email-verification-for-register.png)
### Switching users during OAuth login
If you have an OAuth/Auth Server application using the [ABP Account Pro module](https://abp.io/docs/9.2/modules/account-pro) , you can pass the `prompt=select_account` parameter to force the user to select an account.
![select-account.png](./select-account.png)
For more information, please refer to the [Switching users during OAuth login](https://abp.io/docs/9.2/modules/account-pro#switching-users-during-oauth-login) documentation.
### New ABP Community Articles
There are exciting articles contributed by the ABP community as always. I will highlight some of them here:
* [Implementing CQRS with MediatR in ABP](https://abp.io/community/articles/implementing-cqrs-with-mediatr-in-abp-xiqz2iio) by [Engincan Veske](https://github.com/EngincanV)
* [Using Vue Components in a Razor Pages ABP Application](https://abp.io/community/articles/using-vue-components-in-a-razor-pages-abp-application-z3jr07tv) by [Enis Necipoglu](https://github.com/enisn)
* [Using ABP's AWS Blob Storing Provider with DigitalOcean Spaces](https://abp.io/community/articles/using-abps-aws-blob-storing-provider-with-digitalocean-spaces-7hlyb25g) by [Suhaib Mousa](https://abp.io/community/members/suhaib-mousa)
* [Video Post: Using Vue Components in a Razor Pages ABP Application](https://abp.io/community/articles/using-vue-components-in-a-razor-pages-abp-application-z3jr07tv) by [Enis Necipoglu](https://github.com/enisn)
* [Understanding the Embedded Files in ABP Framework](https://abp.io/community/articles/understanding-the-embedded-files-in-abp-framework-nsrp8aa9) by [Liming Ma](https://github.com/maliming)
* [How to Change the CurrentUser in ABP?](https://abp.io/community/articles/how-to-change-the-currentuser-in-abp-i3uu1m7g) by [Engincan Veske](https://github.com/EngincanV)
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.
## Conclusion
This version comes with some new features and a lot of enhancements to the existing features. You can see the [Road Map](https://abp.io/docs/9.2/release-info/road-map) documentation to learn about the release schedule and planned features for the next releases. Please try ABP v9.2 RC and provide feedback to help us release a more stable version.
Thanks for being a part of this community!

BIN
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/after.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/before.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/configure-timezone.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/cover-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

BIN
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/require-email-verification-for-register.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

BIN
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/select-account.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 KiB

BIN
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/studio-switch-to-preview.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

2
docs/en/Community-Articles/2022-03-09-abpio-platform-52-rc-has-been-published/post.md

@ -186,7 +186,7 @@ We organized the 3rd live [ABP Community Talks](https://community.abp.io/events)
* [Enis Necipoğlu](https://twitter.com/EnisNecipoglu) has also created [an article](https://community.abp.io/posts/using-autofilterer-with-abp-framework-uuqv81jm) to demonstrate how to use his own open source [AutoFilterer](https://github.com/enisn/AutoFilterer) library with the ABP Framework.
* [Jonathan Potts](https://github.com/jonathanpotts) has created his first ABP Community article that shows how to use Bootswatch themes with the ABP Framework. [See it here](https://community.abp.io/posts/customizing-the-abp-basic-theme-with-bootswatch-4luoqzr0).
Thanks to all the contributors. It is appreciated if you want to submit your post and share your knowledge with the ABP community: https://community.abp.io/posts/submit
Thanks to all the contributors. It is appreciated if you want to submit your post and share your knowledge with the ABP community: https://community.abp.io/posts/create
## Conclusion

2
docs/en/Community-Articles/2024-02-19-abpio-platform-81-rc-has-been-published/post.md

@ -228,7 +228,7 @@ There are exciting articles contributed by the ABP community as always. I will h
* [A Best Practice for Designing Interfaces in .NET C#](https://community.abp.io/posts/a-best-practice-for-designing-interfaces-in-.net-c-9xqc4h8d)
* [Invariance, Covariance, and Contravariance in .NET C#](https://community.abp.io/posts/invariance-covariance-and-contravariance-in-.net-c-9blmuhme)
Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/submit) to the ABP Community.
Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/create) to the ABP Community.
## Conclusion

2
docs/en/Community-Articles/2024-05-24-abpio-platform-82-rc-has-been-published/post.md

@ -259,7 +259,7 @@ There are exciting articles contributed by the ABP community as always. I will h
* [Using FluentValidation with ABP Framework](https://community.abp.io/posts/using-fluentvalidation-with-abp-framework-2cxuwl70) by [Enes Döner](https://community.abp.io/members/Enes)
* [Using Blob Storage with ABP](https://community.abp.io/posts/using-blob-storage-with-abp-framework-jygtmhn4) by [Emre Kendirli](https://community.abp.io/members/emrekenderli)
Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/submit) to the ABP Community.
Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/create) to the ABP Community.
## Conclusion

2
docs/en/Community-Articles/2024-08-01-abpio-platform-83-rc-has-been-published/post.md

@ -165,7 +165,7 @@ There are exciting articles contributed by the ABP community as always. I will h
* [How to use .NET Aspire with ABP framework](https://abp.io/community/articles/how-to-use-.net-aspire-with-abp-framework-h29km4kk) by [Berkan Şaşmaz](https://twitter.com/berkansasmazz)
* [Exciting New Feature in ABP.IO CMS Kit: Marked Item System](https://abp.io/community/articles/exciting-new-feature-in-abp.io-cms-kit-marked-item-system.-2hvpq0me) by [Suhaib Mousa](https://abp.io/community/members/suhaibmousa032@gmail.com)
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/submit) to the ABP Community.
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.
## Conclusion

2
docs/en/Community-Articles/2024-09-12-abpio-platform-83-final-has-been-released/post.md

@ -70,7 +70,7 @@ As always, exciting articles have been contributed by the ABP community. I will
* [Introducing the Google Cloud Storage BLOB Provider](https://abp.io/community/articles/introducing-the-google-cloud-storage-blob-provider-yrt6azc0) by [Engincan Veske](https://twitter.com/EngincanVeske)
* [Switching Between Organization Units](https://abp.io/community/articles/switching-between-organization-units-i5tokpzt) by [Liming Ma](https://github.com/maliming)
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/submit) to the ABP Community.
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

2
docs/en/Community-Articles/2024-10-23-abpio-platform-90-rc-has-been-published/post.md

@ -210,7 +210,7 @@ There are exciting articles contributed by the ABP community as always. I will h
* [ABP-Powered Web App with Inertia.js, React, and Vite](https://abp.io/community/articles/abppowered-web-app-with-inertia.js-react-and-vite-j7cccvad) by [Anto Subash](https://antosubash.com/)
* [Multi-Tenancy Support in Angular Apps with ABP.IO](https://abp.io/community/articles/multitenancy-support-in-angular-apps-with-abp.io-lw9l36c5) by [HeadChannel Team](https://headchannel.co.uk/)
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/submit) to the ABP Community.
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.
## Conclusion

2
docs/en/Community-Articles/2024-11-21-abpio-platform-90-has-been-released-based-on-net-90/post.md

@ -80,7 +80,7 @@ In addition to [the articles to highlight .NET 9.0 features written by our team]
* [How to create your Own AI Bot on WhatsApp Using an ABP.io Template](https://abp.io/community/articles/how-to-create-your-own-ai-bot-on-whatsapp-using-the-abp-framework-c6jgvt9c) by [Michael Kokula](https://abp.io/community/members/Michal_Kokula)
* [ABP Now Supports .NET 9](https://abp.io/community/articles/abp-now-supports-.net-9-zpkznc4f) by [Alper Ebiçoğlu](https://x.com/alperebicoglu)
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/submit) to the ABP Community.
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.
## Conclusion

2
docs/en/Community-Articles/2025-01-21-abp-platform-91-rc-has-been-released/post.md

@ -136,7 +136,7 @@ There are exciting articles contributed by the ABP community as always. I will h
* [The new Unit Test structure in ABP application](https://abp.io/community/articles/the-new-unit-test-structure-in-abp-application-4vvvp2oy) by [Liming Ma](https://github.com/maliming)
* [How to Use OpenAI API with ABP Framework](https://abp.io/community/articles/how-to-use-openai-api-with-abp-framework-rsfvihla) by [Berkan Şaşmaz](https://github.com/berkansasmaz)
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/submit) to the ABP Community.
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.
## Conclusion

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list-utc1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list-utc3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/berlin.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-create.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-edit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-create.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-edit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list-utc1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list-utc3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

BIN
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-post.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

781
docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/post.md

@ -0,0 +1,781 @@
# Developing a Multi-Timezone Application Using the ABP Framework
When developing multi-timezone applications, we need to handle users from different time zones and make sure they see the correct time. The system also needs to support users changing their timezone (like when traveling or moving) and make sure all time displays update correctly to show accurate time information.
All these scenarios require us to handle timezone conversions correctly in our application. The ABP framework provides a complete solution for these challenges.
In this article, we'll show you step by step how to handle multi-timezone in the ABP framework.
> The content mentioned in this article will be available after the ABP 9.2 version
## Timezone Settings
The ABP framework provides a setting called `Abp.Timing.TimeZone` for setting and getting the timezone of users, tenants, or applications. The default value is empty, which means the application will use the server's time zone. Check out the [Timing documentation](https://abp.io/docs/latest/framework/infrastructure/timing) for more information.
## ISO 8601 Date Time Format
Different countries and regions may use different time formats:
* Year-Month-Day (YYYY-MM-DD): Mainly used in China, Japan, Korea, Canada (official standard), Germany (ISO standard), ISO 8601 international standard, etc. Example: 2025-03-11
* Day-Month-Year (DD-MM-YYYY): Mainly used in UK, India, Australia, New Zealand, most European countries (like France, Germany, Italy, Spain), some South American countries, etc. Example: 11-03-2025 or 11/03/2025
* Month-Day-Year (MM-DD-YYYY): Mainly used in USA, Philippines, some parts of Canada, etc. Example: 03-11-2025 or 03/11/2025
* Day.Month.Year (DD.MM.YYYY): Mainly used in Germany, Russia, Switzerland, Hungary, Czech Republic, etc. Example: 11.03.2025
Also, different countries/regions might use different separators (like slash /, hyphen -, dot .), and some countries use different month abbreviations or full names (like March 11, 2025).
ISO 8601 uses a standard format to avoid confusion between different date formats and ensure global compatibility.
It has 4 parts:
* Date part: `YYYY-MM-DD`
* `T` as a separator
* Time part: `HH:MM:SS`
* Timezone part: `Z` or `+/-HH:MM`
You'll usually see formats like: `YYYY-MM-DDTHH:MM:SSZ` or `YYYY-MM-DDTHH:MM:SS+/-HH:MM`, for example: `2025-03-11T10:30:00Z` or `2025-03-11T22:30:00+03:00`
When our application needs to handle multiple timezones, we usually use ISO 8601 to represent time.
## Enabling Multi-Timezone Support
When we set the `Kind` of `AbpClockOptions` to `DateTimeKind.Utc`, the ABP framework will normalize all times. Times written to the database and returned to the frontend will be in `UTC`. the `SupportsMultipleTimezone` property will be `true` in the `IClock` service.
```csharp
Configure<AbpClockOptions>(options =>
{
options.Kind = DateTimeKind.Utc;
});
```
### Using DateTime to Store Time
Assuming the `DateTime` stored in the database is `2025-03-01 10:30:00`, then the time returned to the front end will be `2025-03-01T10:30:00Z`. This is a time in ISO 8601 format. Because `DateTime` does not have timezone information, the framework will assume it is `UTC` time.
### Using DateTimeOffset to Store Time
If you use `DateTimeOffset` to store time, the ABP framework will not normalize `DateTimeOffset`, but will return it directly to the front end.
Assuming the `DateTimeOffset` stored in the database is `2025-03-01 13:30:00 +03:00`, then the time returned to the front end will be `2025-03-01T13:30:00+03:00`. This is also a time in ISO 8601 format.
We recommend using `DateTimeOffset` to store time because it has timezone information.
## Timezone Conversion
### Converting UTC Time to User Time
The `IClock` service has 2 methods to convert a given `UTC` time to the user time:
```csharp
DateTime ConvertToUserTime(utcDateTime dateTime)
DateTimeOffset ConvertToUserTime(DateTimeOffset dateTimeOffset)
```
> If `SupportsMultipleTimezone` is `false` or `dateTime.Kind` is not `Utc` or no timezone is set, it will return the given `DateTime` or `DateTimeOffset` without any changes.
**Example:**
If the user's timezone is `Europe/Istanbul`
```csharp
// 2025-03-01T05:30:00Z
var utcTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc);
var userTime = Clock.ConvertToUserTime(utcTime);
// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours later.
userTime.Kind.ShouldBe(DateTimeKind.Unspecified);
userTime.ToString("O").ShouldBe("2025-03-01T08:30:00");
```
```csharp
// 2025-03-01T05:30:00Z
var utcTime = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc), TimeSpan.Zero);
var userTime = Clock.ConvertToUserTime(utcTime);
// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours later.
userTime.Offset.ShouldBe(TimeSpan.FromHours(3));
userTime.ToString("O").ShouldBe("2025-03-01T08:30:00.0000000+03:00");
```
### Converting User Time to UTC
The `IClock` service has 1 method to convert a given user time to UTC.
```csharp
DateTime ConvertToUtc(DateTime dateTime)
```
> If `SupportsMultipleTimezone` is `false` or `dateTime.Kind` is `Utc` or no timezone is set, it will return the given `DateTime` without any changes.
**Example:**
If the user's timezone is `Europe/Istanbul`
```csharp
// 2025-03-01T05:30:00
var userTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified); //Same as Local
var utcTime = Clock.ConvertToUtc(userTime);
// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours earlier.
utcTime.Kind.ShouldBe(DateTimeKind.Utc);
utcTime.ToString("O").ShouldBe("2025-03-01T02:30:00.0000000Z");
```
## Handling Timezone in Different UIs
We'll use the `TimeZoneApp` project to demonstrate handling timezone in different UIs. It has a `Meeting` entity, with several time properties.
```csharp
public class Meeting : AggregateRoot<Guid>
{
public string Subject { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public DateTime ActualStartTime { get; set; }
public DateTime? CanceledTime { get; set; }
public DateTimeOffset ReminderTime { get; set; }
public DateTimeOffset? FollowUpTime { get; set; }
public string Description { get; set; }
}
```
`TimeZoneApp` project is an ABP layered architecture project, it sets a global `Europe/Istanbul` timezone, it contains 4 websites
* `API.Host`: API website, it does not have UI, it returns data in JSON format
* `AuthServer`: Authentication server, it uses Razor Pages as UI
* `Web`: Razor Pages website, it uses JavaScript to manage Meeting creation and editing and display
* `Blazor`: Blazor Server website, it uses Blazor to manage Meeting creation and editing and display
All 4 applications are enabled for multi-timezone support, and use the `UseAbpTimeZone` middleware.
> Blazor WASM and Angular do not need to use the `UseAbpTimeZone` middleware
### DateTime in API Response
In the API response, we usually use the ISO 8601 format time, as you can see, after enabling multi-timezone support, the API returns time to the front end as UTC time.
`2025-03-01T09:30:00Z` and `2025-03-01T12:30:00+00:00` are ISO 8601 format time.
```json
[
{
"subject": "ABP Developer Guide",
"startTime": "2025-03-01T09:30:00Z",
"endTime": "2025-03-01T10:30:00Z",
"actualStartTime": "2025-03-01T11:30:00Z",
"canceledTime": null,
"reminderTime": "2025-03-01T12:30:00+00:00",
"followUpTime": "2025-03-01T13:30:00+00:00",
"description": "We will discuss the ABP developer guide.",
"id": "2af0abd3-be06-ecff-5d4c-3a1895ac7950"
},
{
"subject": "ABP Training",
"startTime": "2025-03-01T09:30:00Z",
"endTime": "2025-03-01T10:30:00Z",
"actualStartTime": "2025-03-01T11:30:00Z",
"canceledTime": "2025-03-01T12:00:00Z",
"reminderTime": "2025-03-01T12:30:00+00:00",
"followUpTime": "2025-03-01T13:30:00+00:00",
"description": "ABP training for the new developers.",
"id": "290b0cb6-3e50-6324-1e79-3a1895ac795f"
}
]
```
### Handling Timezone in MVC/Razor Pages
In the `AuthServer` project, we handle time conversion in a simple way:
1. First, we get the `Meeting` entities from the database using `IRepository<Meeting, Guid>`. At this point, all `DateTime` values are in UTC.
2. Then, when displaying the times in the view, we use `Clock.ConvertToUserTime` to show them in the user's timezone.
> Note: The `ConvertToUserTime` method will only convert times if multi-timezone support is enabled in the application.
```csharp
public class IndexModel : AbpPageModel
{
public List<Meeting>? Meetings { get; set; }
protected IRepository<Meeting, Guid> MeetingRepository { get; }
public IndexModel(IRepository<Meeting, Guid> meetingRepository)
{
MeetingRepository = meetingRepository;
}
public async Task OnGetAsync()
{
Meetings = await MeetingRepository.GetListAsync();
}
}
```
```html
<div class="container">
<abp-row>
<div class="table-responsive">
<table class="table table-striped table-hover mt-3">
<thead>
<tr>
<th>@L["Subject"]</th>
<th>@L["StartTime"] / @L["EndTime"]</th>
<th>@L["ActualStartTime"]</th>
<th>@L["CanceledTime"]</th>
<th>@L["ReminderTime"]</th>
<th>@L["FollowUpTime"]</th>
<th>@L["Description"]</th>
</tr>
</thead>
<tbody>
@foreach (var meeting in Model.Meetings)
{
<tr>
<td>@meeting.Subject</td>
<td>@Clock.ConvertToUserTime(meeting.StartTime) ➡️ @Clock.ConvertToUserTime(meeting.EndTime)</td>
<td>@Clock.ConvertToUserTime(meeting.ActualStartTime)</td>
<td>@(meeting.CanceledTime.HasValue ? Clock.ConvertToUserTime(meeting.CanceledTime.Value) : "N/A")</td>
<td>@Clock.ConvertToUserTime(meeting.ReminderTime).DateTime</td>
<td>@(meeting.FollowUpTime.HasValue ? Clock.ConvertToUserTime(meeting.FollowUpTime.Value).DateTime : "N/A")</td>
<td>@meeting.Description</td>
</tr>
}
</tbody>
</table>
</div>
</abp-row>
</div>
```
![](auth-list.png)
### Handling Timezone in JavaScript
In the `Web` project, we use JavaScript to handle timezone.
#### Displaying Time in UI
* `timeZoneApp.meetings.meeting.getList` gets all `Meeting` entities and displays them in `DataTables`
* `abp.clock.normalizeToLocaleString()` is the ABP JavaScript API, it converts `UTC` time to the current user's timezone, and then calls its `toLocaleString` method to format time
* `dataFormat: "datetime"` is the ABP DataTable extension method, it calls the `abp.clock.normalizeToLocaleString` method to convert and format time
> If the current application is not enabled for multi-timezone support, then the `abp.clock.normalizeToLocaleString` method will not convert the time, it will just call the `Date` object's `toLocaleString` method.
```js
var dataTable = $('#MeetingsTable').DataTable(
abp.libs.datatables.normalizeConfiguration({
serverSide: true,
paging: true,
order: [[1, "asc"]],
searching: false,
scrollX: true,
ajax: abp.libs.datatables.createAjax(timeZoneApp.meetings.meeting.getList),
columnDefs: [
{
title: l('Actions'),
rowAction: {
items:
[
{
text: l('Edit'),
visible: abp.auth.isGranted('TimeZoneApp.Meetings.Edit'),
action: function (data) {
editModal.open({ id: data.record.id });
},
},
{
text: l('Delete'),
visible: abp.auth.isGranted('TimeZoneApp.Meetings.Delete'),
confirmMessage: function (data) {
return l('MeetingDeletionConfirmationMessage', data.record.subject);
},
action: function (data) {
timeZoneApp.meetings.meeting
.delete(data.record.id)
.then(function() {
abp.notify.info(l('SuccessfullyDeleted'));
dataTable.ajax.reload();
});
}
}
]
}
},
{
title: l('Subject'),
data: "subject"
},
{
title: l('StartTime') + ' / ' + l('StartTime'),
data: "startTime",
render: function (data, type, row) {
return abp.clock.normalizeToLocaleString(row.startTime) + ' ➡️ ' + abp.clock.normalizeToLocaleString(row.endTime);
}
},
{
title: l('ActualStartTime'),
data: "actualStartTime",
dataFormat: "datetime"
},
{
title: l('CanceledTime'),
data: "canceledTime",
render: function (data, type, row) {
return data ? abp.clock.normalizeToLocaleString(data) : 'N/A';
}
},
{
title: l('ReminderTime'),
data: "reminderTime",
dataFormat: "datetime"
},
{
title: l('FollowUpTime'),
data: "followUpTime",
render: function (data, type, row) {
return data ? abp.clock.normalizeToLocaleString(data) : 'N/A';
}
},
{
title: l('Description'),
data: "description"
}
]
})
);
```
Below is the screenshot of `DataTables`:
![](mvc-list.png)
#### Creating and Editing Meeting
We use `JavaScript` to create and edit `Meeting`.
ABP's [TagHelper](https://abp.io/docs/latest/framework/ui/mvc-razor-pages/tag-helpers) can automatically create forms based on the model, it will generate corresponding HTML tags based on the attributes in the model. For `DateTime` and `DateTimeOffset` attributes, it will generate and initialize a [DateTimePicker](https://www.daterangepicker.com/) component.
**CreateModal** and **EditModal** :
```html
<abp-dynamic-form abp-model="Meeting" asp-page="/Meetings/CreateModal">
<abp-modal>
<abp-modal-header title="@L["NewMeeting"].Value"></abp-modal-header>
<abp-modal-body>
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
```
```html
<abp-dynamic-form abp-model="Meeting" asp-page="/Meetings/EditModal">
<abp-modal>
<abp-modal-header title="@L["Update"].Value"></abp-modal-header>
<abp-modal-body>
<abp-input asp-for="Id" />
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
```
You can see that the time in the control has been converted to the current user's timezone.
![](mvc-create.png)
![](mvc-edit.png)
When we submit the form, we need to convert the time to `UTC`. In the `JavaScript` of the `Create` and `Edit` pages, we use the `handleDatepicker` this `jQuery` extension method to handle time in the form, it internally gets the user's local time from the selector `input[type="hidden"][data-hidden-datepicker]`, and then uses the `abp.clock.normalizeToString` method to convert the date field in the form to the `ISO 8601` format `UTC` time string.
> If the current application is not enabled for multi-timezone support, then the `abp.clock.normalizeToString` method will not convert the time, it will just convert to the ISO 8601 format time string without timezone.
```js
var abp = abp || {};
$(function () {
abp.modals.meetingCreate = function () {
var initModal = function (publicApi, args) {
var $form = publicApi.getForm();
$form.find('button[type="submit"]').on('click', function (e) {
$form.handleDatepicker('input[type="hidden"][data-hidden-datepicker]');
});
};
return {
initModal: initModal
}
};
});
```
The requested data is as follows:
```csharp
Request URL: Meetings/EditModal
Request Method: POST
Payload:
Id: 0803780b-3762-2af8-1c75-3a1895d59c89
Meeting.Subject: ABP Developer Guide
Meeting.StartTime: 2025-03-01T09:30:00.000Z
Meeting.EndTime: 2025-03-01T10:30:00.000Z
Meeting.ActualStartTime: 2025-03-01T11:30:00.000Z
Meeting.CanceledTime:
Meeting.ReminderTime: 2025-03-01T12:30:00.000Z
Meeting.FollowUpTime: 2025-03-01T13:30:00.000Z
Meeting.Description: We will discuss the ABP developer guide.
```
![](mvc-post.png)
In short, we use the `abp.clock.normalizeToLocaleString` method to display time, and use the `abp.clock.normalizeToString` method to modify the time to be submitted. If you submit data via `ajax`, please remember to use the `abp.clock.normalizeToString` method to convert time.
### Handling Timezone in Blazor
We cannot automatically complete some work in `Blazor UI`, we need to inject `IClock` and use the `ConvertToUserTime` and `ConvertToUtc` methods to display and create/update entities.
Below is a complete `Meeting` page, please refer to the usage of `Clock` in it.
```csharp
@page "/meetings"
@using Volo.Abp.Application.Dtos
@using Microsoft.Extensions.Localization
@using TimeZoneApp.Meetings
@using TimeZoneApp.Localization
@using TimeZoneApp.Permissions
@using Volo.Abp.AspNetCore.Components.Web
@inject IStringLocalizer<TimeZoneAppResource> L
@inject AbpBlazorMessageLocalizerHelper<TimeZoneAppResource> LH
@inherits AbpCrudPageBase<IMeetingAppService, MeetingDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateMeetingDto>
<Card>
<CardHeader>
<Row Class="justify-content-between">
<Column ColumnSize="ColumnSize.IsAuto">
<h2>@L["Meetings"]</h2>
</Column>
<Column ColumnSize="ColumnSize.IsAuto">
@if (HasCreatePermission)
{
<Button Color="Color.Primary" Clicked="OpenCreateModalAsync">@L["NewMeeting"]</Button>
}
</Column>
</Row>
</CardHeader>
<CardBody>
<DataGrid TItem="MeetingDto"
Data="Entities"
ReadData="OnDataGridReadAsync"
TotalItems="TotalCount"
ShowPager="true"
PageSize="PageSize">
<DataGridColumns>
<DataGridEntityActionsColumn TItem="MeetingDto" @ref="@EntityActionsColumn">
<DisplayTemplate>
<EntityActions TItem="MeetingDto" EntityActionsColumn="@EntityActionsColumn">
<EntityAction TItem="MeetingDto"
Text="@L["Edit"]"
Visible=HasUpdatePermission
Clicked="() => OpenEditModalAsync(context)" />
<EntityAction TItem="MeetingDto"
Text="@L["Delete"]"
Clicked="() => DeleteEntityAsync(context)"
Visible=HasDeletePermission
ConfirmationMessage="()=>GetDeleteConfirmationMessage(context)" />
</EntityActions>
</DisplayTemplate>
</DataGridEntityActionsColumn>
<DataGridColumn TItem="MeetingDto"
Field="@nameof(MeetingDto.Subject)"
Caption="@L["Subject"]"></DataGridColumn>
<DataGridColumn TItem="MeetingDto"
Field="@nameof(MeetingDto.StartTime)"
Caption="@(L["StartTime"] + "/" + L["EndTime"])">
<DisplayTemplate>
@Clock.ConvertToUserTime(context.StartTime).ToString("yyyy-MM-dd HH:mm:ss") ➡️ @Clock.ConvertToUserTime(context.EndTime).ToString("yyyy-MM-dd HH:mm:ss")
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="MeetingDto"
Field="@nameof(MeetingDto.ActualStartTime)"
Caption="@L["ActualStartTime"]">
<DisplayTemplate>
@Clock.ConvertToUserTime(context.ActualStartTime).ToString("yyyy-MM-dd HH:mm:ss")
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="MeetingDto"
Field="@nameof(MeetingDto.CanceledTime)"
Caption="@L["CanceledTime"]">
<DisplayTemplate>
@(context.CanceledTime.HasValue ? Clock.ConvertToUserTime(context.CanceledTime.Value).ToString("yyyy-MM-dd HH:mm:ss") : "N/A")
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="MeetingDto"
Field="@nameof(MeetingDto.ReminderTime)"
Caption="@L["ReminderTime"]">
<DisplayTemplate>
@(Clock.ConvertToUserTime(context.ReminderTime).ToString("yyyy-MM-dd HH:mm:ss") )
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="MeetingDto"
Field="@nameof(MeetingDto.FollowUpTime)"
Caption="@L["FollowUpTime"]">
<DisplayTemplate>
@(context.FollowUpTime.HasValue ? Clock.ConvertToUserTime(context.FollowUpTime.Value).ToString("yyyy-MM-dd HH:mm:ss") : "N/A")
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="MeetingDto"
Field="@nameof(MeetingDto.Description)"
Caption="@L["Description"]">
</DataGridColumn>
</DataGridColumns>
</DataGrid>
</CardBody>
</Card>
<Modal @ref="@CreateModal">
<ModalContent IsCentered="true">
<Form>
<ModalHeader>
<ModalTitle>@L["NewMeeting"]</ModalTitle>
<CloseButton Clicked="CloseCreateModalAsync"/>
</ModalHeader>
<ModalBody>
<Validations @ref="@CreateValidationsRef" Model="@NewEntity" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["Subject"]</FieldLabel>
<TextEdit @bind-Text="@NewEntity.Subject">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Field>
<FieldLabel>@L["StartTime"] / @L["EndTime"]</FieldLabel>
<DatePicker TValue="DateTime?" @bind-Dates="SelectedDates" InputMode="DateInputMode.DateTime" SelectionMode="DateInputSelectionMode.Range" />
</Field>
<Field>
<FieldLabel>@L["ActualStartTime"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="NewEntity.ActualStartTime" InputMode="DateInputMode.DateTime"/>
</Field>
<Field>
<FieldLabel>@L["CanceledTime"]</FieldLabel>
<DateEdit TValue="DateTime?" @bind-Date="NewEntity.CanceledTime" InputMode="DateInputMode.DateTime"/>
</Field>
<Field>
<FieldLabel>@L["ReminderTime"]</FieldLabel>
<DateEdit TValue="DateTimeOffset" @bind-Date="NewEntity.ReminderTime" InputMode="DateInputMode.DateTime"/>
</Field>
<Field>
<FieldLabel>@L["FollowUpTime"]</FieldLabel>
<DateEdit TValue="DateTimeOffset?" @bind-Date="NewEntity.FollowUpTime" InputMode="DateInputMode.DateTime"/>
</Field>
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["Description"]</FieldLabel>
<TextEdit @bind-Text="@NewEntity.Description">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
</Validations>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseCreateModalAsync">@L["Cancel"]</Button>
<Button Color="Color.Primary"
Type="@ButtonType.Submit"
PreventDefaultOnSubmit="true"
Clicked="CreateEntityAsync">@L["Save"]</Button>
</ModalFooter>
</Form>
</ModalContent>
</Modal>
<Modal @ref="@EditModal">
<ModalContent IsCentered="true">
<Form>
<ModalHeader>
<ModalTitle>@EditingEntity.Subject</ModalTitle>
<CloseButton Clicked="CloseEditModalAsync"/>
</ModalHeader>
<ModalBody>
<Validations @ref="@EditValidationsRef" Model="@EditingEntity" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["Subject"]</FieldLabel>
<TextEdit @bind-Text="@EditingEntity.Subject">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Field>
<FieldLabel>@L["StartTime"] / @L["EndTime"]</FieldLabel>
<DatePicker TValue="DateTime?" @bind-Dates="SelectedDates" InputMode="DateInputMode.DateTime" SelectionMode="DateInputSelectionMode.Range" />
</Field>
<Field>
<FieldLabel>@L["ActualStartTime"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="EditingEntity.ActualStartTime" InputMode="DateInputMode.DateTime"/>
</Field>
<Field>
<FieldLabel>@L["CanceledTime"]</FieldLabel>
<DateEdit TValue="DateTime?" @bind-Date="EditingEntity.CanceledTime" InputMode="DateInputMode.DateTime"/>
</Field>
<Field>
<FieldLabel>@L["ReminderTime"]</FieldLabel>
<DateEdit TValue="DateTimeOffset" @bind-Date="EditingEntity.ReminderTime" InputMode="DateInputMode.DateTime"/>
</Field>
<Field>
<FieldLabel>@L["FollowUpTime"]</FieldLabel>
<DateEdit TValue="DateTimeOffset?" @bind-Date="EditingEntity.FollowUpTime" InputMode="DateInputMode.DateTime"/>
</Field>
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["Description"]</FieldLabel>
<TextEdit @bind-Text="@EditingEntity.Description">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
</Validations>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="CloseEditModalAsync">@L["Cancel"]</Button>
<Button Color="Color.Primary"
Type="@ButtonType.Submit"
PreventDefaultOnSubmit="true"
Clicked="UpdateEntityAsync">@L["Save"]</Button>
</ModalFooter>
</Form>
</ModalContent>
</Modal>
@code {
IReadOnlyList<DateTime?> SelectedDates;
public Meeting()
{
CreatePolicyName = TimeZoneAppPermissions.Meetings.Create;
UpdatePolicyName = TimeZoneAppPermissions.Meetings.Edit;
DeletePolicyName = TimeZoneAppPermissions.Meetings.Delete;
}
protected override async Task OpenCreateModalAsync()
{
await base.OpenCreateModalAsync();
var now = DateTime.Now;
SelectedDates = new List<DateTime?> { now.Date.AddHours(10),now.Date.AddDays(7).AddHours(10) };
NewEntity.ActualStartTime = now.Date.AddHours(11);
NewEntity.CanceledTime = now.Date.AddHours(12);
NewEntity.ReminderTime = now.Date.AddHours(13);
NewEntity.FollowUpTime = now.Date.AddHours(14);
}
protected override Task OnCreatingEntityAsync()
{
if (SelectedDates.Count == 2 && SelectedDates[0].HasValue && SelectedDates[1].HasValue)
{
NewEntity.StartTime = Clock.ConvertToUtc(SelectedDates[0]!.Value);
NewEntity.EndTime = Clock.ConvertToUtc(SelectedDates[1]!.Value);
}
NewEntity.ActualStartTime = Clock.ConvertToUtc(NewEntity.ActualStartTime);
NewEntity.CanceledTime = NewEntity.CanceledTime.HasValue ? Clock.ConvertToUtc(NewEntity.CanceledTime.Value) : null;
NewEntity.ReminderTime = Clock.ConvertToUtc(NewEntity.ReminderTime.DateTime);
NewEntity.FollowUpTime = NewEntity.FollowUpTime.HasValue ? Clock.ConvertToUtc(NewEntity.FollowUpTime.Value.DateTime) : null;
return Task.CompletedTask;
}
protected override async Task OpenEditModalAsync(MeetingDto entity)
{
await base.OpenEditModalAsync(entity);
SelectedDates = new List<DateTime?> { Clock.ConvertToUserTime(EditingEntity.StartTime), Clock.ConvertToUserTime(EditingEntity.EndTime) };
EditingEntity.ActualStartTime = Clock.ConvertToUserTime(EditingEntity.ActualStartTime);
EditingEntity.CanceledTime = EditingEntity.CanceledTime.HasValue ? Clock.ConvertToUserTime(EditingEntity.CanceledTime.Value) : null;
EditingEntity.ReminderTime = Clock.ConvertToUserTime(EditingEntity.ReminderTime);
EditingEntity.FollowUpTime = EditingEntity.FollowUpTime.HasValue ? Clock.ConvertToUserTime(EditingEntity.FollowUpTime.Value) : null;
}
protected override Task OnUpdatingEntityAsync()
{
if (SelectedDates.Count == 2 && SelectedDates[0].HasValue && SelectedDates[1].HasValue)
{
EditingEntity.StartTime = Clock.ConvertToUtc(SelectedDates[0]!.Value);
EditingEntity.EndTime = Clock.ConvertToUtc(SelectedDates[1]!.Value);
}
EditingEntity.ActualStartTime = Clock.ConvertToUtc(EditingEntity.ActualStartTime);
EditingEntity.CanceledTime = EditingEntity.CanceledTime.HasValue ? Clock.ConvertToUtc(EditingEntity.CanceledTime.Value) : null;
return Task.CompletedTask;
}
}
```
![](blazor-list.png)
![](blazor-create.png)
![](blazor-edit.png)
## Timezone Settings Change
If the timezone settings change, then all times will be converted to the new timezone. For example, if the current timezone changes from `Europe/Istanbul` to `Europe/Berlin`, then all times will be converted to the `Europe/Berlin` timezone.
![](berlin.png)
`Europe/Istanbul`:
![](auth-list-utc3.png)
![](mvc-list-utc3.png)
`Europe/Berlin`:
![](auth-list-utc1.png)
![](mvc-list-utc1.png)
## Browser Timezone Detection
When no timezone setting is configured, ABP's MVC, Blazor, and Angular applications will automatically detect the browser's timezone during initialization. The detected timezone is then stored in either the request's Cookie or Header.
This functionality is implemented by the `UseAbpTimeZone` middleware, which follows a specific order to determine the appropriate timezone:
1. First, it attempts to retrieve the timezone from the application/tenant/user settings
2. If no setting is found, it tries to get the timezone from the request information, including Cookie, Header, QueryString, and Form
3. Finally, if no timezone information is found, it falls back to using the server's timezone as the default
> The timezone information is stored using the key `__timezone`
## TimeZoneApp Source Code
You can download and view the [TimeZoneApp source code](https://github.com/maliming/TimeZone) for detailed implementation.
## Summary
Through this article, we learned how to handle timezone in different types of UIs. I hope this article is helpful to you. If you have any questions, please contact me at any time.

8
docs/en/Community-Articles/2025-03-18-Using-Vue-Components/post.md

@ -4,6 +4,10 @@ In modern web development, integrating dynamic front-end frameworks with server-
This article won't use any SPA approach. The goal of this article is to use Razor Pages with simple Vue components to eliminate jQuery while developing MVC application.
> **🎉 Also video version is available!**
>
> [Watch on YouTube Now!](https://youtu.be/sZ8iSMovHZs?si=GynuJjsLEI1p2g6w)
## Creating the Solution
Let's create a simple TODO list application to demonstrate how to use Vue components in Razor Pages. I'll build a really simple backend without a connection to a database for demonstration purposes. We will focus on the frontend part.
@ -71,13 +75,13 @@ Let's create a simple Vue component to display the TODO list.
```html
<div id="vue-app">
<todo-component :message="'Welcome, @CurrentUser.UserName !'"></todo-component>
<message-component :message="'Welcome, @CurrentUser.UserName !'"></message-component>
</div>
```
- Navigate to the `Index.cshtml.js` file and add the following code:
```js
Vue.component('todo-component', {
Vue.component('message-component', {
template: '<div>Hello, {{ message }}</div>',
props: ['message']
});

BIN
docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

BIN
docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

BIN
docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

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

@ -0,0 +1,248 @@
# Understanding the Embedded Files in ABP Framework
Embedded Files functionality in .NET applications allows external files (like configuration files, images, etc.) to be directly embedded into assemblies (.exe or .dll). This simplifies deployment, prevents file loss or tampering, improves security and performance, and reduces path and dependency management issues. Through embedded resources, programs can access these files more conveniently without additional file operations.
## Embedding Files in Your Project
We embed `Volo\Abp\MyModule\Localization\*.json` files into the assembly in our `MyModule.csproj`.
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Volo.Abp.VirtualFileSystem" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<None Remove="Volo\Abp\MyModule\Localization\*.json" />
<EmbeddedResource Include="Volo\Abp\MyModule\Localization\*.json" />
</ItemGroup>
</Project>
```
If we check the `en.json` file in our IDE, we'll see it's embedded in the assembly.
![image](1.png)
When we decompile the built `MyModule.dll` file, we can also see the `en.json` file.
![image](2.png)
## Accessing Embedded Files in Code
```csharp
public class Program
{
public static async Task<int> Main(string[] args)
{
var embeddedFiles = typeof(Program).Assembly.GetManifestResourceNames();
foreach (var embeddedFile in embeddedFiles)
{
Console.WriteLine(embeddedFile);
var fileStream = typeof(Program).Assembly.GetManifestResourceStream(embeddedFile);
if (fileStream != null)
{
using var reader = new System.IO.StreamReader(fileStream);
var content = await reader.ReadToEndAsync();
Console.WriteLine(content);
}
}
}
}
```
This code will output the embedded file names and their contents.
```
MyModule.Volo.Abp.MyModule.Localization.en.json
{
"key":"value"
}
```
## Integrating with ABP Virtual File System
The ABP Virtual File System makes it possible to manage files that don't physically exist on the file system (disk). It's mainly used to embed (js, css, image..) files into assemblies and use them like physical files at runtime.
The following code shows how to add embedded files from the current application assembly to the ABP virtual file system:
```csharp
[DependsOn(typeof(AbpVirtualFileSystemModule))]
public class MyModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<MyModule>();
});
}
}
```
ABP creates an `AbpEmbeddedFileProvider` to access the embedded files.
The full name of `en.json` is `MyModule.Volo.Abp.MyModule.Localization.en.json`. Without directory information, ABP uses `.` to split and assume directory information. This creates the following directory structure in the virtual file system:
```
[Dir] [/MyModule]
[Dir] [/MyModule/Volo]
[Dir] [/MyModule/Volo/Abp]
[Dir] [/MyModule/Volo/Abp/MyModule]
[Dir] [/MyModule/Volo/Abp/MyModule/Localization]
[File] [/MyModule/Volo/Abp/MyModule/Localization/en.json]
```
Now you can inject `IVirtualFileProvider` to access embedded files using the directory/file structure above.
## Manifest Embedded File Provider
You might have noticed that using `.` to split and assume directory information can cause confusion if filenames contain dots.
For example, if your filename is `zh.hans.json`, ABP will generate the following directory structure, which isn't what we want:
```
[Dir] [/MyModule]
[Dir] [/MyModule/Volo]
[Dir] [/MyModule/Volo/Abp]
[Dir] [/MyModule/Volo/Abp/MyModule]
[Dir] [/MyModule/Volo/Abp/MyModule/Localization]
[Dir] [/MyModule/Volo/Abp/MyModule/Localization/zh]
[File] [/MyModule/Volo/Abp/MyModule/Localization/zh/hans.json]
```
Microsoft provides the `Microsoft.Extensions.FileProviders.Manifest` library to solve this problem.
We need to add this package dependency and set `<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>` in our project:
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<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" />
</ItemGroup>
<ItemGroup>
<None Remove="Volo\Abp\MyModule\Localization\*.json" />
<EmbeddedResource Include="Volo\Abp\MyModule\Localization\*.json" />
</ItemGroup>
</Project>
```
After rebuilding the project, when we decompile `MyModule.dll`, we'll see an additional `Microsoft.Extensions.FileProviders.Embedded.Manifest.xml` file.
![image](3.png)
This manifest file stores all the directory and file information of embedded resources. When ABP finds this file, it will use `ManifestEmbeddedFileProvider` instead of `AbpEmbeddedFileProvider` to access embedded files:
```xml
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Manifest>
<ManifestVersion>1.0</ManifestVersion>
<FileSystem>
<File Name="Microsoft.Extensions.FileProviders.Embedded.Manifest.xml">
<ResourcePath>Microsoft.Extensions.FileProviders.Embedded.Manifest.xml</ResourcePath>
</File>
<Directory Name="Volo">
<Directory Name="Abp">
<Directory Name="MyModule">
<Directory Name="Localization">
<File Name="zh.hans.json">
<ResourcePath>MyModule.Volo.Abp.MyModule.Localization.zh.hans.json</ResourcePath>
</File>
</Directory>
</Directory>
</Directory>
</Directory>
</FileSystem>
</Manifest>
```
## Parameters of AddEmbedded Method
The `AddEmbedded` method can take two parameters:
### baseNamespace
This may only be needed if you haven't used the `Manifest Embedded File Provider` and your project's `root namespace` isn't empty. In this case, set your root namespace here.
The `root namespace` is your project's name by default. You can change it or set it to empty in the `csproj` file.
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>MyModule</RootNamespace>
</PropertyGroup>
</Project>
```
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace></RootNamespace>
</PropertyGroup>
</Project>
```
```csharp
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<MyModule>(baseNamespace: "MyModule");
});
```
```
[Dir] [/Volo]
[Dir] [/Volo/Abp]
[Dir] [/Volo/Abp/MyModule]
[Dir] [/Volo/Abp/MyModule/Localization]
[File] [/Volo/Abp/MyModule/Localization/en.json]
```
### baseFolder
If you don't want to expose all embedded files in the project, but only want to expose a specific folder (and sub folders/files), you can set the base folder relative to your project root folder.
> baseFolder is only effective when using `Manifest Embedded File Provider`.
You can set the `baseFolder` parameter to `/Volo/Abp/MyModule`, resulting in this directory structure:
```csharp
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<MyModule>(baseFolder: "/Volo/Abp/MyModule");
});
```
```
[Dir] [Localization]
[File] [Localization/en.json]
```
## Summary
We recommend using the `Manifest Embedded File Provider` in your projects and libraries. Hope this article has been helpful.
## References
[ABP Virtual File System](https://abp.io/docs/latest/framework/infrastructure/virtual-file-system)
[Manifest Embedded File Provider](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/file-providers#manifest-embedded-file-provider)

115
docs/en/Community-Articles/2025-03-24-How-to-Change-the-CurrentUser-in-ABP/POST.md

@ -0,0 +1,115 @@
# How to Change the CurrentUser in ABP?
[ABP Framework](https://abp.io/) provides a powerful service for accessing information about the currently authenticated user in your application. Understanding how to use and modify this service (`ICurrentUser`) is essential for both basic and certain advanced scenarios.
In this article, we'll explore the [`CurrentUser` service](https://abp.io/docs/latest/framework/infrastructure/current-user), its use cases, and how to change it when necessary.
## Understanding the ICurrentUser Service
The `ICurrentUser` interface is the primary service in ABP Framework for obtaining information about the logged-in user. It provides some key properties, such as `Id`, `UserName`, `TenantId`, `Roles` (roleNames), and more...
`ICurrentUser` is implemented on the `ICurrentPrincipalAccessor` service and works with claims as well. So, all of these properties are actually retrieved from the claims. ICurrentUser has some methods to directly work with the claims, such as:
* FindClaim (finds a single claim by name)
* FindClaims (gets all claims with the given name)
* IsInRole (checks if the user has a specific role)
* GetAllClaims (gets all claims of the user)
## Where the CurrentUser Service is Used?
The CurrentUser service is used extensively throughout ABP applications whenever there's a need to access information about the logged-in user. Common scenarios include: authorization checks, logging, setting common properties like `CreatorId`, `LastModifierId`, `DeleterId`, and more...
## When to Change the CurrentUser Service?
While the CurrentUser service works automatically in the context of HTTP requests (it gets the `User` property of the current `HttpContext`), there are advanced scenarios where you might need to manually set or change the current user:
1. **Background workers:** When executing code outside the context of a user request
2. **Event handlers:** When processing events that may run in a different context
3. **Unit & integration tests:** When simulating a user for testing purposes
## How to Change the CurrentUser Service?
If you need to change the CurrentUser service, you can inject the `ICurrentPrincipalAccessor` service, use its `Change` method to change the current user, and then use the `CurrentUser` service as usual.
Here's how to change the current user for a specific scope:
```csharp
using System.Security.Claims;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Identity;
using Volo.Abp.Security.Claims;
namespace MyProject.Products;
public class ProductEventHandler : IDistributedEventHandler<OrderPlacedEto>, ITransientDependency
{
private readonly IProductRepository _productRepository;
private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
private readonly IdentityUserManager _userManager;
public ProductEventHandler(
IProductRepository productRepository,
ICurrentPrincipalAccessor currentPrincipalAccessor,
IdentityUserManager userManager
)
{
_productRepository = productRepository;
_currentPrincipalAccessor = currentPrincipalAccessor;
_userManager = userManager;
}
public async Task HandleEventAsync(OrderPlacedEto eventData)
{
var product = await _productRepository.FindAsync(eventData.ProductId);
if (product == null)
{
return;
}
//Get the admin user
var adminUser = await _userManager.FindByNameAsync("admin");
if (adminUser == null)
{
return;
}
var newPrincipal = new ClaimsPrincipal(new ClaimsIdentity(
new Claim[] {
new Claim(AbpClaimTypes.UserId, adminUser.Id.ToString()),
new Claim(AbpClaimTypes.UserName, "admin"),
}));
//IMPORTANT: It will set the CreatorId, LastModifierId, etc. with the admin user
using (_currentPrincipalAccessor.Change(newPrincipal))
{
product.StockCount -= eventData.Quantity;
// Update the product
await _productRepository.UpdateAsync(product);
}
}
}
```
In this example, we have a distributed event handler that processes an `OrderPlacedEto` event. When an order is placed, we need to update the product's stock count. However, we want this operation to be performed under an admin user's context for auditing purposes.
Here's what the code does step by step:
1. First, it retrieves the product using the product ID from the event data.
2. Then, it finds the admin user by username using the `_userManager.FindByNameAsync("admin")`.
3. A new `ClaimsPrincipal` is created with the admin user's claims (`UserId` and `UserName`).
4. Using the `_currentPrincipalAccessor.Change()` method within a `using` statement, it temporarily changes the current user context to the admin user.
5. Inside this scope, it updates the product's stock count by subtracting the ordered quantity.
6. Finally, it saves the changes to the database using the repository.
**The important part here is that any audit properties (like `CreatorId`, `LastModifierId`, etc.) will be set to the admin user's ID because we changed the current principal. Once the using block ends, the original user context is automatically restored.**
This pattern is particularly useful in background jobs, event handlers, or any scenario where you need to perform operations under a specific user's context, regardless of the actual authenticated user.
## Conclusion
The `CurrentUser` service in ABP Framework provides a simple way to access information about the authenticated user. While it works automatically in most scenarios, there are cases where you need to explicitly change the current user identity, particularly in background processing scenarios.
By using the ICurrentPrincipalAccessor.Change() method within a using statement, you can temporarily change the current user for a specific scope of execution, enabling your background processes, event handlers, or tests to execute with the identity of a specific user.

38
docs/en/Community-Articles/2025-03-27-Announcing-the-ABP-Contributor-Program/post.md

@ -0,0 +1,38 @@
We are excited to introduce the ABP Contributor Program, which is an amazing opportunity for community enthusiasts to contribute to the ABP ecosystem while getting valuable benefits\!
**Create, Contribute and Be Part of Something Bigger**
This is your opportunity to not only share your knowledge but also to grow alongside a passionate community of developers and creators. When you create and contribute to the ABP ecosystem, you’re not just building content, you’re also helping shape the future of ABP.
## **Why Become an ABP Contributor?**
By sharing your knowledge, creating valuable content, and engaging with the ABP Community, you’ll enjoy a range of benefits, including:
* Free ABP Personal License – If you meet all the requirements, you’ll receive an ABP Personal License at no cost
* Community Badge & Title – Stand out in the ABP Community with a special contributor badge and label next to your name.
* Exclusive Discord Role – Get a unique Contributor role on the ABP Community Discord server.
* Increased Visibility – Your contributions will be recognized by hundreds of thousands of developers worldwide.
Completely Free – No fees, just log in, contribute, and start collecting benefits\!
## **How to Apply?**
If you’re passionate about ABP and think you meet the required criteria, follow these simple steps:
Create content related to ABP: blog posts, tutorials, videos, or documentation. You can publish it on the ABP Community website or any external platform (just make sure to submit it to the ABP Community site).
Apply by email: Send your application to marketing@volosoft.com, and the ABP Team will review your eligibility.
Periodic Assessments: The team reviews submissions every three months to determine and renew contributor status. Keep contributing to continue enjoying the perks\!
## **Join Us & Grow\!**
This is your chance to give back to the community, grow your presence, and get rewarded for your contributions. Whether you love writing documentation, sharing tutorials, or building open-source projects, your efforts matter.
Apply today and be part of something impactful\!
[image1]: images/img_1.png

170
docs/en/Community-Articles/2025-04-20-IDX10204/POST.md

@ -0,0 +1,170 @@
# Common Errors in JWT Bearer Authentication
When implementing JWT Bearer authentication in an ABP(tiered) application, you might occasionally encounter errors starting with `IDX`. These errors are related to JWT Bearer Token validation and this article will help you understand and resolve them.
## Enable JWT Bearer authentication
Your API project usually contains the following code, which enables JWT Bearer authentication and makes it as the default authentication scheme.
We simply configure the JWT's `Authority` and `Audience` properties, and it will work fine.
```csharp
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://localhost:44301/"; //configuration["AuthServer:Authority"];
options.Audience = "MyProjectName";
});
```
> `AddJwtBearer` and `AddAbpJwtBearer` will do the same thing, but `AddAbpJwtBearer` is recommended.
## JWT authentication process
Let's take a look at how the above code works.
A JWT Token usually consists of three parts: `Header`, `Payload`, and `Signature`.
- `Header`: Contains the type and signing algorithm of the token
- `Payload`: Contains the claims of the token, including `sub`, `aud`, `exp`, `iat`, `iss`, `jti`, `preferred_username`, `given_name`, `role`, `email`, etc.
- `Signature`: The cryptographic signature of the token used to verify its authenticity
Here is an example of a JWT Token issued by `AuthServer(OpenIddict)`:
The `Header` part:
![JWT Header](header.png)
The `Payload` part:
![JWT Payload](payload.png)
### TokenValidationParameters
In the `JwtBearerOptions`, there is a `TokenValidationParameters` property, which is used to validate the JWT Token.
The default implementation for JWT Token validation is `JsonWebTokenHandler`, which comes from the [Microsoft.IdentityModel.JsonWebTokens](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/) package.
We didn't set the `TokenValidationParameters` property in the code above, so the default values below will be used:
```csharp
//...
TokenValidationParameters.ValidateAudience = true
TokenValidationParameters.ValidAudience = "MyProjectName"
TokenValidationParameters.ValidAudiences = null
TokenValidationParameters.ValidateIssuer = true
TokenValidationParameters.ValidIssuer = null
TokenValidationParameters.ValidIssuers = null
//...
```
### JWT Bearer Token Validation Process
During JWT Bearer authentication, API website will get the token from the HTTP request and validate it.
The `JsonWebTokenHandler` will get the `OpenID Connect` metadata from the `AuthServer`, it will be used in the validation process, the current metadata request address is: https://localhost:44301/.well-known/openid-configuration , it is a fixed address calculated from the `Authority` property.
First, the token's Signature is verified using the public key obtained from `OpenID Connect` metadata(https://localhost:44301/.well-known/jwks).
Then, the payload is validated. The payload is a JSON object containing essential information such as the `token type`, `expiration time`, `issuer`, and `audience` etc.
Most of the validation problems we may encounter are payload validation failures, for example:
#### Lifetime
If the token in your request has expired, the validation will fail. You will see the exception information like `IDX10230` in the log.
#### Audience
The `ValidAudience` of `TokenValidationParameters` is `MyProjectName`, the `aud` in the payload of the token is also `MyProjectName`, if the token does not contain `aud` or the `aud` does not match, the validation will fail. You may see the exception information like `IDX10206`, `IDX10277` or `IDX10208`.
> If the `ValidateAudience` of `TokenValidationParameters` is `false`, then the `aud` will not be validated.
#### Issuer
The default value of `TokenValidationParameters.ValidateIssuer` is `true`, it requires the token's payload to contain the `issuer` field, and it must match one of `TokenValidationParameters.ValidIssuer` or `TokenValidationParameters.ValidIssuers`.
> The default value of `ValidIssuer` or `ValidIssuers` is `null`, it will use the `issuer` from the `OpenID Connect` metadata as the default value.
1. If the token's payload does not contain the `issuer` field, you may see the error `IDX10211`.
2. If the API website cannot get the `OpenID Connect` metadata from AuthServer website, the validation will fail. You may see the error `IDX10204`, the full exception message is: `IDX10204: Unable to validate issuer. validationParameters.ValidIssuer is null or whitespace AND validationParameters.ValidIssuers is null or empty.`
3. If the `issuer` does not match, the validation will fail. You may see the error `IDX10205` in the log.
> If the `ValidateIssuer` of `TokenValidationParameters` is `false`, then the `issuer` will not be validated.
> Please note that `OpenIddict` will use the current HTTP request information as the value of `issuer`. If the AuthServer website is deployed behind a reverse proxy or similar deployment configurations, the `issuer` in the token may not be the value you expect. In this case, please specify it manually.
```csharp
PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
{
serverBuilder.SetIssuer("https://localhost:44301/");
});
```
## Troubleshooting
To troubleshoot any `IDX` errors during JWT authentication, you can enable detailed logging by configuring the `identitymodel` logs as follows:
```csharp
using System.Diagnostics.Tracing;
using Microsoft.IdentityModel.Logging;
public class Program
{
public async static Task<int> Main(string[] args)
{
IdentityModelEventSource.ShowPII = true;
IdentityModelEventSource.Logger.LogLevel = EventLevel.Verbose;
var wilsonTextLogger = newTextWriterEventListener("Logs/identitymodel.txt");
wilsonTextLogger.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Verbose);
//...
}
}
```
Additionally, you can enable `OpenIddict`'s `Verbose` logs for more detailed debugging information:
```csharp
var loggerConfiguration = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.MinimumLevel.Override("OpenIddict", LogEventLevel.Verbose)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
```
## Summary
For JWT authentication, you need to pay attention to the following key points:
1. Ensure your API website can communicate with the AuthServer properly
2. Verify that the `aud` claim in your token matches the expected audience
3. Confirm that the `issuer` claim in your token is valid and matches the configuration
You can customize the `JwtBearerOptions`'s `TokenValidationParameters` to modify the validation rules to meet your actual needs.
For example, if your `issuer` needs to support multiple subdomains, you can use the [Owl.TokenWildcardIssuerValidator](https://github.com/maliming/Owl.TokenWildcardIssuerValidator) library to customize the validation.
```csharp
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://abp.io";
options.Audience = "abp_io";
options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator;
options.TokenValidationParameters.ValidIssuers = new[]
{
"https://{0}.abp.io"
};
});
```
## References
- [Configure JWT bearer authentication in ASP.NET Core]([https://learn.microsoft.com/en-us/aspnet/core/security/authentication/jwt-auth?view=aspnetcore-8.0](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/configure-jwt-bearer-authentication))
- [OpenIddict](https://github.com/openiddict/openiddict-core)
- [IdentityModel](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet)
- [Owl.TokenWildcardIssuerValidator](https://github.com/maliming/Owl.TokenWildcardIssuerValidator)

BIN
docs/en/Community-Articles/2025-04-20-IDX10204/header.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
docs/en/Community-Articles/2025-04-20-IDX10204/payload.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

262
docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/POST.md

@ -0,0 +1,262 @@
# Using Microsoft AI Extensions Library and OpenAI to Summarize User Comments
Either you are building an e-commerce application or a simple blog, **user comments** (about your products or blog posts) **can grow rapidly**, making it harder for users to get the gist of discussions at a glance. AI is a pretty good tool to solve the problem. By using AI, you can **summarize all the user comments** and show a single paragraph to your users, so they can easily understand the overall thought of users about the product or the blog post.
In this tutorial, we’ll walk through a real-life implementation of using AI to summarize multiple user comments in an application. I will implement the solution based on ABP's **[CMS Kit](https://abp.io/docs/latest/modules/cms-kit)** library, as it already features a **[commenting system](https://abp.io/docs/latest/modules/cms-kit/comments)** and a [demo application](https://cms-kit-demo.abpdemo.com/) that displays user comments on **[gallery images](https://cms-kit-demo.abpdemo.com/image-gallery)** (it has not a comment summary feature yet, we will implement it in this tutorial).
## A Screenshot
Here, an example screenshot from the application with the comment summary feature:
![comment-example](comment-example.png)
## Cloning the Repository
If you want to follow the development, you can clone the [CMS Kit Demo repository](https://github.com/abpframework/cms-kit-demo) to your computer and make it running by following the instructions on the [README file](https://github.com/abpframework/cms-kit-demo?tab=readme-ov-file#cms-kit-demo).
I suggest to you to play a little with [the application](https://cms-kit-demo.abpdemo.com/) (create a new user for yourself, add some comments to the images in the gallery), so you understand how it works.
## Preparing the Solution for AI
I will use [Microsoft AI Extensions Library](https://learn.microsoft.com/en-us/dotnet/ai/ai-extensions) to use the AI features. It is an abstraction library that can work with multiple AI models and tools. I will use an OpenAI model in the demo.
The first step is to add the [Microsoft.Extensions.AI.OpenAI](http://nuget.org/packages/Microsoft.Extensions.AI.OpenAI) NuGet package to the project:
````bash
dotnet add package Microsoft.Extensions.AI.OpenAI --prerelease
````
>The Microsoft AI Extensions Library was in preview at the time when I wrote this article. If it has a stable release now, you can remove the `--prerelease` parameter for the preceding command.
We will store the OpenAI key and model name in user secrets. So, locate the root path of the CMS Kit project (`src\CmsKitDemo` folder) and execute the following commands in order in a command-line terminal:
````bash
dotnet user-secrets init
dotnet user-secrets set OpenAIKey <your-openai-key>
dotnet user-secrets set ModelName <your-openai-model-name>
````
For this example, you need to have an [OpenAI API Key](https://platform.openai.com/). That's all. Now, we are ready to use the AI.
## Implementing the AI Summarization
Let's start from the most important point of this article: Comment summarization. I will create a class named `AiCommentSummarizer` to implement the summarization work. Here, the full content of that class:
````csharp
using System.Text;
using Microsoft.Extensions.AI;
using OpenAI;
using Volo.Abp.DependencyInjection;
namespace CmsKitDemo.Utils;
public class AiCommentSummarizer : ITransientDependency
{
private readonly IConfiguration _configuration;
public AiCommentSummarizer(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task<string> SummarizeAsync(string[] commentTexts)
{
// Get the model and key from the configuration
var aiModel = _configuration["ModelName"];
var apiKey = _configuration["OpenAIKey"];
if (aiModel.IsNullOrEmpty() || apiKey.IsNullOrEmpty())
{
return "";
}
// Create the IChatClient
var client = new OpenAIClient(apiKey)
.GetChatClient(aiModel)
.AsIChatClient();
// Create a prompt (input for AI)
var promptBuilder = new StringBuilder();
promptBuilder.AppendLine(
@"There are comments from different users of our website about an image.
We want to summarize the comments into a single comment.
Return a single comment with a maximum of 512 characters. Comments are separated by a newline character and given below."
);
promptBuilder.AppendLine();
foreach (var commentText in commentTexts)
{
promptBuilder.AppendLine("User comment:");
promptBuilder.AppendLine(commentText);
promptBuilder.AppendLine();
}
// Submit the prompt and get the response
var response = await client.GetResponseAsync(
promptBuilder.ToString(),
new ChatOptions { MaxOutputTokens = 1024 }
);
return response.Text;
}
}
````
That class is pretty simple and already decorated with comments:
* First, we are getting the API Key and an OpenAI model name from user secrets. I used `gpt-4.1` as the model name, but you can use another available model.
* Then we are obtaining an `IChatClient` reference for OpenAI. `IChatClient` interface is an abstraction that is provided by the [Microsoft AI Extensions Library](https://learn.microsoft.com/en-us/dotnet/ai/ai-extensions) library, so we can implement rest of the code independently from OpenAI.
* Then we continue by building a proper prompt (input) for the AI operation.
* And finally we are using the AI to generate a response (the summary).
At this point, all the AI-related work has already been done. The rest of this article explains how to integrate that summarization feature with the [CMS Kit Demo application](https://cms-kit-demo.abpdemo.com/).
## Adding a CommentsSummary Property to the GalleryImage Entity
The `GalleryImage` entity is used to represent an image on [the image gallery](https://cms-kit-demo.abpdemo.com/image-gallery). I add a `CommentsSummary` property to that entity:
````csharp
public class GalleryImage : CreationAuditedAggregateRoot<Guid>
{
public string Description { get; set; }
public Guid CoverImageMediaId { get; set; }
public string CommentsSummary { get; set; } // The new property is here
//...
}
````
Since the CMS Kit Demo application uses Entity Framework Core, I need to add a new database schema migration and update the database:
````bash
dotnet ef migrations add Added_Summary_To_GalleryImage
dotnet ef database update
````
## Updating the Summary
Great, we have a `GalleryImage.CommentsSummary` property now. But, how will it be updated when a users adds or removes a comment for an image? To implement that;
* We will listen all the change events for user comments (when a user adds, removes or updates a comment).
* Whenever a comment is changed, we will find the related gallery image, retrieve all the user comments for this image, use the `AiCommentSummarizer` class to summarize all the comments.
* Finally, we wil set the `GalleryImage.CommentsSummary` property with the generated summary text.
Here, the implementation:
````csharp
using CmsKitDemo.Entities;
using CmsKitDemo.Utils;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus;
using Volo.CmsKit.Comments;
namespace CmsKitDemo.EventHandlers;
public class GalleryImageCommentListener :
ILocalEventHandler<EntityChangedEventData<Comment>>,
ITransientDependency
{
private readonly IRepository<GalleryImage, Guid> _galleryImageRepository;
private readonly IRepository<Comment, Guid> _commentRepository;
private readonly AiCommentSummarizer _aiCommentSummarizer;
public GalleryImageCommentListener(
IRepository<GalleryImage, Guid> galleryImageRepository,
IRepository<Comment, Guid> commentRepository,
AiCommentSummarizer aiCommentSummarizer)
{
_galleryImageRepository = galleryImageRepository;
_commentRepository = commentRepository;
_aiCommentSummarizer = aiCommentSummarizer;
}
public async Task HandleEventAsync(EntityChangedEventData<Comment> eventData)
{
var comment = eventData.Entity;
//Here, we only interest in comments related to image gallery items
if (comment.EntityType != CmsKitDemoConsts.ImageGalleryEntityType)
{
return;
}
if (!Guid.TryParse(comment.EntityId, out var galleryImageId))
{
return;
}
// Get the related image from database
var galleryImage = await _galleryImageRepository.FindAsync(galleryImageId);
if (galleryImage == null)
{
return;
}
// Get all the comments related to the image
var queryable = await _commentRepository.GetQueryableAsync();
var allCommentTexts = await queryable
.Where(c => c.EntityType == CmsKitDemoConsts.ImageGalleryEntityType &&
c.EntityId == comment.EntityId)
.Select(c => c.Text)
.ToArrayAsync();
// Update the summary of comments related to the image
if (allCommentTexts.Length <= 0)
{
galleryImage.CommentsSummary = "";
}
else
{
galleryImage.CommentsSummary =
await _aiCommentSummarizer.SummarizeAsync(allCommentTexts);
}
// Update the image in database
await _galleryImageRepository.UpdateAsync(galleryImage);
}
}
````
Let's explain that class:
* `GalleryImageCommentListener` implements the `ILocalEventHandler<EntityChangedEventData<Comment>>` interface. In this way, it can handle an event whenever a `Comment` [entity](https://abp.io/docs/latest/framework/architecture/domain-driven-design/entities) is changed (created, updated or deleted). We are using ABP's [local event bus](https://abp.io/docs/latest/framework/infrastructure/event-bus/local) and its [pre-defined events](https://abp.io/docs/latest/framework/infrastructure/event-bus/local#pre-built-events).
* `HandleEventAsync` is called by the ABP Framework whenever a new `Comment` is created, or an existing `Comment` is deleted or updated.
* ABP's `Comment` entity is reusable and it can be associated with any kind of objects (blog posts, images, etc). So, first we are checking if this comment is related to an image gallery item.
* Then we are getting the related `GalleryImage` entity from the database.
* And getting all comments (including the new one) from the database for this image.
* Finally, using the `AiCommentSummarizer` class to generate the summary and set the `CommentsSummary` property.
## Show the Summary Card on the UI
Everything is ready on the backend. Now, we can show the summary text on the user interface. To do, that, I added `CommentsSummary` property also to the `GalleryImageDto` class and used it on the `/Pages/Gallery/Detail.cshtml` view:
````csharp
@if (!Model.Image.CommentsSummary.IsNullOrEmpty())
{
<div class="card mt-3">
<div class="card-body">
<h6 class="card-title">Summary of the User Comments</h6>
<p class="mb-auto">@Model.Image.CommentsSummary</p>
</div>
</div>
}
````
That section renders the following card on the user interface:
![summary-card](summary-card.png)
## Conclusion
In this article, I demonstrated how to use [Microsoft AI Extensions Library](https://learn.microsoft.com/en-us/dotnet/ai/ai-extensions) to work with OpenAI for summarization of multiple user comments. I reused the [ABP's CMS Kit Demo application](https://github.com/abpframework/cms-kit-demo) to show it in a more real world example.
## Source Code
* [Source code of the CMS Kit Demo application](https://github.com/abpframework/cms-kit-demo)
* [All the changes made for this article (as a pull request)](https://github.com/abpframework/cms-kit-demo/pull/18)

BIN
docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/comment-example.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/summary-card.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

2
docs/en/cli/index.md

@ -342,6 +342,7 @@ Note that this command can upgrade your solution from a previous version, and al
* `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default.
* `--check-all`: Check the new version of each package separately. Default is `false`.
* `--version` or `-v`: Specifies the version to use for update. If not specified, latest version is used.
* * `--leptonx-version` or `-lv`: Specifies the LeptonX version to use for update. If not specified, latest version or the version that is compatible with `--version` argument is used.
### clean
@ -888,6 +889,7 @@ Some features of the CLI requires to be logged in to ABP Platform. The login com
```bash
abp login # Opens a default browser to log in to ABP Platform via abp.io
abp login --device # Use device login flow
abp login username -p ****** --password # Use user password login
```
A new login with an already active session overwrites the previous session.

4
docs/en/docs-nav.json

@ -613,6 +613,10 @@
"text": "Google Cloud Storage Provider",
"path": "framework/infrastructure/blob-storing/google.md"
},
{
"text": "Bunny.Net Provider",
"path": "framework/infrastructure/blob-storing/bunny.md"
},
{
"text": "Create a Custom Provider",
"path": "framework/infrastructure/blob-storing/custom-provider.md"

2
docs/en/framework/api-development/static-csharp-clients.md

@ -47,7 +47,7 @@ Implement this class in your service application. You can use [auto API controll
First, add [Volo.Abp.Http.Client](https://www.nuget.org/packages/Volo.Abp.Http.Client) nuget package to your client project:
````
Install-Package Volo.Abp.Http.Client
dotnet add package Volo.Abp.Http.Client
````
Then add `AbpHttpClientModule` dependency to your module:

2
docs/en/framework/api-development/swagger.md

@ -29,7 +29,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Swashbuckle](https://www.nuget.org/packages/Volo.Abp.Swashbuckle) NuGet package to your `Web` or `HttpApi.Host` project:
`Install-Package Volo.Abp.Swashbuckle`
`dotnet add package Volo.Abp.Swashbuckle`
2. Add the `AbpSwashbuckleModule` to the dependency list of your module:

6
docs/en/framework/architecture/microservices/index.md

@ -25,6 +25,8 @@ However, developing such a well-modular application can be a problem since it is
ABP can help you in that point by offering a **microservice-compatible, strict module architecture** where your module is split into multiple layers/projects and developed in its own VS solution completely isolated and independent from other modules. Such a developed module is a natural microservice yet it can be easily plugged-in a monolithic application. See the [module development best practice guide](../best-practices) that offers a **microservice-first module design**. All [standard ABP modules](https://github.com/abpframework/abp/tree/master/modules) are developed based on this guide. So, you can use these modules by embedding into your monolithic solution or deploy them separately and use via remote APIs. They can share a single database or can have their own database based on your simple configuration.
## Microservice Demo Solution: eShopOnAbp
## Microservice Solution Template
The [eShopOnAbp project](https://github.com/abpframework/eShopOnAbp) demonstrates a complete microservice solution based on the ABP.
ABP provides a pre-architected and production-ready microservice solution template that includes multiple services, API gateways and applications well integrated with each other. This template helps you quickly start building distributed systems with common microservice patterns.
See the [Microservice Solution Template](../../../solution-templates/microservice/index.md) documentation for details.

2
docs/en/framework/architecture/modularity/basics.md

@ -1,7 +1,5 @@
# Modularity
## Introduction
ABP was designed to support to build fully modular applications and systems where every module may have entities, services, database integration, APIs, UI components and so on;
* This document introduces the basics of the module system.

11
docs/en/framework/architecture/modularity/extending/module-entity-extensions.md

@ -151,6 +151,17 @@ property =>
> Tip: Use `DefaultValueFactory` option only if the default value may change over the time (like `DateTime.Now` in this example). If it is a constant value, then use the `DefaultValue` option.
### DataTypeAttribute
`DataTypeAttribute` is used to specify the type of the property. It is used to determine how to render the property on the user interface:
```csharp
property =>
{
property.Attributes.Add(new DataTypeAttribute(DataType.Date));
}
```
### Validation
Entity extension system allows you to define validation for extension properties in a few ways.

12
docs/en/framework/data/entity-framework-core/index.md

@ -755,7 +755,17 @@ public static class QADbContextModelCreatingExtensions
> The `Object Extension` feature need the `Change Tracking`, which means you can't use the read-only repositories for the entities that have `extension properties(MapEfCoreProperty)`, Please see the [Repositories documentation](../../architecture/domain-driven-design/repositories.md) to learn the change tracking behavior.
See the "*ConfigureByConvention Method*" section above for more information.
See the **ConfigureByConvention Method** section above for more information.
### Accessing Extra Properties(Shadow Properties)
Extra properties stored in separate fields in the database are known as **Shadow Properties**. These properties are not defined in the entity class, but are part of the EF Core model and can be referenced in LINQ queries using the EF.Property static method
```csharp
var query = (await GetQueryableAsync()).Where(x => EF.Property<string>(x, "Title") == "MyTitle");
```
See the [EF Core Shadow and Indexer Properties document](https://learn.microsoft.com/en-us/ef/core/modeling/shadow-properties) for more information.
## Advanced Topics

9
docs/en/framework/data/entity-framework-core/postgresql.md

@ -21,6 +21,15 @@ Find `UseSqlServer()` call in *YourProjectName*DbContextFactory.cs inside the `.
> Depending on your solution structure, you may find more `UseSqlServer()` calls that needs to be changed.
## EnableLegacyTimestampBehavior
Please enable the `Npgsql.EnableLegacyTimestampBehavior` in `PreConfigureServices` method of your EF Core module and `DbContextFactory(IDesignTimeDbContextFactory)` class.
`AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);`
See https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic
## Change the Connection Strings
PostgreSql connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com](https://www.connectionstrings.com/postgresql/) for details of PostgreSql connection string options.

2
docs/en/framework/fundamentals/caching.md

@ -270,7 +270,7 @@ Distributed cache service provides an interesting feature. Assume that you've up
### IDistributedCacheSerializer
`IDistributedCacheSerializer` service is used to serialize and deserialize the cache items. The default implementation is the `Utf8JsonDistributedCacheSerializer` class that uses `IJsonSerializer` service to convert objects to [JSON](../../json-serialization.md) and vice verse. Then it uses UTC8 encoding to convert the JSON string to a byte array which is accepted by the distributed cache.
`IDistributedCacheSerializer` service is used to serialize and deserialize the cache items. The default implementation is the `Utf8JsonDistributedCacheSerializer` class that uses `IJsonSerializer` service to convert objects to [JSON](../../json-serialization.md) and vice verse. Then it uses UTF8 encoding to convert the JSON string to a byte array which is accepted by the distributed cache.
You can [replace](./dependency-injection.md) this service with your own implementation if you want to implement your own serialization logic.

51
docs/en/framework/fundamentals/configuration.md

@ -1,3 +1,52 @@
# Configuration
ASP.NET Core has an flexible and extensible key-value based configuration system. In fact, the configuration system is a part of Microsoft.Extensions libraries and it is independent from ASP.NET Core. That means it can be used in any type of application. See [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) to learn the configuration infrastructure. ABP is 100% compatible with the configuration system.
ASP.NET Core has an flexible and extensible key-value based configuration system. The configuration system is a part of Microsoft.Extensions libraries and it is independent from ASP.NET Core. That means it can be used in any type of application.
See [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) to learn the configuration infrastructure. ABP is 100% compatible with the configuration system.
## Getting the Configuration
You may need to get the `IConfiguration` service in various places in your codebase. The following section shows two common ways.
### In Module Classes
You typically need to get configuration while initializing your application. You can get the `IConfiguration` service using the `ServiceConfigurationContext.Configuration` property inside your [module class](../architecture/modularity/basics.md) as the following example:
````csharp
public class MyAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var connectionString = context.Configuration["ConnectionStrings:Default"];
}
}
````
`context.Configuration` is a shortcut property for the `context.Services.GetConfiguration()` method. In general, prefer using `context.Configuration` for simplicity and readability when working within module classes. Use `context.Services.GetConfiguration()` in other contexts where you have an `IServiceCollection` object but do not have access to the `context.Configuration` property. (`IServiceCollection.GetConfiguration` is an extension method that can be used whenever you have an `IServiceCollection` object).
### In Your Services
You can directly [inject](dependency-injection.md) the `IConfiguration` service into your services:
````csharp
public class MyService : ITransientDependency
{
private readonly IConfiguration _configuration;
public MyService(IConfiguration configuration)
{
_configuration = configuration;
}
public string? GetConnectionString()
{
return _configuration["ConnectionStrings:Default"];
}
}
````
## See Also
* [Microsoft's Configuration Documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/)
* [The Options Pattern](options.md)

2
docs/en/framework/fundamentals/fluent-validation.md

@ -21,7 +21,7 @@ If you want to manually install;
1. Add the [Volo.Abp.FluentValidation](https://www.nuget.org/packages/Volo.Abp.FluentValidation) NuGet package to your project:
````
Install-Package Volo.Abp.FluentValidation
dotnet add package Volo.Abp.FluentValidation
````
2. Add the `AbpFluentValidationModule` to the dependency list of your module:

11
docs/en/framework/fundamentals/localization.md

@ -248,6 +248,17 @@ namespace MyProject
The `L` property is also available for some other base classes like `AbpController` and `AbpPageModel`.
## Supported Languages
You can configure the `AbpLocalizationOptions`'s `Languages` property to add the languages supported by the application. The template already sets common languages, but you can add new languages as shown below:
```csharp
Configure<AbpLocalizationOptions>(options =>
{
options.Languages.Add(new LanguageInfo("uz", "uz", "Uzbek"));
});
```
## The Client Side
See the following documents to learn how to reuse the same localization texts in the JavaScript side;

20
docs/en/framework/infrastructure/background-jobs/hangfire.md

@ -25,7 +25,7 @@ If you want to manually install;
1. Add the [Volo.Abp.BackgroundJobs.HangFire](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.HangFire) NuGet package to your project:
````
Install-Package Volo.Abp.BackgroundJobs.HangFire
dotnet add package Volo.Abp.BackgroundJobs.HangFire
````
2. Add the `AbpBackgroundJobsHangfireModule` to the dependency list of your module:
@ -84,6 +84,24 @@ After you have installed these NuGet packages, you need to configure your projec
}
````
### AbpHangfireOptions
You can configure the [BackgroundJobServerOptions](https://api.hangfire.io/html/T_Hangfire_BackgroundJobServerOptions.htm) of `AbpHangfireOptions` to customize the server.
````csharp
Configure<AbpHangfireOptions>(options =>
{
// If no ServerOptions is set, ABP will use the default BackgroundJobServerOptions instance.
options.ServerOptions = new BackgroundJobServerOptions
{
Queues = ["default", "alpha"],
//... other properties
};
});
````
> You don't need to call `AddHangfireServer` method, ABP will use AbpHangfireOptions's `ServerOptions` to create a server.
### Specifying Queue
You can use the [`QueueAttribute`](https://docs.hangfire.io/en/latest/background-processing/configuring-queues.html) to specify the queue:

2
docs/en/framework/infrastructure/background-jobs/quartz.md

@ -25,7 +25,7 @@ If you want to manually install;
1. Add the [Volo.Abp.BackgroundJobs.Quartz](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.Quartz) NuGet package to your project:
````
Install-Package Volo.Abp.BackgroundJobs.Quartz
dotnet add package Volo.Abp.BackgroundJobs.Quartz
````
2. Add the `AbpBackgroundJobsQuartzModule` to the dependency list of your module:

20
docs/en/framework/infrastructure/background-workers/hangfire.md

@ -23,7 +23,7 @@ If you want to manually install;
1. Add the [Volo.Abp.BackgroundWorkers.Hangfire](https://www.nuget.org/packages/Volo.Abp.BackgroundWorkers.Hangfire) NuGet package to your project:
````
Install-Package Volo.Abp.BackgroundWorkers.Hangfire
dotnet add package Volo.Abp.BackgroundWorkers.Hangfire
````
2. Add the `AbpBackgroundWorkersHangfireModule` to the dependency list of your module:
@ -84,6 +84,24 @@ After you have installed these NuGet packages, you need to configure your projec
}
````
### AbpHangfireOptions
You can configure the [BackgroundJobServerOptions](https://api.hangfire.io/html/T_Hangfire_BackgroundJobServerOptions.htm) of `AbpHangfireOptions` to customize the server.
````csharp
Configure<AbpHangfireOptions>(options =>
{
// If no ServerOptions is set, ABP will use the default BackgroundJobServerOptions instance.
options.ServerOptions = new BackgroundJobServerOptions
{
Queues = ["default", "alpha"],
//... other properties
};
});
````
> You don't need to call `AddHangfireServer` method, ABP will use AbpHangfireOptions's `ServerOptions` to create a server.
## Create a Background Worker
`HangfireBackgroundWorkerBase` is an easy way to create a background worker.

2
docs/en/framework/infrastructure/background-workers/quartz.md

@ -21,7 +21,7 @@ If you want to manually install;
1. Add the [Volo.Abp.BackgroundWorkers.Quartz](https://www.nuget.org/packages/Volo.Abp.BackgroundWorkers.Quartz) NuGet package to your project:
````
Install-Package Volo.Abp.BackgroundWorkers.Quartz
dotnet add package Volo.Abp.BackgroundWorkers.Quartz
````
2. Add the `AbpBackgroundWorkersQuartzModule` to the dependency list of your module:

2
docs/en/framework/infrastructure/current-user.md

@ -138,7 +138,7 @@ public class MyAppService : ApplicationService
{
new Claim(AbpClaimTypes.UserId, Guid.NewGuid().ToString()),
new Claim(AbpClaimTypes.UserName, "john"),
new Claim("MyCustomCliam", "42")
new Claim("Claim", "42")
}
)
);

6
docs/en/framework/infrastructure/event-bus/distributed/index.md

@ -297,10 +297,14 @@ Configure<AbpDistributedEntityEventOptions>(options =>
options.AutoEventSelectors.Add(
type => type.Namespace.StartsWith("MyProject.")
);
//Ignore for a single entity
options.IgnoredEventSelectors.Add<IgnoredProductEntity>();
});
````
* The last one provides flexibility to decide if the events should be published for the given entity type. Returns `true` to accept a `Type`.
* The `type.Namespace.StartsWith("MyProject.")` provides flexibility to decide if the events should be published for the given entity type. Returns `true` to accept a `Type`.
* The `IgnoredEventSelectors` is used to ignore the events for the specified entity types. It is useful if you enabled for all entities and want to ignore for some entities.
You can add more than one selector. If one of the selectors match for an entity type, then it is selected.

8
docs/en/framework/infrastructure/image-manipulation.md

@ -22,7 +22,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Imaging.Abstractions](https://www.nuget.org/packages/Volo.Abp.Imaging.Abstractions) NuGet package to your project:
```
Install-Package Volo.Abp.Imaging.Abstractions
dotnet add package Volo.Abp.Imaging.Abstractions
```
2. Add the `AbpImagingAbstractionsModule` to the dependency list of your module:
@ -240,7 +240,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Imaging.MagickNet](https://www.nuget.org/packages/Volo.Abp.Imaging.MagickNet) NuGet package to your project:
```
Install-Package Volo.Abp.Imaging.MagickNet
dotnet add package Volo.Abp.Imaging.MagickNet
```
2. Add `AbpImagingMagickNetModule` to your [module](../architecture/modularity/basics.md)'s dependency list:
@ -284,7 +284,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Imaging.ImageSharp](https://www.nuget.org/packages/Volo.Abp.Imaging.ImageSharp) NuGet package to your project:
```
Install-Package Volo.Abp.Imaging.ImageSharp
dotnet add package Volo.Abp.Imaging.ImageSharp
```
2. Add `AbpImagingImageSharpModule` to your [module](../architecture/modularity/basics.md)'s dependency list:
@ -350,7 +350,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Imaging.AspNetCore](https://www.nuget.org/packages/Volo.Abp.Imaging.AspNetCore) NuGet package to your project:
```
Install-Package Volo.Abp.Imaging.AspNetCore
dotnet add package Volo.Abp.Imaging.AspNetCore
```
2. Add `AbpImagingAspNetCoreModule` to your [module](../architecture/modularity/basics.md)'s dependency list:

2
docs/en/framework/infrastructure/settings.md

@ -24,6 +24,8 @@ public class EmailSettingProvider : SettingDefinitionProvider
}
````
> If you're developing a [DDD module](../architecture/domain-driven-design), you usually create this class in the `Domain` layer, of course, that's not mandatory.
ABP automatically discovers this class and registers the setting definitions.
### SettingDefinition

2
docs/en/framework/infrastructure/sms-sending.md

@ -26,7 +26,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Sms](https://www.nuget.org/packages/Volo.Abp.Sms) NuGet package to your project:
```
Install-Package Volo.Abp.Sms
dotnet add package Volo.Abp.Sms
```
2. Add the `AbpSmsModule` to the dependency list of your module:

8
docs/en/framework/infrastructure/string-encryption.md

@ -22,7 +22,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Security](https://www.nuget.org/packages/Volo.Abp.Security) NuGet package to your project:
`Install-Package Volo.Abp.Security`
`dotnet add package Volo.Abp.Security`
2. Add the `AbpSecurityModule` to the dependency list of your module:
@ -52,11 +52,11 @@ All encryption operations are included in `IStringEncryptionService`. You can in
public string Encrypt(string value)
{
// To enrcypt a value
// To encrypt a value
return StringEncryptionService.Encrypt(value);
}
public string Decrpyt(string value)
public string Decrypt(string value)
{
// To decrypt a value
return StringEncryptionService.Decrypt(value);
@ -122,4 +122,4 @@ Configure<AbpStringEncryptionOptions>(opts =>
- **Keysize:** This constant is used to determine the keysize of the encryption algorithm.
Default value: `256`
Default value: `256`

2
docs/en/framework/infrastructure/text-templating/razor.md

@ -25,7 +25,7 @@ If you want to manually install;
1. Add the [Volo.Abp.TextTemplating.Razor](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Razor) NuGet package to your project:
````
Install-Package Volo.Abp.TextTemplating.Razor
dotnet add package Volo.Abp.TextTemplating.Razor
````
2. Add the `AbpTextTemplatingRazorModule` to the dependency list of your module:

2
docs/en/framework/infrastructure/text-templating/scriban.md

@ -19,7 +19,7 @@ If you want to manually install;
1. Add the [Volo.Abp.TextTemplating.Scriban](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Scriban) NuGet package to your project:
````
Install-Package Volo.Abp.TextTemplating.Scriban
dotnet add package Volo.Abp.TextTemplating.Scriban
````
2. Add the `AbpTextTemplatingScribanModule` to the dependency list of your module:

100
docs/en/framework/infrastructure/timing.md

@ -8,11 +8,11 @@ ABP provides a basic infrastructure to make it easy and handle automatically whe
## IClock
`DateTime.Now` returns a `DateTime` object with the **local date & time of the server**. A `DateTime` object **doesn't store the time zone information**. So, you can not know the **absolute date & time** stored in this object. You can only make **assumptions**, like assuming that it was created in UTC+05 time zone. The things especially gets complicated when you save this value to a database and read later, or send it to a client in a **different time zone**.
`DateTime.Now` returns a `DateTime` object with the **local date & time of the server**. A `DateTime` object **doesn't store the time zone information**. So, you can not know the **absolute date & time** stored in this object. You can only make **assumptions**, like assuming that it was created in UTC+05 time zone. Things especially get complicated when you save this value to a database and read it later, or send it to a client in a **different time zone**.
One solution to this problem is always use `DateTime.UtcNow` and assume all `DateTime` objects as UTC time. In this way, you can convert it to the time zone of the target client when needed.
One solution to this problem is always using `DateTime.UtcNow` and assuming all `DateTime` objects as UTC time. In this way, you can convert it to the time zone of the target client when needed.
`IClock` provides an abstraction while getting the current time, so you can control the kind of the date time (UTC or local) in a single point in your application.
`IClock` provides an abstraction while getting the current time, so you can control the kind of the datetime (UTC or local) in a single point in your application.
**Example: Getting the current time**
@ -40,14 +40,14 @@ namespace AbpDemo
}
````
* Inject the `IClock` service when you need to get the current time. Common base classes (like ApplicationService) already injects it and provides as a base property - so, you can directly use as `Clock`.
* Inject the `IClock` service when you need to get the current time. Common base classes (like ApplicationService) already inject it and provide it as a base property - so, you can directly use it as `Clock`.
* Use the `Now` property to get the current time.
> Most of the times, `IClock` is the only service you need to know and use in your application.
> Most of the time, `IClock` is the only service you need to know and use in your application.
### Clock Options
`AbpClockOptions` is the [options](../fundamentals/options.md) class that used to set the clock kind.
`AbpClockOptions` is the [options](../fundamentals/options.md) class that is used to set the clock kind.
**Example: Use UTC Clock**
@ -58,13 +58,12 @@ Configure<AbpClockOptions>(options =>
});
````
Write this inside the `ConfigureServices` method of your [module](../architecture/modularity/basics.md).
> Default `Kind` is `Unspecified`, that actually make the Clock as it doesn't exists at all. Either make it `Utc` or `Local` if you want to get benefit of the Clock system.
Write this inside of the `ConfigureServices` method of your [module](../architecture/modularity/basics.md).
> The default `Kind` is `Unspecified`, which effectively disables the **Clock** functionality. Either make it `Utc` or `Local` if you want to get the benefit of the Clock system.
### DateTime Normalization
Other important function of the `IClock` is to normalize `DateTime` objects.
Another important function of the `IClock` is to normalize `DateTime` objects.
**Example usage:**
@ -75,11 +74,11 @@ var normalizedDateTime = Clock.Normalize(dateTime)
`Normalize` method works as described below:
* Converts the given `DateTime` to the UTC (by using the `DateTime.ToUniversalTime()` method) if current Clock is UTC and given `DateTime` is local.
* Converts the given `DateTime` to the local (by using the `DateTime.ToLocalTime()` method) if current Clock is local and given `DateTime` is UTC.
* Converts the given `DateTime` to the UTC (by using the `DateTime.ToUniversalTime()` method) if the current Clock is UTC and the given `DateTime` is local.
* Converts the given `DateTime` to the local (by using the `DateTime.ToLocalTime()` method) if the current Clock is local and the given `DateTime` is UTC.
* Sets `Kind` of the given `DateTime` (using the `DateTime.SpecifyKind(...)` method) to the `Kind` of the current Clock if given `DateTime`'s `Kind` is `Unspecified`.
`Normalize` method is used by the ABP when the it gets a `DateTime` that is not created by `IClock.Now` and may not be compatible with the current Clock type. Examples;
`Normalize` method is used by the ABP when it gets a `DateTime` that is not created by `IClock.Now` and may not be compatible with the current Clock type. Examples;
* `DateTime` type binding in the ASP.NET Core MVC model binding.
* Saving data to and reading data from database via [Entity Framework Core](../data/entity-framework-core).
@ -89,12 +88,67 @@ var normalizedDateTime = Clock.Normalize(dateTime)
`DisableDateTimeNormalization` attribute can be used to disable the normalization operation for desired classes or properties.
### DateTime Converter Between UTC and User's Time Zone
#### Convert given UTC to user's time zone.
`DateTime ConvertToUserTime(DateTime utcDateTime)` and `DateTimeOffset ConvertToUserTime(DateTimeOffset dateTimeOffset)` methods convert given UTC `DateTime` or `DateTimeOffset` to the user's time zone.
> If `SupportsMultipleTimezone` is `false` or `dateTime.Kind` is not `Utc` or these is no timezone setting, it returns the given `DateTime` or `DateTimeOffset` without any changes.
**Example:**
If user's `TimeZone Setting` is `Europe/Istanbul`
````csharp
// 2025-03-01T05:30:00Z
var utcTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc);
var userTime = Clock.ConvertToUserTime(utcTime);
// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours later.
userTime.Kind.ShouldBe(DateTimeKind.Unspecified);
userTime.ToString("O").ShouldBe("2025-03-01T08:30:00");
````
````csharp
// 2025-03-01T05:30:00Z
var utcTime = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc), TimeSpan.Zero);
var userTime = Clock.ConvertToUserTime(utcTime);
// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours later.
userTime.Offset.ShouldBe(TimeSpan.FromHours(3));
userTime.ToString("O").ShouldBe("2025-03-01T08:30:00.0000000+03:00");
````
#### Converts given user's DateTime to UTC
`DateTime ConvertToUtc(DateTime dateTime)` method convert given user's `DateTime` to UTC.
> If `SupportsMultipleTimezone` is `false` or `dateTime.Kind` is `Utc` or these is no timezone setting, it returns the given `DateTime` without any changes.
**Example:**
If user's `TimeZone Setting` is `Europe/Istanbul`
````csharp
// 2025-03-01T05:30:00
var userTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified); //Same as Local
var utcTime = Clock.ConvertToUtc(userTime);
// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours earlier.
utcTime.Kind.ShouldBe(DateTimeKind.Utc);
utcTime.ToString("O").ShouldBe("2025-03-01T02:30:00.0000000Z");
````
### Other IClock Properties
In addition to the `Now`, `IClock` service has the following properties:
* `Kind`: Returns a `DateTimeKind` for the currently used clock type (`DateTimeKind.Utc`, `DateTimeKind.Local` or `DateTimeKind.Unspecified`).
* `SupportsMultipleTimezone`: Returns `true` if currently used clock is UTC.
* `SupportsMultipleTimezone`: Returns `true` if the currently used clock is UTC.
## Time Zones
@ -102,12 +156,26 @@ This section covers the ABP infrastructure related to managing time zones.
### TimeZone Setting
ABP defines **a setting**, named `Abp.Timing.TimeZone`, that can be used to set and get the time zone for a user, [tenant](../architecture/multi-tenancy) or globally for the application. The default value is `UTC`.
ABP defines **a setting**, named `Abp.Timing.TimeZone`, that can be used to set and get the time zone for a user, [tenant](../architecture/multi-tenancy) or globally for the application. The default value is empty, which means the application will use the server's time zone.
You can change your host/tenant global time zone in the [Settings Management UI](../../modules/setting-management#setting-management-ui)
The [Account Pro Module](../../modules/account-pro#Time-Zone-Setting) supports user to set their own time zone in the account settings page.
See the [setting documentation](../infrastructure/settings.md) to learn more about the setting system.
### UseAbpTimeZone Middleware
The `app.UseAbpTimeZone()` middleware is used to set the time zone for the current request.
* It will get timezone from settings, the order is `User` -> `Tenant` -> `Application/Global`.
* If current request is anonymous, it will get timezone from the request header/cookie/form/query string. the key is `__timezone`.
> If you want to get current timezone, you can inject `ICurrentTimezoneProvider` service.
> Please add this middleware after authentication.
### ITimezoneProvider
`ITimezoneProvider` is a service to simple convert [Windows Time Zone Id](https://support.microsoft.com/en-us/help/973627/microsoft-time-zone-index-values) values to [Iana Time Zone Name](https://www.iana.org/time-zones) values and vice verse. It also provides methods to get list of these time zones and get a `TimeZoneInfo` with a given name.
`ITimezoneProvider` is a service to simply convert [Windows Time Zone Id](https://support.microsoft.com/en-us/help/973627/microsoft-time-zone-index-values) values to [Iana Time Zone Name](https://www.iana.org/time-zones) values and vice verse. It also provides methods to get the list of these time zones and get a `TimeZoneInfo` with a given name.
It has been implemented using the [TimeZoneConverter](https://github.com/mj1856/TimeZoneConverter) library.

2
docs/en/framework/real-time/signalr.md

@ -27,7 +27,7 @@ If you want to manually install;
1. Add the [Volo.Abp.AspNetCore.SignalR](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR) NuGet package to your project:
```
Install-Package Volo.Abp.AspNetCore.SignalR
dotnet add package Volo.Abp.AspNetCore.SignalR
```
Or use the Visual Studio NuGet package management UI to install it.

11
docs/en/framework/ui/maui/index.md

@ -37,10 +37,15 @@ Open a command line terminal and run the `adb reverse` command to expose a port
`adb reverse tcp:44305 tcp:44305`
> You should replace "44305" with the real port.
> You should run the command after starting the emulator.
> Replace `44305` with the port number your backend application is running on.
>
> Run this command **after** the Android emulator has started.
> If you don't have a separate installation of Android Debug Bridge, you can open it from **Visual Studio** by following toolbar menu `Tools` > `Android` > `Android Adb Command Prompt`. Android emulator has to be running for this operation.
> [!IMPORTANT]
> If your project uses a **tiered** or **microservice** architecture, ensure that both the **auth server** and all **remote service ports** are properly proxied using the `adb reverse` command. You can find all the required remote service ports and AuthServer configurations in your `YourProjectName.Maui/appsettings.json` file.
> [!NOTE]
> If you don't have a separate installation of **Android Debug Bridge** _(adb)_, you can open it from **Visual Studio** by following toolbar menu `Tools` > `Android` > `Android Adb Command Prompt`. Android emulator has to be running for this operation.
### iOS

5
docs/en/framework/ui/mvc-razor-pages/customization-user-interface.md

@ -89,7 +89,10 @@ This example overrides the **login page** defined by the [Account Module](../../
Create a page model class deriving from the ` LoginModel ` (defined in the ` Volo.Abp.Account.Web.Pages.Account ` namespace):
> If you are using the `AbpAccountWebOpenIddictModule` or `AbpAccountPublicWebOpenIddictModule`, the base class is `OpenIddictSupportedLoginModel` instead of `LoginModel`. And you should change the `ExposeServices` attribute to `[ExposeServices(typeof (MyLoginModel), typeof(OpenIddictSupportedLoginModel), typeof(LoginModel))]`
````csharp
[ExposeServices(typeof (MyLoginModel), typeof(LoginModel))]
public class MyLoginModel : LoginModel
{
public MyLoginModel(
@ -114,8 +117,6 @@ public class MyLoginModel : LoginModel
You can override any method or add new properties/methods if needed.
> Notice that we didn't use `[Dependency(ReplaceServices = true)]` or `[ExposeServices(typeof(LoginModel))]` since we don't want to replace the existing class in the dependency injection, we define a new one.
Copy `Login.cshtml` file into your solution as just described above. Change the **@model** directive to point to the `MyLoginModel`:
````xml

2
docs/en/get-started/empty-aspnet-core-application.md

@ -104,7 +104,7 @@ Replacing ASP.NET Core's DI system by Autofac and integrating to ABP is pretty e
1. Install [Volo.Abp.Autofac](https://www.nuget.org/packages/Volo.Abp.Autofac) package
````
Install-Package Volo.Abp.Autofac
dotnet add package Volo.Abp.Autofac
````
2. Add the ``AbpAutofacModule`` Dependency

BIN
docs/en/images/account-pro-external-login-settings.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 258 KiB

BIN
docs/en/images/account-pro-select-account-parameter.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
docs/en/images/account-pro-select-account.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
docs/en/images/account-pro-time-zone-setting.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
docs/en/images/create-aspnet-core-application.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 125 KiB

BIN
docs/en/images/create-new-aspnet-core-application-v2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 181 KiB

BIN
docs/en/images/pen-test-alert-list-9.1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
docs/en/images/samples-cms-kit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

BIN
docs/en/images/samples-easycrm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

BIN
docs/en/images/samples-eshoponabp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

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

Loading…
Cancel
Save