Browse Source

Merge branch 'dev' into issue-#3596

pull/18843/head
honurbu 2 years ago
parent
commit
6c96c82805
  1. 5
      Directory.Packages.props
  2. 31
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  3. 1
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
  4. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json
  5. 46
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  6. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json
  7. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json
  8. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json
  9. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ru.json
  10. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json
  11. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
  12. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json
  13. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json
  14. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json
  15. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json
  16. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en-GB.json
  17. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
  18. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json
  19. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json
  20. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json
  21. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json
  22. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json
  23. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json
  24. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json
  25. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json
  26. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json
  27. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json
  28. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json
  29. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json
  30. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ru.json
  31. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json
  32. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json
  33. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json
  34. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json
  35. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
  36. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json
  37. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  38. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
  39. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hant.json
  40. 72
      docs/en/Community-Articles/2024-01-04-CLI-Online-Translate/Post.md
  41. BIN
      docs/en/Community-Articles/2024-01-04-CLI-Online-Translate/deepl.jpg
  42. 33
      docs/en/Getting-Started-Running-Solution.md
  43. 2
      docs/en/Migration-Guides/Abp-8_0.md
  44. 85
      docs/en/Migration-Guides/Abp-8_1.md
  45. 6
      docs/en/Multi-Tenancy.md
  46. 197
      docs/en/UI/Angular/HTTP-Error-Handling.md
  47. 144
      docs/en/UI/Angular/HTTP-Requests.md
  48. 15
      docs/en/docs-nav.json
  49. 20
      framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorRemoteTenantStore.cs
  50. 5
      framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/MultiTenancyMiddleware.cs
  51. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcRemoteTenantStore.cs
  52. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/FindTenantResultDto.cs
  53. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs
  54. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/AbpTenantAppService.cs
  55. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs
  56. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js
  57. 5
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
  58. 31
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcActionDescriptorProvider.cs
  59. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs
  60. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs
  61. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs
  62. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs
  63. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeatureActionFilter.cs
  64. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs
  65. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeatureActionFilter.cs
  66. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeaturePageFilter.cs
  67. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Response/AbpNoContentActionFilter.cs
  68. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs
  69. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs
  70. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/AbpValidationActionFilter.cs
  71. 5
      framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpSerilogMiddleware.cs
  72. 5
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/AbpRequestLocalizationMiddleware.cs
  73. 7
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAuditingMiddleware.cs
  74. 5
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs
  75. 6
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Filters/IAbpFilter.cs
  76. 19
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Middleware/AbpMiddlewareBase.cs
  77. 5
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs
  78. 5
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs
  79. 5
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpDynamicClaimsMiddleware.cs
  80. 5
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AbpCorrelationIdMiddleware.cs
  81. 7
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Uow/AbpUnitOfWorkMiddleware.cs
  82. 12
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/WebContentFileProvider.cs
  83. 26
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProviderManager.cs
  84. 17
      framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs
  85. 2
      framework/src/Volo.Abp.BlazoriseUI/Components/UiMessageAlert.razor
  86. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs
  87. 1
      framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
  88. 25
      framework/src/Volo.Abp.Core/Volo/Abp/DisableAbpFeaturesAttribute.cs
  89. 1
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj
  90. 20
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs
  91. 43
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureValueProviderManager.cs
  92. 8
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureValueProviderManager.cs
  93. 6
      framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/ITenantNormalizer.cs
  94. 4
      framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/ITenantStore.cs
  95. 10
      framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantConfiguration.cs
  96. 11
      framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/UpperInvariantTenantNormalizer.cs
  97. 8
      framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ConfigurationStore/DefaultTenantStore.cs
  98. 7
      framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantConfigurationProvider.cs
  99. 27
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValueProviderManager.cs
  100. 3
      framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_WithDomainResolver_Tests.cs

5
Directory.Packages.props

@ -6,7 +6,7 @@
<PackageVersion Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="2.0.24" />
<PackageVersion Include="aliyun-net-sdk-sts" Version="3.1.2" />
<PackageVersion Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" />
<PackageVersion Include="AsyncKeyedLock" Version="6.3.3" />
<PackageVersion Include="AsyncKeyedLock" Version="6.3.4" />
<PackageVersion Include="Autofac" Version="8.0.0" />
<PackageVersion Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
<PackageVersion Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />
@ -27,7 +27,7 @@
<PackageVersion Include="Dapper" Version="2.1.21" />
<PackageVersion Include="Dapr.AspNetCore" Version="1.12.0" />
<PackageVersion Include="Dapr.Client" Version="1.12.0" />
<PackageVersion Include="Devart.Data.Oracle.EFCore" Version="10.1.151.7" />
<PackageVersion Include="Devart.Data.Oracle.EFCore" Version="10.3.10.8" />
<PackageVersion Include="DistributedLock.Core" Version="1.0.5" />
<PackageVersion Include="DistributedLock.Redis" Version="1.0.2" />
<PackageVersion Include="DeepL.net" Version="1.8.0" />
@ -107,7 +107,6 @@
<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="Nito.AsyncEx.Coordination" Version="5.1.2" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="NuGet.Versioning" Version="6.7.0" />

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

@ -563,8 +563,37 @@
"RegistrationUrl": "Registration Url",
"AllowAbpStudioBetaAccess": "Allow ABP Studio Beta Access",
"TotalQuestionCanNotBeNullMessage": "Total Question can not be null",
"Permission:OrganizationAutoRenewalPayments": "Organization Auto Renewal Payments",
"Permission:RetryFailedPayments": "Retry Failed Payments",
"AutoRenewalIsNotEnabled": "Auto Renewal is not enabled!",
"LicenseIsNotExpired": "License is not expired!",
"PaymentNotFound": "Payment not found!",
"PaymentAlreadyTried": "Payment already tried!",
"PaymentIsNotFailed": "Payment is not failed!",
"OrganizationIdIsNull": "OrganizationId is null!",
"Menu:AutoRenewals": "Auto Renewals",
"OrganizationAutoRenewalPayments": "Organization Auto Renewal Payments",
"PaymentDate": "Payment Date",
"TryCount": "Try Count",
"ErrorMessage": "Error Message",
"ErrorCode": "Error Code",
"CreditCard": "Credit Card",
"BuyerName": "Buyer Name",
"BuyerSurname": "Buyer Surname",
"BuyerEmail": "Buyer Email",
"AutoRenewalStartTime": "Auto Renewal Start Time",
"AutoRenewalEndTime": "Auto Renewal End Time",
"AutoRenewalEnabled": "Auto Renewal Enabled",
"LastAutoRenewalPaymentTime": "Last Auto Renewal Payment Time",
"OrganizationDoesNotHaveACreditCard": "Organization does not have a credit card!",
"Permission:EditWinners": "Edit Winners",
"Permission:ChangeDrawingStatus": "Change Drawing Status",
"RemoveAllWinnersConfirmationMessage": "Are you sure you want to remove all winners?"
"Menu:Licenses": "Licenses",
"OrganizationId": "Organization Id",
"RemoveAllWinnersConfirmationMessage": "Are you sure you want to remove all winners?",
"AutoRenewals": "Auto Renewals",
"OrganizationHasCreditCard": "Organization has credit card. Please remove credit card first!",
"DisplayName:DontSendEmailForLicenseExpire": "Don't Send Email For License Expire",
"DontSendEmailForLicenseExpire": "Don't Send Email For License Expire"
}
}

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

@ -29,6 +29,7 @@
"Volo.AbpIo.Domain:030012": "A user is entitled to have only 1 free trial period. You already used your trial license.",
"Volo.AbpIo.Domain:030013": "A user with an active license cannot start a trial license.",
"Volo.AbpIo.Domain:040000": "Telemetry already exists!.",
"Volo.AbpIo.Domain:050000": "Organization has credit card. You can not delete it!",
"Volo.AbpIo.Domain:070000": "The organization name can only contain latin letters, numbers, dots and hyphens!",
"Volo.AbpIo.Domain:070001": "The company name can only contain latin letters, numbers, dots, space and hyphens!",
"WantToLearn?": "Want to learn?",

6
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json

@ -531,8 +531,8 @@
"MyOrganizations_Detail_LicenseStartAndExpiryDate": "تاريخ بدء الترخيص - تاريخ انتهاء الصلاحية",
"MyOrganizations_Detail_OwnerRightInfo": "أنت تستخدم {0} من {1} حقوق المالك.",
"MyOrganizations_Detail_CopyApiKey": "انسخ المفتاح",
"MyOrganizations_Detail_ApiKeyDescription": "مفتاح API هو الرمز المميز لحزم PRO المستضافة على <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{1}.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "مصدر NuGet الخاص بك هو <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_ApiKeyDescription": "مفتاح API هو الرمز المميز لحزم PRO المستضافة على <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{1}.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "مصدر NuGet الخاص بك هو <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_PrivateNugetSourceWarning": "تتم إضافة هذا تلقائيًا كوجز إلى NuGet.Config في حل ABP الخاص بك. لا تشارك مفتاحك الخاص مع مستخدمين غير مصرح لهم!",
"MyOrganizations_Detail_DeveloperSeatInfo": "أنت تستخدم {0} من {1} مقاعد مطوري البرامج.",
"NeedMoreSeatsForYourTeam": "هل تحتاج إلى المزيد من المقاعد لفريقك؟",
@ -615,7 +615,7 @@
"Tools_Page_Description": "يوفر ABP التجاري أدوات تطوير سريعة للتطبيقات لزيادة إنتاجية المطورين. يتيح لك ABP Suite إنشاء صفحات CRUD بسهولة.",
"DeveloperPrice": "سعر المطور",
"AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0} <small>مطورين</small>",
"LicenseRemainingDays": "لمدة <span> {0} </span> يوم",
"LicenseRemainingDays": "لمدة <span class=\"text-white\"> {0} </span> يوم",
"ExtendPaymentInfoSection_Description": "من خلال تمديد / تجديد رخصتك، ستستمر في الحصول على <a href=\"{0}\" target=\"_blank\">الدعم المتميز</a>. ستتمكن أيضًا من الحصول على تحديثات رئيسية أو ثانوية للوحدات والسمات. ستتمكن من الاستمرار في إنشاء مشاريع جديدة. وستظل قادرًا على استخدام <a href=\"{1}\" target=\"_blank\">ABP Suite</a> مما يسرع من تطويرك.",
"LicenseRenewalPrice": "سعر تجديد الرخصة",
"LicensePrice": "سعر الرخصة",

46
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json

@ -556,8 +556,8 @@
"MyOrganizations_Detail_LicenseStartAndExpiryDate": "License Start Date - Expiry Date",
"MyOrganizations_Detail_OwnerRightInfo": "You are using {0} of your {1} owners rights.",
"MyOrganizations_Detail_CopyApiKey": "Copy the Key",
"MyOrganizations_Detail_ApiKeyDescription": "The API Key is the token of PRO packages hosted on <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{1}.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "Your private NuGet source is <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_ApiKeyDescription": "The API Key is the token of PRO packages hosted on <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{1}.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "Your private NuGet source is <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_PrivateNugetSourceWarning": "This is automatically added as a feed to your NuGet.Config in your ABP solution. Do not share your private key with unauthorized users!",
"MyOrganizations_Detail_DeveloperSeatInfo": "You are using {0} of your {1} developer seats.",
"NeedMoreSeatsForYourTeam": "Need more seats for your team?",
@ -641,7 +641,7 @@
"Tools_Page_Description": "ABP Commercial provides rapid application development tooling to increase developer productivity. ABP Suite allows you to create CRUD pages easily.",
"DeveloperPrice": "Developer Price",
"AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0} <small>developers</small>",
"LicenseRemainingDays": "for <span> {0} </span> days",
"LicenseRemainingDays": "for <span class=\"text-white\"> {0} </span> days",
"ExtendPaymentInfoSection_Description": "By extending/renewing your license, you will continue to get <a href=\"{0}\" target=\"_blank\">premium support</a>. You will also be able to get major or minor updates for modules and themes. You will be able to continue creating new projects. And you will still be able to use <a href=\"{1}\" target=\"_blank\">ABP Suite</a> which speeds up your development.",
"LicenseRenewalPrice": "License renewal price",
"LicensePrice": "License Price",
@ -838,7 +838,7 @@
"ModulesPageTitle": "ABP Pre-Built Application Modules",
"Volo.AbpIo.Commercial:040001": "API Access Key is incorrect.",
"GetLepton": "Get Lepton Now",
"MyOrganizations_Detail_LicenseStartDate": "License Start Date",
"MyOrganizations_Detail_LicenseStartDate": "Start Date",
"MyOrganizations_Detail_LicenseExpiryDate": "Expiry Date",
"BlazoriseSupport": "How do I get the Blazorise license key and support from the Blazorise team?",
"BlazoriseSupportExplanation": "Follow the steps below to get support from the Blazorise team and get your Blazorise license key:",
@ -1090,8 +1090,46 @@
"AbpStudioBetaAccessInfoTitle": "ABP Studio Beta Access",
"AbpStudioBetaAccessInfoText": "We're thrilled to share with you the <span class=\"text-highlight-white\">beta</span> version of <span class=\"text-highlight-white\">ABP Studio</span>! This release marks a significant milestone in our development journey, and we're eager to gather your feedback to make the application even better.",
"YouAreNotAuthorizedToDownloadStudio": "You are not authorized to download ABP Studio.",
"OrganizationHasNoDefaultCreditCard": "Your organization has no default credit card. Please add a credit card to your organization.",
"YouAreNotAuthorizedToPayThisPaymentRequest": "You are not authorized to pay this payment request.",
"YouAreNotAuthorizedToCreateBillingInfoForThisPaymentRequest": "You are not authorized to create billing info for this payment request.",
"OrganizationNotFound": "Organization not found.",
"CannotDeleteDefaultCardBecauseAutoRenewalEnabled": "You cannot delete this card at the moment because the Auto-Renewal feature is enabled. To delete the card, first disable Auto-Renewal.",
"AreYouSureWantToDeleteThisCard": "Are you sure you want to delete this card?",
"AreYouSureWantToSetThisCardAsDefault": "Are you sure you want to set this card as default?",
"OrganizationBillingInfoIsNotSuitableForIyzicoPayment": "Your organization's billing info is not suitable for iyzico payment.",
"AutomaticRenewal": "Automatic Renewal",
"AutomaticRenewal_Description": "Renewing a license before it expires lets you get a discount of up to %40. The auto-renewal process lets you renew your license without losing this discount, and your development will never interrupt. Auto-renewal is only available for credit card payment. You can disable auto-renewal at any time by accessing your Organization Management page. ABP does not save your credit card information, but our global payment gateways do secure savings.",
"CardNotFoundMessage": "Do you want to add a new card?",
"CardNotFoundTitle": "Card Not Found",
"AutoRenewalEnabled": "Auto Renewal Enabled",
"AutoRenewalDisabled": "Auto Renewal Disabled",
"PaymentRequestIdIsNotProvided": "Payment request id is not provided.",
"PaymentFailedInfo": "Sorry, payment failed! This could be due to insufficient funds, invalid credit card numbers or invalid pin",
"UsedPayment": "This payment has been already used",
"ABPStudioBetaAccess": "ABP Studio Beta Access",
"VisitABPStudio": "Visit ABP Studio",
"EditBillingInformation": "Edit Billing Information",
"Organization": "Organization",
"E-Book": "E-Book",
"CreditCards": "Credit Cards",
"BillingInformation": "Billing Information",
"AddNewCreditCard": "Add New Credit Card",
"MyOrganizations_LearnMorePlan": "Learn more about plans on the pricing page",
"AutoLicenseRenewalIsNotEnabled": "Auto license renewal is not enabled.",
"SetAsDefaultPaymentMethod": "Set as default payment method",
"{0}PerAdditionalDeveloper": "{0} per additional developer",
"CardAlias": "Card Alias",
"AbpDoesNotSaveYourPaymentDetails_Description": "The payment data will be saved in <a href=\"{0}\" target=\"_blank\" class=\"{1}\">{2}</a> security vaults and you can remove stored data anytime. Enabling auto-renewal ensures that your ABP subscription will automatically renew prior to expiration, providing a valid credit card. Disabling auto-renewal means you will have to renew your subscription manually. To continue your project development without interruption, we suggest you enable the Auto-Renewal option.",
"AddBillingInformation": "Add Billing Information",
"YouHaveNoCardsSaved": "You have no cards saved.",
"CreateCreditCardModal_BillingDetails_Description": "You must save your billing details to be able to add your credit card.",
"TaxNo": "Tax No",
"CardNumber": "Card Number",
"NameOnCard": "Name on Card",
"BillingDetails": "Billing Details",
"ThereIsNoDeveloper": "There is no developer.",
"CardDetails": "Debit/Credit Card Details",
"NoActiveLicence": "You are not eligible for this action! You have no active license."
}
}

6
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json

@ -553,8 +553,8 @@
"MyOrganizations_Detail_LicenseStartAndExpiryDate": "Lisenssin alkamispäivä - viimeinen voimassaolopäivä",
"MyOrganizations_Detail_OwnerRightInfo": "Käytät {0} {1} omistajan oikeuksistasi.",
"MyOrganizations_Detail_CopyApiKey": "Kopioi avain",
"MyOrganizations_Detail_ApiKeyDescription": "API-avain on <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{1}-palvelussa isännöivien PRO-pakettien tunnus.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "Yksityinen NuGet-lähteesi on <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_ApiKeyDescription": "API-avain on <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{1}-palvelussa isännöivien PRO-pakettien tunnus.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "Yksityinen NuGet-lähteesi on <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_PrivateNugetSourceWarning": "Tämä lisätään automaattisesti syötteeksi NuGet.Configiin ABP-ratkaisussasi. Älä jaa yksityistä avaintasi luvattomien käyttäjien kanssa!",
"MyOrganizations_Detail_DeveloperSeatInfo": "Käytät {0} {1} kehittäjäpaikastasi.",
"NeedMoreSeatsForYourTeam": "Tarvitsetko lisää paikkoja tiimillesi?",
@ -639,7 +639,7 @@
"Tools_Page_Description": "ABP Commercial tarjoaa nopeat sovelluskehitystyökalut kehittäjien tuottavuuden lisäämiseksi. ABP Suiten avulla voit luoda CRUD-sivuja helposti.",
"DeveloperPrice": "Kehittäjän hinta",
"AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0} <small>kehittäjät</small>",
"LicenseRemainingDays": "<span> {0}</span> päivän ajan",
"LicenseRemainingDays": "<span class=\"text-white\"> {0}</span> päivän ajan",
"ExtendPaymentInfoSection_Description": "Pidentämällä/uusimalla käyttölupaasi saat edelleen <a href=\"{0}\" target=\"_blank\">premium-tuen</a>. Voit myös saada suurempia tai pieniä päivityksiä moduuleille ja teemoille. Voit jatkaa uusien projektien luomista. Voit silti käyttää <a href=\"{1}\" target=\"_blank\">ABP Suitea</a>, joka nopeuttaa kehitystäsi.",
"LicenseRenewalPrice": "Lisenssin uusimisen hinta",
"LicensePrice": "Lisenssin hinta",

6
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json

@ -556,8 +556,8 @@
"MyOrganizations_Detail_LicenseStartAndExpiryDate": "Datum početka licence - Datum isteka",
"MyOrganizations_Detail_OwnerRightInfo": "Koristite {0} od svojih {1} vlasničkih prava.",
"MyOrganizations_Detail_CopyApiKey": "Kopirajte ključ",
"MyOrganizations_Detail_ApiKeyDescription": "API ključ je token PRO paketa hostiranih na <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{1}.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "Vaš privatni NuGet izvor je <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_ApiKeyDescription": "API ključ je token PRO paketa hostiranih na <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{1}.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "Vaš privatni NuGet izvor je <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_PrivateNugetSourceWarning": "Ovo se automatski dodaje kao feed u vaš NuGet.Config u vašem ABP rješenju. Ne dijelite svoj privatni ključ s neovlaštenim korisnicima!",
"MyOrganizations_Detail_DeveloperSeatInfo": "Koristite {0} od svojih {1} razvojnih mjesta.",
"NeedMoreSeatsForYourTeam": "Trebate više mjesta za svoj tim?",
@ -641,7 +641,7 @@
"Tools_Page_Description": "ABP Commercial pruža alate za brzi razvoj aplikacija za povećanje produktivnosti programera. ABP Suite vam omogućuje jednostavno stvaranje CRUD stranica.",
"DeveloperPrice": "Cijena programera",
"AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0} <small>programeri</small>",
"LicenseRemainingDays": "za <span>{0}</span> dana",
"LicenseRemainingDays": "za <span class=\"text-white\">{0}</span> dana",
"ExtendPaymentInfoSection_Description": "Produljenjem/obnavljanjem licence i dalje ćete dobivati <a href=\"{0}\" target=\"_blank\">premium podršku</a> . Također ćete moći dobiti veća ili manja ažuriranja za module i teme. Moći ćete nastaviti stvarati nove projekte. I dalje ćete moći koristiti <a href=\"{1}\" target=\"_blank\">ABP Suite</a> koji ubrzava vaš razvoj.",
"LicenseRenewalPrice": "Cijena obnove licence",
"LicensePrice": "Cijena licence",

6
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json

@ -554,8 +554,8 @@
"MyOrganizations_Detail_LicenseStartAndExpiryDate": "licensz kezdő dátuma – lejárati dátum",
"MyOrganizations_Detail_OwnerRightInfo": "Ön {1} tulajdonosi jogának {0}-át használja.",
"MyOrganizations_Detail_CopyApiKey": "Másolja ki a kulcsot",
"MyOrganizations_Detail_ApiKeyDescription": "Az API-kulcs a(z) <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{1} webhelyen tárolt PRO-csomagok tokenje.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "Az Ön privát NuGet-forrása <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_ApiKeyDescription": "Az API-kulcs a(z) <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{1} webhelyen tárolt PRO-csomagok tokenje.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "Az Ön privát NuGet-forrása <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_PrivateNugetSourceWarning": "Ez automatikusan hozzáadódik feedként a NuGet.Config-hoz az ABP-megoldásban. Ne ossza meg privát kulcsát illetéktelen felhasználókkal!",
"MyOrganizations_Detail_DeveloperSeatInfo": "Ön {1} fejlesztői helyéből {0}-ot használ.",
"NeedMoreSeatsForYourTeam": "Több hozzáférésre van szüksége a csapatának?",
@ -639,7 +639,7 @@
"Tools_Page_Description": "Az ABP Commercial gyors alkalmazásfejlesztési eszközöket biztosít a fejlesztők termelékenységének növelése érdekében. Az ABP Suite segítségével könnyedén hozhat létre CRUD oldalakat.",
"DeveloperPrice": "Fejlesztői ár",
"AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0} <small>fejlesztő</small>",
"LicenseRemainingDays": "<span>{0}</span> napig",
"LicenseRemainingDays": "<span class=\"text-white\">{0}</span> napig",
"ExtendPaymentInfoSection_Description": "A licensz meghosszabbításával/megújításával továbbra is <a href=\"{0}\" target=\"_blank\">prémium támogatást</a> kap. Emellett kisebb-nagyobb frissítéseket is kaphat a modulokhoz és témákhoz. Folytathatja új projektek létrehozását. És továbbra is használhatja az <a href=\"{1}\" target=\"_blank\">ABP Suite</a> -ot, amely felgyorsítja a fejlesztést.",
"LicenseRenewalPrice": "Licensz megújítási ára",
"LicensePrice": "Licensz ára",

6
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ru.json

@ -557,8 +557,8 @@
"MyOrganizations_Detail_LicenseStartAndExpiryDate": "Дата начала действия лицензии – дата истечения срока действия",
"MyOrganizations_Detail_OwnerRightInfo": "Вы используете {0} ваших прав владельца {1}.",
"MyOrganizations_Detail_CopyApiKey": "Скопируйте ключ",
"MyOrganizations_Detail_ApiKeyDescription": "Ключ API — это токен PRO-пакетов, размещенных на <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{1}.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "Ваш личный источник NuGet: <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_ApiKeyDescription": "Ключ API — это токен PRO-пакетов, размещенных на <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{1}.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "Ваш личный источник NuGet: <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_PrivateNugetSourceWarning": "Он автоматически добавляется в качестве канала в ваш NuGet.Config в вашем решении ABP. ",
"MyOrganizations_Detail_DeveloperSeatInfo": "Вы используете {0} из {1} мест разработчика.",
"NeedMoreSeatsForYourTeam": "Вам нужно больше мест для вашей команды?",
@ -642,7 +642,7 @@
"Tools_Page_Description": "ABP Commercial предоставляет инструменты для быстрой разработки приложений, позволяющие повысить продуктивность разработчиков. ",
"DeveloperPrice": "Цена застройщика",
"AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0} <small>Разработчики</small>",
"LicenseRemainingDays": "для <span> {0} </span> дни",
"LicenseRemainingDays": "для <span class=\"text-white\"> {0} </span> дни",
"ExtendPaymentInfoSection_Description": "Продлив/продлив лицензию, вы продолжите получать <a href=\"{0}\" target=\"_blank\">премиум-поддержка</a>. <a href=\"{1}\" target=\"_blank\">ABP Люкс</a> что ускоряет ваше развитие.",
"LicenseRenewalPrice": "Стоимость продления лицензии",
"LicensePrice": "Цена лицензии",

6
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json

@ -563,8 +563,8 @@
"MyOrganizations_Detail_LicenseStartAndExpiryDate": "Lisans Başlangıç Tarihi - Sona Erme Tarihi",
"MyOrganizations_Detail_OwnerRightInfo": "{1} sahiplik hakkınızın {0}'ını kullanıyorsunuz.",
"MyOrganizations_Detail_CopyApiKey": "Anahtarı Kopyala",
"MyOrganizations_Detail_ApiKeyDescription": "API Anahtarı, <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{1}</a> üzerinde barındırılan PRO paketlerinin belirtecidir.",
"MyOrganizations_Detail_YourPrivateNugetSource": "Özel NuGet kaynağınız <a href=\"{0}\" arget=\"_blank\" class=\"text-primary\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_ApiKeyDescription": "API Anahtarı, <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{1}</a> üzerinde barındırılan PRO paketlerinin belirtecidir.",
"MyOrganizations_Detail_YourPrivateNugetSource": "Özel NuGet kaynağınız <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_PrivateNugetSourceWarning": "Bu, ABP çözümünüzdeki NuGet.Config'inize otomatik olarak bir besleme olarak eklenir. Özel anahtarınızı yetkisiz kullanıcılarla paylaşmayın!",
"MyOrganizations_Detail_DeveloperSeatInfo": "{1} geliştirici koltuğunuzun {0} tanesini kullanıyorsunuz.",
"NeedMoreSeatsForYourTeam": "Takımınız için daha fazla geliştirici koltuğu gerekiyor mu?",
@ -648,7 +648,7 @@
"Tools_Page_Description": "ABP Commercial, geliştirici verimliliğini artırmak için hızlı uygulama geliştirme araçları sağlar. ABP Suite, CRUD sayfalarını kolayca oluşturmanızı sağlar.",
"DeveloperPrice": "Geliştirici Fiyatı",
"AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0} <small>geliştiriciler</small>",
"LicenseRemainingDays": "<span> {0} </span> gün boyunca",
"LicenseRemainingDays": "<span class=\"text-white\"> {0} </span> gün boyunca",
"ExtendPaymentInfoSection_Description": "Lisansınızı uzatarak/yenileyerek <a href=\"{0}\" target=\"_blank\">premium destek</a> almaya devam edeceksiniz. You will also be able to get major or minor updates for modules and themes. You will be able to continue creating new projects. Ve gelişiminizi hızlandıran <a href=\"{1}\" target=\"_blank\">ABP Suite</a>'i kullanmaya devam edebileceksiniz.",
"LicenseRenewalPrice": "Lisans yenileme fiyatı",
"LicensePrice": "Lisans fiyatı",

6
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json

@ -556,8 +556,8 @@
"MyOrganizations_Detail_LicenseStartAndExpiryDate": "许可证开始日期 - 到期日期",
"MyOrganizations_Detail_OwnerRightInfo": "您正在使用{1}所有者权利中的{0}项权利。",
"MyOrganizations_Detail_CopyApiKey": "复制密钥",
"MyOrganizations_Detail_ApiKeyDescription": "API 密钥是 <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{1}.</a> 上托管的 PRO 包的标记。",
"MyOrganizations_Detail_YourPrivateNugetSource": "您的 NuGet 私有源是 <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_ApiKeyDescription": "API 密钥是 <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{1}.</a> 上托管的 PRO 包的标记。",
"MyOrganizations_Detail_YourPrivateNugetSource": "您的 NuGet 私有源是 <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_PrivateNugetSourceWarning": "此密钥会自动添加到 ABP 解决方案中的 NuGet.Config 中。请勿与未经授权的用户共享您的私人密钥!",
"MyOrganizations_Detail_DeveloperSeatInfo": "您正在使用{1}个开发人员席位中的{0}个。",
"NeedMoreSeatsForYourTeam": "您的团队需要更多座位?",
@ -641,7 +641,7 @@
"Tools_Page_Description": "ABP 商业版提供快速应用程序开发工具,以提高开发人员的工作效率。ABP Suite 可让您轻松创建 CRUD 页面。",
"DeveloperPrice": "开发者价格",
"AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0}<小>开发人员</小",
"LicenseRemainingDays": "为<span> {0} </span> 天",
"LicenseRemainingDays": "为<span class=\"text-white\"> {0} </span> 天",
"ExtendPaymentInfoSection_Description": "延长/续订许可证后,您将继续获得<a href=\"{0}\" target=\"_blank\">高级支持</a>。您还可以获得模块和主题的主要或次要更新。您还可以继续创建新项目。您还可以使用 <a href=\"{1}\" target=\"_blank\">ABP Suite</a> 来加快开发速度。",
"LicenseRenewalPrice": "许可证续期价格",
"LicensePrice": "许可证价格",

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

@ -545,7 +545,7 @@
"MyOrganizations_Detail_OwnerRightInfo": "您正在使用您的 {1} 項所有者權利中的 {0} 項。",
"MyOrganizations_Detail_CopyApiKey": "複製密鑰",
"MyOrganizations_Detail_ApiKeyDescription": "API 金鑰是<a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{1} 上託管的 PRO 包的令牌。</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "您的私有 NuGet 來源是<a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "您的私有 NuGet 來源是<a href=\"{0}\" target=\"_blank\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_PrivateNugetSourceWarning": "這會作為 feed 自動添加到 ABP 解決方案中的 NuGet.Config 中。不要與未經授權的用戶分享您的私鑰!",
"MyOrganizations_Detail_DeveloperSeatInfo": "您正在使用 {1} 個開發者席位中的 {0} 個。",
"NeedMoreSeatsForYourTeam": "您的團隊需要更多座位嗎?",
@ -628,7 +628,7 @@
"Tools_Page_Description": "ABP Commercial 提供快速應用程式開發工具來提高開發人員的工作效率。 ABP Suite 讓您輕鬆建立 CRUD 頁面。",
"DeveloperPrice": "開發商價格",
"AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0}<small>開發者</small>",
"LicenseRemainingDays": "<span>{0}</span>天",
"LicenseRemainingDays": "<span class=\"text-white\">{0}</span>天",
"ExtendPaymentInfoSection_Description": "透過延長/更新您的許可證,您將繼續獲得<a href=\"{0}\" target=\"_blank\">高級支援</a>。您還可以獲得模組和主題的主要或次要更新。您將能夠繼續建立新專案。而且您仍然可以使用<a href=\"{1}\" target=\"_blank\">ABP Suite</a>來加快您的開發速度。",
"LicenseRenewalPrice": "許可證更新價格",
"LicensePrice": "許可價格",

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json

@ -255,6 +255,8 @@
"LatestArticles": "أحدث المقالات",
"RaffleHeader": "مرحبًا عضو مجتمع برنامج الجسر الأكاديمي!",
"RafflesInfo": "<br>هذه هي صفحة السحب المخصصة لإظهار تقديرنا لك لكونك عضوًا نشطًا في المجتمع. نحن نقوم بإجراء <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">محادثات مجتمعية لبرنامج ABP</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">، ومؤتمر ABP .NET</a> ، ونحضر أو نرعى الأحداث المتعلقة بـ .NET والتي نقدم فيها بعض الهدايا.<br><br> يمكنك متابعة هذه الصفحة لرؤية السحوبات القادمة أو حضورها أو الاطلاع على السحوبات السابقة التي قمنا بسحبها بما في ذلك الفائزين.<br><br> شكرا لكونك عضوا نشطا! نراكم في السحوبات القادمة.",
"RafflesInfoTitle": "<span class=\"gradient-community\">سحوبات</span> مجتمع ABP"
"RafflesInfoTitle": "<span class=\"gradient-community\">سحوبات</span> مجتمع ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> أيد.",
"Preview": "معاينة"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json

@ -255,6 +255,8 @@
"LatestArticles": "poslední články",
"RaffleHeader": "Dobrý den, člen komunity ABP!",
"RafflesInfo": "<br>Toto je stránka s losováním věnovaná tomu, abychom vám ukázali naše uznání za to, že jste aktivním členem komunity. Pořádáme <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP Community Talks</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, ABP .NET Conference</a> , účastníme se nebo sponzorujeme akce související s .NET, ve kterých rozdáváme nějaké dárky.<br><br> Na této stránce můžete sledovat nadcházející tomboly, zúčastnit se jich nebo si prohlédnout předchozí tomboly, které losujeme, včetně výherců.<br><br> Děkujeme, že jste aktivním členem! Uvidíme se v nadcházejícím slosování.",
"RafflesInfoTitle": "Komunitní <span class=\"gradient-community\">tomboly</span> ABP"
"RafflesInfoTitle": "Komunitní <span class=\"gradient-community\">tomboly</span> ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> podporováno.",
"Preview": "Náhled"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json

@ -255,6 +255,8 @@
"LatestArticles": "Neueste Artikel",
"RaffleHeader": "Hallo ABP-Community-Mitglied!",
"RafflesInfo": "<br>Auf dieser Verlosungsseite möchten wir Ihnen unsere Wertschätzung dafür zeigen, dass Sie ein aktives Community-Mitglied sind. Wir veranstalten <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP Community Talks</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">und ABP .NET Conferences</a> , nehmen an .NET-bezogenen Veranstaltungen teil oder sponsern diese, bei denen wir einige Geschenke verschenken.<br><br> Sie können dieser Seite folgen, um die bevorstehenden Verlosungen zu sehen, daran teilzunehmen oder frühere Verlosungen, die wir verlosen, einschließlich der Gewinner, anzusehen.<br><br> Vielen Dank, dass Sie ein aktives Mitglied sind! Wir sehen uns bei den kommenden Gewinnspielen.",
"RafflesInfoTitle": "ABP-Community- <span class=\"gradient-community\">Verlosungen</span>"
"RafflesInfoTitle": "ABP-Community- <span class=\"gradient-community\">Verlosungen</span>",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> unterstützt.",
"Preview": "Vorschau"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en-GB.json

@ -103,6 +103,8 @@
"Language": "Language",
"CreatePostLanguageInfo": "Language of the post",
"SeeMore": "See More",
"MemberNotPublishedPostYet": "This member hasn't published any posts yet."
"MemberNotPublishedPostYet": "This member hasn't published any posts yet.",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> supported.",
"Preview": "Preview"
}
}

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

@ -256,6 +256,8 @@
"RaffleHeader": "Hello ABP Community Member!",
"RafflesInfo": "<br>This is the raffle page dedicated to show our appreciation towards you for being an active Community Member. We do <a class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP Community Talks</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">,ABP .NET Conference</a>, attend or sponsor to the .NET-related events in which we give away some gifts. <br><br>You can follow this page to see the upcoming raffles, attend them, or see previous raffles we draw including the winners. <br><br>Thank you for being an active member! See you in the upcoming raffles.",
"RafflesInfoTitle": "ABP Community <span class=\"gradient-community\">Raffles</span>",
"ToLuckyWinner": "to 1 lucky winner"
"ToLuckyWinner": "to 1 lucky winner",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> supported.",
"Preview": "Preview"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json

@ -255,6 +255,8 @@
"LatestArticles": "últimos artículos",
"RaffleHeader": "¡Hola miembro de la comunidad ABP!",
"RafflesInfo": "<br>Esta es la página del sorteo dedicada a mostrarle nuestro agradecimiento por ser un miembro activo de la comunidad. Realizamos <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">charlas comunitarias de ABP</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, conferencias ABP .NET</a> , asistimos o patrocinamos eventos relacionados con .NET en los que regalamos algunos obsequios.<br><br> Puedes seguir esta página para ver los próximos sorteos, asistir a ellos o ver sorteos anteriores que sorteamos incluyendo a los ganadores.<br><br> ¡Gracias por ser un miembro activo! Nos vemos en los próximos sorteos.",
"RafflesInfoTitle": "<span class=\"gradient-community\">Rifas</span> de la Comunidad ABP"
"RafflesInfoTitle": "<span class=\"gradient-community\">Rifas</span> de la Comunidad ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> soportado.",
"Preview": "Avance"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json

@ -255,6 +255,8 @@
"LatestArticles": "Viimeisimmät artikkelit",
"RaffleHeader": "Hei ABP-yhteisön jäsen!",
"RafflesInfo": "<br>Tämä on arvontasivu, joka on omistettu osoittamaan kiitollisuuttamme sinua kohtaan, että olet aktiivinen yhteisön jäsen. Teemme <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP Community Talksia</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, ABP .NET -konferenssia</a> , osallistumme tai sponsoroimme .NET-tapahtumia, joissa annamme lahjoja.<br><br> Voit seurata tätä sivua nähdäksesi tulevat arvonnat, osallistua niihin tai nähdäksesi aiemmat arvonnamme, mukaan lukien voittajat.<br><br> Kiitos aktiivisesta jäsenyydestäsi! Nähdään tulevissa arvonnassa.",
"RafflesInfoTitle": "ABP-yhteisön <span class=\"gradient-community\">arpajaiset</span>"
"RafflesInfoTitle": "ABP-yhteisön <span class=\"gradient-community\">arpajaiset</span>",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> tuettu.",
"Preview": "Esikatselu"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json

@ -255,6 +255,8 @@
"LatestArticles": "Derniers articles",
"RaffleHeader": "Bonjour membre de la communauté ABP !",
"RafflesInfo": "<br>Ceci est la page de tirage au sort dédiée à vous montrer notre gratitude pour votre participation active à la communauté. Nous organisons <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">des discussions communautaires ABP</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, des conférences ABP .NET</a> , assistons ou sponsorisons des événements liés à .NET au cours desquels nous offrons des cadeaux.<br><br> Vous pouvez suivre cette page pour voir les tirages au sort à venir, y assister ou voir les tirages au sort précédents que nous tirons, y compris les gagnants.<br><br> Merci d&#39;être un membre actif ! Rendez-vous lors des prochains tirages au sort.",
"RafflesInfoTitle": "<span class=\"gradient-community\">Tirages</span> au sort communautaires ABP"
"RafflesInfoTitle": "<span class=\"gradient-community\">Tirages</span> au sort communautaires ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> prise en charge.",
"Preview": "Aperçu"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json

@ -255,6 +255,8 @@
"LatestArticles": "नवीनतम लेख",
"RaffleHeader": "नमस्ते एबीपी समुदाय सदस्य!",
"RafflesInfo": "<br>यह रैफ़ल पेज है जो एक सक्रिय समुदाय सदस्य होने के लिए आपके प्रति हमारी सराहना दिखाने के लिए समर्पित है। हम <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">एबीपी सामुदायिक वार्ता</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, एबीपी .NET सम्मेलन</a> करते हैं, .NET से संबंधित कार्यक्रमों में भाग लेते हैं या प्रायोजित करते हैं जिसमें हम कुछ उपहार देते हैं।<br><br> आप आगामी रैफ़ल देखने, उनमें भाग लेने, या विजेताओं सहित हमारे द्वारा निकाले गए पिछले रैफ़ल देखने के लिए इस पृष्ठ का अनुसरण कर सकते हैं।<br><br> सक्रिय सदस्य बनने के लिए धन्यवाद! आगामी रैफ़ल में मिलते हैं।",
"RafflesInfoTitle": "एबीपी कम्युनिटी <span class=\"gradient-community\">रैफल्स</span>"
"RafflesInfoTitle": "एबीपी कम्युनिटी <span class=\"gradient-community\">रैफल्स</span>",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> का समर्थन किया।",
"Preview": "पूर्व दर्शन"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json

@ -255,6 +255,8 @@
"LatestArticles": "Najnoviji članci",
"RaffleHeader": "Pozdrav članu ABP zajednice!",
"RafflesInfo": "<br>Ovo je stranica za nagradnu igru namijenjena da vam pokažemo našu zahvalnost što ste aktivni član zajednice. Vodimo <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">razgovore o ABP zajednici</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, ABP .NET konferenciji</a> , prisustvujemo ili sponzoriramo događaje vezane uz .NET na kojima dijelimo neke darove.<br><br> Možete pratiti ovu stranicu kako biste vidjeli nadolazeće nagradne igre, prisustvovali im ili vidjeli prethodne nagradne igre koje izvlačimo uključujući dobitnike.<br><br> Hvala vam što ste aktivan član! Vidimo se u nadolazećim nagradnim igrama.",
"RafflesInfoTitle": "<span class=\"gradient-community\">Izvlačenje nagradne igre</span> zajednice ABP"
"RafflesInfoTitle": "<span class=\"gradient-community\">Izvlačenje nagradne igre</span> zajednice ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> podržan.",
"Preview": "Pregled"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json

@ -255,6 +255,8 @@
"LatestArticles": "Legfrissebb cikkek",
"RaffleHeader": "Kedves ABP közösségi tag!",
"RafflesInfo": "<br>Ez az a sorsolási oldal, amelynek célja, hogy kifejezzük hálánkat feléd, amiért aktív közösségi tag vagy. <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">Az ABP Community Talks-okat</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, az ABP .NET konferenciákat</a> szervezzük, részt veszünk vagy szponzorálunk olyan .NET-hez kapcsolódó eseményeket, amelyeken ajándékokat adunk.<br><br> Ezt az oldalt követheti, hogy megtekinthesse a közelgő sorsolásokat, részt vegyen azokon, vagy megtekinthesse korábbi sorsolásainkat, beleértve a nyerteseket is.<br><br> Köszönjük, hogy aktív tag vagy! Találkozunk a sorsoláson.",
"RafflesInfoTitle": "ABP közösségi <span class=\"gradient-community\">tombola</span>"
"RafflesInfoTitle": "ABP közösségi <span class=\"gradient-community\">tombola</span>",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> támogatott.",
"Preview": "Előnézet"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json

@ -255,6 +255,8 @@
"LatestArticles": "Nýjustu greinar",
"RaffleHeader": "Halló ABP samfélagsmeðlimur!",
"RafflesInfo": "<br>Þetta er happdrættisíðan sem er tileinkuð þér að þakka þér fyrir að vera virkur samfélagsmeðlimur. Við gerum <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP Community Talks</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, ABP .NET ráðstefnu</a> , sækjum eða styrkjum .NET tengda viðburði þar sem við gefum nokkrar gjafir.<br><br> Þú getur fylgst með þessari síðu til að sjá komandi happdrætti, mæta á þær eða sjá fyrri happdrætti sem við drögum út, þar á meðal vinningshafa.<br><br> Þakka þér fyrir að vera virkur meðlimur! Sjáumst í komandi happdrætti.",
"RafflesInfoTitle": "ABP <span class=\"gradient-community\">samfélagshappdrætti</span>"
"RafflesInfoTitle": "ABP <span class=\"gradient-community\">samfélagshappdrætti</span>",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> stutt.",
"Preview": "Forskoðun"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json

@ -255,6 +255,8 @@
"LatestArticles": "Articoli Recenti",
"RaffleHeader": "Ciao membro della comunità ABP!",
"RafflesInfo": "<br>Questa è la pagina della lotteria dedicata a mostrare il nostro apprezzamento nei tuoi confronti per essere un membro attivo della comunità. Organizziamo <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP Community Talks</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, ABP .NET Conference</a> , partecipiamo o sponsorizziamo eventi relativi a .NET in cui regaliamo alcuni regali.<br><br> Puoi seguire questa pagina per vedere le prossime lotterie, parteciparvi o vedere le lotterie precedenti che estraiamo, compresi i vincitori.<br><br> Grazie per essere un membro attivo! Ci vediamo alle prossime lotterie.",
"RafflesInfoTitle": "<span class=\"gradient-community\">Lotterie</span> comunitarie ABP"
"RafflesInfoTitle": "<span class=\"gradient-community\">Lotterie</span> comunitarie ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> supportato.",
"Preview": "Anteprima"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json

@ -255,6 +255,8 @@
"LatestArticles": "Laatste artikels",
"RaffleHeader": "Hallo ABP Community-lid!",
"RafflesInfo": "<br>Dit is de loterijpagina die bedoeld is om onze waardering voor u te tonen omdat u een actief lid van de community bent. We houden <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP Community Talks</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, ABP .NET Conference</a> , wonen of sponsoren de .NET-gerelateerde evenementen bij waarin we een aantal geschenken weggeven.<br><br> U kunt deze pagina volgen om de komende loterijen te zien, deze bij te wonen of eerdere loterijen te zien die we trekken, inclusief de winnaars.<br><br> Bedankt dat u een actief lid bent! Tot ziens bij de komende loterijen.",
"RafflesInfoTitle": "ABP- <span class=\"gradient-community\">gemeenschaploterijen</span>"
"RafflesInfoTitle": "ABP- <span class=\"gradient-community\">gemeenschaploterijen</span>",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> ondersteund.",
"Preview": "Voorbeeld"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json

@ -255,6 +255,8 @@
"LatestArticles": "ostatnie artykuły",
"RaffleHeader": "Witaj, członku społeczności ABP!",
"RafflesInfo": "<br>To jest strona loterii, której celem jest wyrażenie naszego uznania dla Ciebie za bycie aktywnym członkiem społeczności. Prowadzimy <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">rozmowy społecznościowe ABP</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, konferencje ABP .NET</a> , uczestniczymy lub sponsorujemy wydarzenia związane z .NET, podczas których rozdajemy prezenty.<br><br> Możesz śledzić tę stronę, aby zobaczyć nadchodzące loterie, wziąć w nich udział lub zobaczyć poprzednie loterie, które losowaliśmy, w tym zwycięzców.<br><br> Dziękujemy za bycie aktywnym członkiem! Do zobaczenia w nadchodzących loteriach.",
"RafflesInfoTitle": "<span class=\"gradient-community\">Loterie</span> społecznościowe ABP"
"RafflesInfoTitle": "<span class=\"gradient-community\">Loterie</span> społecznościowe ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> utrzymany.",
"Preview": "Zapowiedź"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json

@ -255,6 +255,8 @@
"LatestArticles": "Artigos Mais Recentes",
"RaffleHeader": "Olá, membro da comunidade ABP!",
"RafflesInfo": "<br>Esta é a página do sorteio dedicada a mostrar nosso agradecimento por você ser um membro ativo da comunidade. Fazemos <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP Community Talks</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, ABP .NET Conference</a> , participamos ou patrocinamos eventos relacionados ao .NET nos quais distribuímos alguns presentes.<br><br> Você pode seguir esta página para ver os próximos sorteios, participar deles ou ver os sorteios anteriores que sorteamos, incluindo os vencedores.<br><br> Obrigado por ser um membro ativo! Nos vemos nos próximos sorteios.",
"RafflesInfoTitle": "<span class=\"gradient-community\">Sorteios</span> da Comunidade ABP"
"RafflesInfoTitle": "<span class=\"gradient-community\">Sorteios</span> da Comunidade ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> suportado.",
"Preview": "Visualização"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json

@ -255,6 +255,8 @@
"LatestArticles": "ultimele articole",
"RaffleHeader": "Bună ziua, membru al comunității ABP!",
"RafflesInfo": "<br>Aceasta este pagina de tombolă dedicată pentru a arăta aprecierea noastră față de dvs. pentru că sunteți un membru activ al comunității. Facem <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP Community Talks</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, ABP .NET Conference</a> , participăm sau sponsorizăm evenimentele legate de .NET în care oferim câteva cadouri.<br><br> Puteți urmări această pagină pentru a vedea tombolele viitoare, a participa la ele sau pentru a vedea tombolele anterioare pe care le extragem, inclusiv câștigătorii.<br><br> Vă mulțumim că sunteți un membru activ! Ne vedem la tombolele viitoare.",
"RafflesInfoTitle": "<span class=\"gradient-community\">Tombole</span> comunitare ABP"
"RafflesInfoTitle": "<span class=\"gradient-community\">Tombole</span> comunitare ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> sprijinit.",
"Preview": "previzualizare"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ru.json

@ -258,6 +258,8 @@
"LatestArticles": "Последние статьи",
"RaffleHeader": "Привет, участник сообщества ABP!",
"RafflesInfo": "<br>Эта страница розыгрыша посвящена тому, чтобы выразить нашу признательность вам за то, что вы являетесь активным членом сообщества. <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">Обсуждения сообщества ABP</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">,Конференция ABP .NET</a>, посещать или спонсировать мероприятия, связанные с .NET, на которых мы раздаем подарки. <br><br>Вы можете подписаться на эту страницу, чтобы увидеть предстоящие розыгрыши, посетить их или просмотреть предыдущие розыгрыши, которые мы проводим, включая победителей. <br><br>Спасибо за активное участие! ",
"RafflesInfoTitle": "Сообщество АБП <span class=\"gradient-community\">Розыгрыши</span>"
"RafflesInfoTitle": "Сообщество АБП <span class=\"gradient-community\">Розыгрыши</span>",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> поддерживается.",
"Preview": "Предварительный просмотр"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json

@ -255,6 +255,8 @@
"LatestArticles": "Najnovšie články",
"RaffleHeader": "Dobrý deň, člen komunity ABP!",
"RafflesInfo": "<br>Toto je stránka žrebovania venovaná na vyjadrenie nášho uznania vám za to, že ste aktívnym členom komunity. Robíme <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP Community Talks</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, ABP .NET konferenciu</a> , zúčastňujeme sa alebo sponzorujeme podujatia súvisiace s .NET, na ktorých rozdávame nejaké darčeky.<br><br> Na tejto stránke si môžete pozrieť pripravované tomboly, zúčastniť sa ich alebo si pozrieť predchádzajúce žrebovanie, ktoré vyžrebujeme vrátane výhercov.<br><br> Ďakujeme, že ste aktívnym členom! Vidíme sa v najbližšom žrebovaní.",
"RafflesInfoTitle": "Komunitné <span class=\"gradient-community\">tomboly</span> ABP"
"RafflesInfoTitle": "Komunitné <span class=\"gradient-community\">tomboly</span> ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> podporované.",
"Preview": "Náhľad"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json

@ -255,6 +255,8 @@
"LatestArticles": "Najnovejši članki",
"RaffleHeader": "Pozdravljeni član skupnosti ABP!",
"RafflesInfo": "<br>To je stran za nagradno igro, namenjena izkazovanju naše hvaležnosti do vas, ker ste aktivni član skupnosti. Izvajamo <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP Community Talks</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, ABP .NET Conference</a> , se udeležujemo ali sponzoriramo dogodke, povezane z .NET, na katerih podarimo nekaj daril.<br><br> To stran lahko spremljate in si ogledate prihajajoče nagradne igre, se jih udeležite ali si ogledate prejšnje nagradne igre, ki smo jih izžrebali, vključno z zmagovalci.<br><br> Hvala, ker ste aktivni član! Se vidimo na prihajajočih nagradnih igrah.",
"RafflesInfoTitle": "<span class=\"gradient-community\">Žrebanje</span> skupnosti ABP"
"RafflesInfoTitle": "<span class=\"gradient-community\">Žrebanje</span> skupnosti ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> podprt.",
"Preview": "Predogled"
}
}

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

@ -189,6 +189,8 @@
"Layout_MetaDescription": "ABP Topluluğu, insanların ABP çerçevesi hakkında paylaşımlarda bulunabileceği ve projeleri takip edebileceği bir ortamdır.",
"Index_Page_CommunityIntroduction": "Burası ABP Çerçevesi, .NET ve yazılım geliştirme için bir merkezdir. Makaleleri okuyabilir, eğitim videolarını izleyebilir, ABP'nin gelişim süreci ve ABP ile ilgili etkinlikler hakkında bilgi alabilir, diğer geliştiricilere yardımcı olabilir ve uzmanlığınızı ABP topluluğu ile paylaşabilirsiniz.",
"IConsentToMedium": "Bu yazının https://medium.com/volosoft adresinde yayınlanmasına izin veriyorum.",
"DiscordPageTitle": "ABP Discord Topluluğu"
"DiscordPageTitle": "ABP Discord Topluluğu",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> destekleniyor.",
"Preview": "Ön izleme"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json

@ -255,6 +255,8 @@
"LatestArticles": "Bài viết mới nhất",
"RaffleHeader": "Xin chào Thành viên Cộng đồng ABP!",
"RafflesInfo": "<br>Đây là trang xổ số dành riêng để thể hiện sự đánh giá cao của chúng tôi đối với bạn vì đã trở thành Thành viên cộng đồng tích cực. Chúng tôi tổ chức <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">các buổi nói chuyện cộng đồng ABP</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">, Hội nghị ABP .NET</a> , tham dự hoặc tài trợ cho các sự kiện liên quan đến .NET mà trong đó chúng tôi tặng một số quà tặng.<br><br> Bạn có thể theo dõi trang này để xem các cuộc xổ số sắp tới, tham dự hoặc xem các cuộc xổ số trước đây mà chúng tôi rút ra bao gồm cả những người chiến thắng.<br><br> Cảm ơn bạn đã là thành viên tích cực! Hẹn gặp lại các bạn trong đợt xổ số sắp tới.",
"RafflesInfoTitle": "<span class=\"gradient-community\">Xổ</span> số cộng đồng ABP"
"RafflesInfoTitle": "<span class=\"gradient-community\">Xổ</span> số cộng đồng ABP",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> được hỗ trợ.",
"Preview": "Xem trước"
}
}

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

@ -255,6 +255,8 @@
"LatestArticles": "最新文章",
"RaffleHeader": "你好,ABP 社区成员!",
"RafflesInfo": "<br>这是一个抽奖页面,旨在感谢您成为活跃的社区成员。我们举办 <a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP 社区讲座</a> <a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">、ABP .NET 会议</a>,参加或赞助与 .NET 相关的活动,并在活动中赠送一些礼品。<br><br>您可以关注此页面,查看即将举行的抽奖活动、参加这些活动或查看我们以前举行的抽奖活动(包括获奖者)。<br><br>感谢您成为我们的活跃成员!在即将举行的抽奖活动中再见。",
"RafflesInfoTitle": "ABP 社区<span class=\"gradient-community\">来抽奖</span"
"RafflesInfoTitle": "ABP 社区<span class=\"gradient-community\">来抽奖</span",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 支持的。",
"Preview": "预览"
}
}

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

@ -255,6 +255,8 @@
"LatestArticles": "最新的文章",
"RaffleHeader": "ABP 社群成員您好!",
"RafflesInfo": "<br>這是抽獎頁面,旨在表達我們對您作為活躍社區成員的感謝。我們舉辦<a target=\"_blank\" class=\"fw-6 \" href=\"https://community.abp.io/events\">ABP 社群講座</a><a target=\"_blank\" class=\"fw-6\" href=\"https://abp.io/conference/2023\">、ABP .NET 會議</a>、參加或贊助 .NET 相關活動,並在活動中贈送一些禮物。<br><br>您可以關注此頁面查看即將舉行的抽獎活動、參加抽獎活動或查看我們之前抽獎的抽獎活動(包括獲獎者)。<br><br>感謝您成為活躍會員!在即將到來的抽獎活動中再見。",
"RafflesInfoTitle": "ABP 社區<span class=\"gradient-community\">萊佛士</span>"
"RafflesInfoTitle": "ABP 社區<span class=\"gradient-community\">萊佛士</span>",
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> 支持的。",
"Preview": "预览"
}
}

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

@ -353,6 +353,8 @@
"BuildSolutionsWithAbp": "Build maintainable .NET solutions by following software development best practices using ABP.",
"BuyOnAmazon": "Buy on Amazon",
"BuyOnPackt": "Buy on Packt",
"BuyOnDangDang": "Buy on DangDang",
"BuyOnJD": "Buy on JD",
"Discounted": "Discounted",
"MasteringAbpFramework_Book_KeyFeatures": "Key Features",
"MasteringAbpFramework_Book_Key_Features_Description_1": "Build robust, maintainable, modular, and scalable software solutions using ABP Framework.",

2
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json

@ -353,6 +353,8 @@
"BuildSolutionsWithAbp": "使用 ABP,遵循软件开发最佳实践,构建可维护的 .NET 解决方案。",
"BuyOnAmazon": "在亚马逊上购买",
"BuyOnPackt": "在 Packt 购买",
"BuyOnDangDang": "在当当网购买",
"BuyOnJD": "在京东上购买",
"Discounted": "折扣",
"MasteringAbpFramework_Book_KeyFeatures": "主要功能",
"MasteringAbpFramework_Book_Key_Features_Description_1": "使用 ABP 框架构建稳健、可维护、模块化和可扩展的软件解决方案。",

2
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hant.json

@ -353,6 +353,8 @@
"BuildSolutionsWithAbp": "使用 ABP 遵循軟體開發最佳實務來建立可維護的 .NET 解決方案。",
"BuyOnAmazon": "在亞馬遜上購買",
"BuyOnPackt": "在 Packt 上購買",
"BuyOnDangDang": "在當當上購買",
"BuyOnJD": "在京東上購買",
"Discounted": "折扣",
"MasteringAbpFramework_Book_KeyFeatures": "主要特徵",
"MasteringAbpFramework_Book_Key_Features_Description_1": "使用 ABP 框架建構健壯、可維護、模組化和可擴展的軟體解決方案。",

72
docs/en/Community-Articles/2024-01-04-CLI-Online-Translate/Post.md

@ -0,0 +1,72 @@
# Use Deepl to translate localization files
Translating localized text during the development of ABP modules is a boring job. For this reason, we have added the `translate` command to the CLI tool to translate the localization files, but it still requires manual translation of texts by the developer.
Now, we introduce a new way to translate the target language, which is to use the [Deepl](https://www.deepl.com/translator) translation service to translate the target language.
You can use the `translate --online` command in your module directory to translate the target language.
For example, if you have added all `en` localization texts to your module, and you want to translate them to `zh-Hans`, you can run the following command:
```
abp translate -r en -c zh-Hans --online --deepl-auth-key your_auth_key
```
* `-r` parameter is used to specify the source language. It is usually a localized file that you have created.
* `-c` parameter is used to specify the target language. It is usually a language that you want to translate.
* `--online` parameter is used to indicate that the translation is performed from the Deepl service.
* `--deepl-auth-key` parameter is the API key of your Deepl account. You can get it from [here](https://support.deepl.com/hc/en-us/articles/360020695820-Authentication-Key).
The output of the above command is as follows:
```
ABP CLI 8.0.0
Abp translate online...
Target culture: zh-Hans
Reference culture: en
Create translation: Settings => 设置
Create translation: SuccessfullySaved => 成功保存
Create translation: Permission:SettingManagement => 设置管理
Create translation: Permission:Emailing => 发送电子邮件
Create translation: Permission:EmailingTest => 电子邮件测试
Create translation: Permission:TimeZone => 时区
Create translation: SendTestEmail => 发送测试电子邮件
Create translation: SenderEmailAddress => 发件人电子邮件地址
Create translation: TargetEmailAddress => 目标电子邮件地址
Create translation: Subject => 主题
Create translation: Body => 正文
Create translation: TestEmailSubject => 测试电子邮件 {0}
Create translation: TestEmailBody => 在此测试电子邮件正文信息
Create translation: SuccessfullySent => 成功发送
Create translation: Send => 发送
Create translation: Menu:Emailing => 发送电子邮件
Create translation: Menu:TimeZone => 时区
Create translation: DisplayName:Timezone => 时区
Create translation: TimezoneHelpText => 此设置用于应用程序范围或基于租户的设置。
Create translation: SmtpHost => 主机
Create translation: SmtpPort => 端口
Create translation: SmtpUserName => 用户名
Create translation: SmtpPassword => 密码
Create translation: SmtpDomain => 域名
Create translation: SmtpEnableSsl => 启用 ssl
Create translation: SmtpUseDefaultCredentials => 使用默认凭据
Create translation: DefaultFromAddress => 默认地址
Create translation: DefaultFromDisplayName => 显示名称的默认值
Create translation: Feature:SettingManagementGroup => 设置管理
Create translation: Feature:SettingManagementEnable => 启用设置管理
Create translation: Feature:SettingManagementEnableDescription => 在应用程序中启用设置管理系统。
Create translation: Feature:AllowChangingEmailSettings => 允许更改电子邮件设置。
Create translation: Feature:AllowChangingEmailSettingsDescription => 允许更改电子邮件设置。
Write translation json to setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/zh-Hans.json.
```
The generated `zh-Hans.json` as follow:
![zh-Hans.json](deepl.jpg)
In this example, It only translates one `en.json` to `zh-Hans.json`, but if there are multiple `en.json` files in the module, it will translate all `en.json` files to `zh-Hans.json`.
Of course, the translation is not always correct, you can update the translation in the generated `zh-Hans.json` files.
Enjoy it!

BIN
docs/en/Community-Articles/2024-01-04-CLI-Online-Translate/deepl.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

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

@ -127,6 +127,29 @@ abp bundle
> **Note**: Before starting the application, run `abp install-libs` command in your Web directory to restore the client-side libraries. This will populate the `libs` folder.
{{ if UI == "BlazorServer" }}
> **Important:** The `.AuthServer` application serves as the **Authentication Server** for the `.Blazor` application. It is essential to have the `.AuthServer` application running in the background to ensure the proper functioning of the `.Blazor` application.
To do this, open terminal in `.AuthServer` project folder and run the following command.
````bash
dotnet run
````
Once the `.AuthServer`application has started, it is time to run `.HttpApi.Host` application.
> **Important:** Prior to launching the `.Blazor` project, it is essential to execute the `.HttpApi.Host` application as well.
To do this, open terminal in `.HttpApi.Host` project folder and run the following command.
````bash
dotnet run
````
Once the `.AuthServer` and `.HttpApi.Host` applications has started, you can proceed to run the `.Blazor` project.
{{ end # UI }}
{{ if Tiered == "Yes" }}
> Tiered solutions use **Redis** as the distributed cache. Ensure that it is installed and running in your local computer. If you are using a remote Redis Server, set the configuration in the `appsettings.json` files of the projects below.
@ -195,6 +218,16 @@ You can see the application APIs and test them here. Get [more info](https://swa
### Running the Blazor Application (Client Side)
> **Important:** The `.HttpApi.Host` application serves as the **Authentication Server** for the `.Blazor` application. It is essential to have the `.HttpApi.Host` application running in the background to ensure the proper functioning of the `.Blazor` application.
To do this, you can open terminal in `.HttpApi.Host` project folder and run the following command.
````bash
dotnet run
````
Once the `.HttpApi.Host` application has started, you can proceed to run the `.Blazor` application.
Ensure that the `.Blazor` project is the startup project and run the application.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.

2
docs/en/Migration-Guides/Abp-8_0.md

@ -181,7 +181,7 @@ You can see the following list of NuGet libraries that have been upgraded with .
| Dapper | 2.0.123 | 2.1.21 |
| Dapr.AspNetCore | 1.9.0 | 1.12.0 |
| Dapr.Client | 1.9.0 | 1.12.0 |
| Devart.Data.Oracle.EFCore | 10.1.134.7 | 10.1.151.7 |
| Devart.Data.Oracle.EFCore | 10.1.134.7 | 10.3.10.8 |
| DistributedLock.Core | 1.0.4 | 1.0.5 |
| DistributedLock.Redis | 1.0.1 | 1.0.2 |
| EphemeralMongo.Core | 1.1.0 | 1.1.3 |

85
docs/en/Migration-Guides/Abp-8_1.md

@ -0,0 +1,85 @@
# ABP Version 8.1 Migration Guide
This document is a guide for upgrading ABP v8.0 solutions to ABP v8.1. There are some changes in this version that may affect your applications, please read it carefully and apply the necessary changes to your application.
## Added `NormalizedName` property to `Tenant`
The `Tenant` entity has a new property called `NormalizedName`. It is used to find/cache a tenant by its name in a case-insensitive way.
This property is automatically set when a tenant is created or updated. It gets the normalized name of the tenant name by `UpperInvariantTenantNormalizer(ITenantNormalizer)` service. You can implement this service to change the normalization logic.
### `ITenantStore`
The `ITenantStore` will use the `NormalizedName` parameter to get tenants, Please use the `ITenantNormalizer` to normalize the tenant name before calling the `ITenantStore` methods.
### Update `NormalizedName` in `appsettings.json`
If your tenants defined in the `appsettings.json` file, you should add the `NormalizedName` property to your tenants.
````json
"Tenants": [
{
"Id": "446a5211-3d72-4339-9adc-845151f8ada0",
"Name": "tenant1",
"NormalizedName": "TENANT1" // <-- Add this property
},
{
"Id": "25388015-ef1c-4355-9c18-f6b6ddbaf89d",
"Name": "tenant2",
"NormalizedName": "TENANT2", // <-- Add this property
"ConnectionStrings": {
"Default": "...tenant2's db connection string here..."
}
}
]
````
### Update `NormalizedName` in the database
Please add a sql script to your migration to set the `NormalizedName` property of the existing tenants. You can use the following script:
> This script is for the SQL Server database. You can change it for your database.
> The table name `SaasTenants` is used for ABP commercial Saas module. `AbpTenants` is for the ABP open-source Tenant Management module.
```sql
UPDATE SaasTenants SET NormalizedName = UPPER(Name) WHERE NormalizedName IS NULL OR NormalizedName = ''
```
```csharp
/// <inheritdoc />
public partial class Add_NormalizedName : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "NormalizedName",
table: "SaasTenants",
type: "nvarchar(64)",
nullable: false,
defaultValue: "");
migrationBuilder.Sql("UPDATE SaasTenants SET NormalizedName = UPPER(Name) WHERE NormalizedName IS NULL OR NormalizedName = ''");
migrationBuilder.CreateIndex(
name: "IX_SaasTenants_NormalizedName",
table: "SaasTenants",
column: "NormalizedName");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_SaasTenants_NormalizedName",
table: "SaasTenants");
migrationBuilder.DropColumn(
name: "NormalizedName",
table: "SaasTenants");
}
}
```
See https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/managing?tabs=dotnet-core-cli#adding-raw-sql to learn how to add raw SQL to migrations.

6
docs/en/Multi-Tenancy.md

@ -394,6 +394,8 @@ app.UseMultiTenancy();
`ITenantStore` is used to get the tenant configuration from a data source.
> Tenant names are not case-sensitive. `ITenantStore` will use the `NormalizedName` parameter to get tenants, You need to use `ITenantNormalizer` to normalize tenant names.
#### Tenant Management Module
The [tenant management module](Modules/Tenant-Management) is **included in the startup templates** and implements the `ITenantStore` interface to get the tenants and their configuration from a database. It also provides the necessary functionality and UI to manage the tenants and their connection strings.
@ -408,11 +410,13 @@ The [tenant management module](Modules/Tenant-Management) is **included in the s
"Tenants": [
{
"Id": "446a5211-3d72-4339-9adc-845151f8ada0",
"Name": "tenant1"
"Name": "tenant1",
"NormalizedName": "TENANT1"
},
{
"Id": "25388015-ef1c-4355-9c18-f6b6ddbaf89d",
"Name": "tenant2",
"NormalizedName": "TENANT2",
"ConnectionStrings": {
"Default": "...tenant2's db connection string here..."
}

197
docs/en/UI/Angular/HTTP-Error-Handling.md

@ -0,0 +1,197 @@
# HTTP Error Handling
When the `RestService` is used, all HTTP errors are reported to the [`HttpErrorReporterService`](./HTTP-Error-Reporter-Service), and then `ErrorHandler`, a service exposed by the `@abp/ng.theme.shared` package automatically handles the errors.
## Custom HTTP Error Handler
### Function Method `Deprecated`
A custom HTTP error handler can be registered to an injection token named `HTTP_ERROR_HANDLER`. If a custom handler function is registered, the `ErrorHandler` executes that function.
See an example:
```ts
// http-error-handler.ts
import { ContentProjectionService, PROJECTION_STRATEGY } from '@abp/ng.core';
import { ToasterService } from '@abp/ng.theme.shared';
import { HttpErrorResponse } from '@angular/common/http';
import { Injector } from '@angular/core';
import { of, EMPTY } from 'rxjs';
import { Error404Component } from './error404/error404.component';
export function handleHttpErrors(injector: Injector, httpError: HttpErrorResponse) {
if (httpError.status === 400) {
const toaster = injector.get(ToasterService);
toaster.error(httpError.error?.error?.message || 'Bad request!', '400');
return EMPTY;
}
if (httpError.status === 404) {
const contentProjection = injector.get(ContentProjectionService);
contentProjection.projectContent(PROJECTION_STRATEGY.AppendComponentToBody(Error404Component));
return EMPTY;
}
return of(httpError);
}
// app.module.ts
import { Error404Component } from './error404/error404.component';
import { handleHttpErrors } from './http-error-handling';
import { HTTP_ERROR_HANDLER, ... } from '@abp/ng.theme.shared';
@NgModule({
// ...
providers: [
// ...
{ provide: HTTP_ERROR_HANDLER, useValue: handleHttpErrors }
],
declarations: [
//...
Error404Component],
})
export class AppModule {}
```
In the example above:
- Created a function named `handleHttpErrors` and defined as value of the `HTTP_ERROR_HANDLER` provider in app.module. After this, the function executes when an HTTP error occurs.
- 400 bad request errors is handled. When a 400 error occurs.
- Since `of(httpError)` is returned at bottom of the `handleHttpErrors`, the `ErrorHandler` will handle the HTTP errors except 400 and 404 errors.
**Note 1:** If you put `return EMPTY` to next line of handling an error, default error handling will not work for that error. [EMPTY](https://rxjs.dev/api/index/const/EMPTY) can be imported from `rxjs`.
```ts
export function handleHttpErrors(
injector: Injector,
httpError: HttpErrorResponse
) {
if (httpError.status === 403) {
// handle 403 errors here
return EMPTY; // put return EMPTY to skip default error handling
}
}
```
**Note 2:** If you put `return of(httpError)`, default error handling will work.
- `of` is a function. It can be imported from `rxjs`.
- `httpError` is the second parameter of the error handler function which is registered to the `HTTP_ERROR_HANDLER` provider. Type of the `httpError` is `HttpErrorResponse`.
```ts
import { of } from "rxjs";
export function handleHttpErrors(
injector: Injector,
httpError: HttpErrorResponse
) {
if (httpError.status === 500) {
// handle 500 errors here
}
// you can return the of(httpError) at bottom of the function to run the default handler of ABP for HTTP errors that you didn't handle above.
return of(httpError);
}
```
### Service Method
You can provide **more than one handler** with services, a custom HTTP error handler service can be registered with injection token named **`CUSTOM_ERROR_HANDLERS`**. ABP has some default [error handlers](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/theme-shared/src/lib/providers/error-handlers.provider.ts).
### How To Add New Handler Service
ABP error handler services are implements the interface of **CustomHttpErrorHandlerService**.
**Interface of `CUSTOM_ERROR_HANDLERS`**
```ts
interface CustomHttpErrorHandlerService {
readonly priority: number;
canHandle(error: unknown): boolean;
execute(): void;
}
```
- **`priority`** ABP sorts the services according to the number of the priority variable. Higher priority will be checked first.
- **`canHandle`** Check if the service can handle the error. Returns boolean.
- **`execute`** If the service can handle the error, then run the execute method.
**In Summary**
- Services are sorted by their priority number.
- Start from highest priority service and run canHandle() method. Pick the service if can handle the error, if not check next service.
- If the service found, run the execute method of a service. Done.
See an example:
```ts
// custom-error-handler.service.ts
import { inject, Injectable } from "@angular/core";
import { HttpErrorResponse } from "@angular/common/http";
import { CustomHttpErrorHandlerService } from "@abp/ng.theme.shared";
import { CUSTOM_HTTP_ERROR_HANDLER_PRIORITY } from "@abp/ng.theme.shared";
import { ToasterService } from "@abp/ng.theme.shared";
@Injectable({ providedIn: "root" })
export class MyCustomErrorHandlerService
implements CustomHttpErrorHandlerService
{
// You can write any number here, ex: 9999
readonly priority = CUSTOM_HTTP_ERROR_HANDLER_PRIORITY.veryHigh;
protected readonly toaster = inject(ToasterService);
private error: HttpErrorResponse | undefined = undefined;
// What kind of error should be handled by this service? You can decide it in this method. If error is suitable to your case then return true; otherwise return false.
canHandle(error: unknown): boolean {
if (error instanceof HttpErrorResponse && error.status === 400) {
this.error = error;
return true;
}
return false;
}
// If this service is picked from ErrorHandler, this execute method will be called.
execute() {
this.toaster.error(
this.error.error?.error?.message || "Bad request!",
"400"
);
}
}
```
```ts
// app.module.ts
import { CUSTOM_ERROR_HANDLERS, ... } from '@abp/ng.theme.shared';
import { MyCustomErrorHandlerService } from './custom-error-handler.service';
@NgModule({
// ...
providers: [
// ...
{
provide: CUSTOM_ERROR_HANDLERS,
useExisting: MyCustomErrorHandlerService,
multi: true,
}
]
})
export class AppModule {}
```
In the example above:
- Created a service named `MyCustomErrorHandlerService`, and provided via `useExisting` key because we dont want another instance of it. And set `multi` key to true because ABP default error handlers are also provided with **CUSTOM_ERROR_HANDLERS** injection token.
- 400 errors are handled from custom `MyCustomErrorHandlerService`. When a 400 error occurs, backend error message will be displayed as shown below:
![custom-error-handler-toaster-message](images/custom-error-handler-toaster-message.jpg)
### Notes
- If your service cannot handle the error. Then ABP will check the next Error Service.
- If none of the service handle the error. Then basic confirmation message about the error will be shown to the user.
- You can provide more than one service, with CUSTOM_ERROR_HANDLER injection token.
- If you want your custom service to be evaluated (checked) earlier, set the priority variable high.

144
docs/en/UI/Angular/HTTP-Requests.md

@ -1,7 +1,5 @@
# How to Make HTTP Requests
## About HttpClient
Angular has the amazing [HttpClient](https://angular.io/guide/http) for communication with backend services. It is a layer on top and a simplified representation of [XMLHttpRequest Web API](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest). It also is the recommended agent by Angular for any HTTP request. There is nothing wrong with using the `HttpClient` in your ABP project.
@ -23,14 +21,10 @@ Although clear and flexible, handling errors this way is repetitive work, even w
An `HttpInterceptor` is able to catch `HttpErrorResponse` and can be used for a centralized error handling. Nevertheless, cases where default error handler, therefore the interceptor, must be disabled require additional work and comprehension of Angular internals. Check [this issue](https://github.com/angular/angular/issues/20203) for details.
## RestService
ABP core module has a utility service for HTTP requests: `RestService`. Unless explicitly configured otherwise, it catches HTTP errors and dispatches a `RestOccurError` action. This action is then captured by the `ErrorHandler` introduced by the `ThemeSharedModule`. Since you should already import this module in your app, when the `RestService` is used, all HTTP errors get automatically handled by default.
### Getting Started with RestService
In order to use the `RestService`, you must inject it in your class as a dependency.
@ -48,11 +42,9 @@ class DemoService {
You do not have to provide the `RestService` at module or component/directive level, because it is already **provided in root**.
### How to Make a Request with RestService
You can use the `request` method of the `RestService` is for HTTP requests. Here is an example:
You can use the `request` method of the `RestService` is for HTTP requests. Here is an example:
```js
getFoo(id: number) {
@ -65,8 +57,6 @@ getFoo(id: number) {
}
```
The `request` method always returns an `Observable<T>`. Therefore you can do the following wherever you use `getFoo` method:
```js
@ -79,12 +69,8 @@ doSomethingWithFoo(id: number) {
}
```
**You do not have to worry about unsubscription.** The `RestService` uses `HttpClient` behind the scenes, so every observable it returns is a finite observable, i.e. it closes subscriptions automatically upon success or error.
As you see, `request` method gets a request options object with `Rest.Request<T>` type. This generic type expects the interface of the request body. You may pass `null` when there is no body, like in a `GET` or a `DELETE` request. Here is an example where there is one:
```js
@ -99,11 +85,7 @@ postFoo(body: Foo) {
}
```
You may [check here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L23) for complete `Rest.Request<T>` type, which has only a few changes compared to [HttpRequest](https://angular.io/api/common/http/HttpRequest) class in Angular.
You may [check here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L23) for complete `Rest.Request<T>` type, which has only a few changes compared to [HttpRequest](https://angular.io/api/common/http/HttpRequest) class in Angular.
### How to Disable Default Error Handler of RestService
@ -120,8 +102,6 @@ deleteFoo(id: number) {
}
```
`skipHandleError` config option, when set to `true`, disables the error handler and the returned observable starts throwing an error that you can catch in your subscription.
```js
@ -137,14 +117,10 @@ removeFooFromList(id: number) {
}
```
### How to Get a Specific API Endpoint From Application Config
Another nice config option that `request` method receives is `apiName` (available as of v2.4), which can be used to get a specific module endpoint from application configuration.
```js
putFoo(body: Foo, id: string) {
const request: Rest.Request<Foo> = {
@ -157,8 +133,6 @@ putFoo(body: Foo, id: string) {
}
```
`putFoo` above will request `https://localhost:44305/api/some/path/to/foo/{id}` as long as the environment variables are as follows:
```js
@ -167,19 +141,17 @@ putFoo(body: Foo, id: string) {
export const environment = {
apis: {
default: {
url: 'https://localhost:44305',
url: "https://localhost:44305",
},
foo: {
url: 'https://localhost:44305/api/some/path/to/foo',
url: "https://localhost:44305/api/some/path/to/foo",
},
},
/* rest of the environment variables here */
}
};
```
### How to Observe Response Object or HTTP Events Instead of Body
`RestService` assumes you are generally interested in the body of a response and, by default, sets `observe` property as `'body'`. However, there may be times you are rather interested in something else, such as a custom proprietary header. For that, the `request` method receives `observe` property in its config object.
@ -202,104 +174,6 @@ getSomeCustomHeaderValue() {
You may find `Rest.Observe` enum [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L10).
## HTTP Error Handling
When the `RestService` is used, all HTTP errors are reported to the [`HttpErrorReporterService`](./HTTP-Error-Reporter-Service), and then `ErrorHandler`, a service exposed by the `@abp/ng.theme.shared` package automatically handles the errors.
### Custom HTTP Error Handler
A custom HTTP error handler can be registered to an injection token named `HTTP_ERROR_HANDLER`. If a custom handler function is registered, the `ErrorHandler` executes that function.
See an example:
```js
// http-error-handler.ts
import { ContentProjectionService, PROJECTION_STRATEGY } from '@abp/ng.core';
import { ToasterService } from '@abp/ng.theme.shared';
import { HttpErrorResponse } from '@angular/common/http';
import { Injector } from '@angular/core';
import { throwError } from 'rxjs';
import { Error404Component } from './error404/error404.component';
export function handleHttpErrors(injector: Injector, httpError: HttpErrorResponse) {
if (httpError.status === 400) {
const toaster = injector.get(ToasterService);
toaster.error(httpError.error?.error?.message || 'Bad request!', '400');
return;
}
if (httpError.status === 404) {
const contentProjection = injector.get(ContentProjectionService);
contentProjection.projectContent(PROJECTION_STRATEGY.AppendComponentToBody(Error404Component));
return;
}
return throwError(httpError);
}
// app.module.ts
import { Error404Component } from './error404/error404.component';
import { handleHttpErrors } from './http-error-handling';
import { HTTP_ERROR_HANDLER, ... } from '@abp/ng.theme.shared';
@NgModule({
// ...
providers: [
// ...
{ provide: HTTP_ERROR_HANDLER, useValue: handleHttpErrors }
],
declarations: [
//...
Error404Component],
})
export class AppModule {}
```
In the example above:
- Created a function named `handleHttpErrors` and defined as value of the `HTTP_ERROR_HANDLER` provider in app.module. After this, the function executes when an HTTP error occurs.
- 400 bad request errors is handled. When a 400 error occurs, backend error message will be displayed as shown below:
![custom-error-handler-toaster-message](images/custom-error-handler-toaster-message.jpg)
- 404 not found errors is handled. When a 404 error occurs, `Error404Component` will be appended to the `<body>` as shown below:
![custom-error-handler-404-component](images/custom-error-handler-404-component.jpg)
- Since `throwError(httpError)` is returned at bottom of the `handleHttpErrors`, the `ErrorHandler` will handle the HTTP errors except 400 and 404 errors.
**Note 1:** If you put `return` to next line of handling an error, default error handling will not work for that error.
```js
export function handleHttpErrors(injector: Injector, httpError: HttpErrorResponse) {
if (httpError.status === 403) {
// handle 403 errors here
return; // put return to skip default error handling
}
}
```
**Note 2:** If you put `return throwError(httpError)`, default error handling will work.
- `throwError` is a function. It can be imported from `rxjs`.
- `httpError` is the second parameter of the error handler function which is registered to the `HTTP_ERROR_HANDLER` provider. Type of the `httpError` is `HttpErrorResponse`.
```js
import { throwError } from 'rxjs';
export function handleHttpErrors(injector: Injector, httpError: HttpErrorResponse) {
if (httpError.status === 500) {
// handle 500 errors here
return;
}
// you can return the throwError(httpError) at bottom of the function to run the default handler of ABP for HTTP errors that you didn't handle above.
return throwError(httpError)
}
```
### How to Skip HTTP interceptors and ABP headers
The ABP Framework adds several HTTP headers to the HttpClient, such as the "Auth token" or "tenant Id".
@ -309,4 +183,8 @@ The ABP Http interceptors check the value of the `IS_EXTERNAL_REQUEST` token. If
The `ExternalHttpClient` extends from `HTTPClient` and sets the `IS_EXTERNAL_REQUEST` context token to true.
When you are using `ExternalHttpClient` as HttpClient in your components, it does not add ABP-specific headers.
Note: With `IS_EXTERNAL_REQUEST` or without it, ABP loading service works.
Note: With `IS_EXTERNAL_REQUEST` or without it, ABP loading service works.
## See Also
- [HTTP Error Handling / Customization](./HTTP-Error-Handling)

15
docs/en/docs-nav.json

@ -1053,7 +1053,16 @@
},
{
"text": "HTTP Requests",
"path": "UI/Angular/HTTP-Requests.md"
"items": [
{
"text": "How to Make HTTP Requests",
"path": "UI/Angular/HTTP-Requests.md"
},
{
"text": "HTTP Error Handling / Customization",
"path": "UI/Angular/HTTP-Error-Handling.md"
}
]
},
{
"text": "Localization",
@ -1173,8 +1182,8 @@
"path": "UI/Angular/Content-Security-Strategy.md"
},
{
"text":"Abp Window Service",
"path":"UI/Angular/Abp-Window-Service.md"
"text": "Abp Window Service",
"path": "UI/Angular/Abp-Window-Service.md"
}
]
},

20
framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorRemoteTenantStore.cs

@ -23,13 +23,13 @@ public class MauiBlazorRemoteTenantStore : ITenantStore, ITransientDependency
Cache = cache;
}
public async Task<TenantConfiguration?> FindAsync(string name)
public async Task<TenantConfiguration?> FindAsync(string normalizedName)
{
var cacheKey = CreateCacheKey(name);
var cacheKey = CreateCacheKey(normalizedName);
var tenantConfiguration = await Cache.GetOrAddAsync(
cacheKey,
async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name))!,
async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(normalizedName))!,
() => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
@ -57,13 +57,13 @@ public class MauiBlazorRemoteTenantStore : ITenantStore, ITransientDependency
return tenantConfiguration;
}
public TenantConfiguration? Find(string name)
public TenantConfiguration? Find(string normalizedName)
{
var cacheKey = CreateCacheKey(name);
var cacheKey = CreateCacheKey(normalizedName);
var tenantConfiguration = Cache.GetOrAdd(
cacheKey,
() => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name)))!,
() => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(normalizedName)))!,
() => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
@ -98,16 +98,16 @@ public class MauiBlazorRemoteTenantStore : ITenantStore, ITransientDependency
return null;
}
return new TenantConfiguration(tenantResultDto.TenantId.Value, tenantResultDto.Name!);
return new TenantConfiguration(tenantResultDto.TenantId.Value, tenantResultDto.Name!, tenantResultDto.NormalizedName!);
}
protected virtual string CreateCacheKey(string tenantName)
protected virtual string CreateCacheKey(string normalizedName)
{
return $"RemoteTenantStore_Name_{tenantName}";
return $"RemoteTenantStore_Name_{normalizedName}";
}
protected virtual string CreateCacheKey(Guid tenantId)
{
return $"RemoteTenantStore_Id_{tenantId:N}";
}
}
}

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

@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Middleware;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
@ -15,7 +16,7 @@ using Volo.Abp.Settings;
namespace Volo.Abp.AspNetCore.MultiTenancy;
public class MultiTenancyMiddleware : IMiddleware, ITransientDependency
public class MultiTenancyMiddleware : AbpMiddlewareBase, ITransientDependency
{
public ILogger<MultiTenancyMiddleware> Logger { get; set; }
@ -38,7 +39,7 @@ public class MultiTenancyMiddleware : IMiddleware, ITransientDependency
_options = options.Value;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
TenantConfiguration? tenant = null;
try

12
framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcRemoteTenantStore.cs

@ -29,9 +29,9 @@ public class MvcRemoteTenantStore : ITenantStore, ITransientDependency
Options = options.Value;
}
public async Task<TenantConfiguration?> FindAsync(string name)
public async Task<TenantConfiguration?> FindAsync(string normalizedName)
{
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(name);
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(normalizedName);
var httpContext = HttpContextAccessor?.HttpContext;
if (httpContext != null && httpContext.Items[cacheKey] is TenantConfigurationCacheItem tenantConfigurationInHttpContext)
@ -42,7 +42,7 @@ public class MvcRemoteTenantStore : ITenantStore, ITransientDependency
var tenantConfiguration = await Cache.GetAsync(cacheKey);
if (tenantConfiguration == null)
{
await TenantAppService.FindTenantByNameAsync(name);
await TenantAppService.FindTenantByNameAsync(normalizedName);
tenantConfiguration = await Cache.GetAsync(cacheKey);
}
@ -79,9 +79,9 @@ public class MvcRemoteTenantStore : ITenantStore, ITransientDependency
return tenantConfiguration?.Value;
}
public TenantConfiguration? Find(string name)
public TenantConfiguration? Find(string normalizedName)
{
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(name);
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(normalizedName);
var httpContext = HttpContextAccessor?.HttpContext;
if (httpContext != null && httpContext.Items[cacheKey] is TenantConfigurationCacheItem tenantConfigurationInHttpContext)
@ -92,7 +92,7 @@ public class MvcRemoteTenantStore : ITenantStore, ITransientDependency
var tenantConfiguration = Cache.Get(cacheKey);
if (tenantConfiguration == null)
{
AsyncHelper.RunSync(async () => await TenantAppService.FindTenantByNameAsync(name));
AsyncHelper.RunSync(async () => await TenantAppService.FindTenantByNameAsync(normalizedName));
tenantConfiguration = Cache.Get(cacheKey);
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/FindTenantResultDto.cs

@ -11,5 +11,7 @@ public class FindTenantResultDto
public string? Name { get; set; }
public string? NormalizedName { get; set; }
public bool IsActive { get; set; }
}

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs

@ -90,13 +90,13 @@ public class AbpModalFooterTagHelperService : AbpTagHelperService<AbpModalFooter
return element.ToHtmlString();
}
protected virtual string GetCancelButton()
protected virtual string GetCancelButton(bool isDestructive = false)
{
var element = new TagBuilder("button");
element.Attributes.Add("type", "button");
element.Attributes.Add("data-bs-dismiss", "modal");
element.AddCssClass("btn");
element.AddCssClass("btn-secondary");
element.AddCssClass("btn-outline-primary");
element.InnerHtml.Append(_localizer["Cancel"]);
return element.ToHtmlString();

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/AbpTenantAppService.cs

@ -9,15 +9,17 @@ namespace Pages.Abp.MultiTenancy;
public class AbpTenantAppService : ApplicationService, IAbpTenantAppService
{
protected ITenantStore TenantStore { get; }
protected ITenantNormalizer TenantNormalizer { get; }
public AbpTenantAppService(ITenantStore tenantStore)
public AbpTenantAppService(ITenantStore tenantStore, ITenantNormalizer tenantNormalizer)
{
TenantStore = tenantStore;
TenantNormalizer = tenantNormalizer;
}
public virtual async Task<FindTenantResultDto> FindTenantByNameAsync(string name)
{
var tenant = await TenantStore.FindAsync(name);
var tenant = await TenantStore.FindAsync(TenantNormalizer.NormalizeName(name)!);
if (tenant == null)
{
@ -29,6 +31,7 @@ public class AbpTenantAppService : ApplicationService, IAbpTenantAppService
Success = true,
TenantId = tenant.Id,
Name = tenant.Name,
NormalizedName = tenant.NormalizedName,
IsActive = tenant.IsActive
};
}
@ -47,6 +50,7 @@ public class AbpTenantAppService : ApplicationService, IAbpTenantAppService
Success = true,
TenantId = tenant.Id,
Name = tenant.Name,
NormalizedName = tenant.NormalizedName,
IsActive = tenant.IsActive
};
}

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs

@ -18,13 +18,16 @@ public class TenantSwitchModalModel : AbpPageModel
public TenantInfoModel Input { get; set; } = default!;
protected ITenantStore TenantStore { get; }
protected ITenantNormalizer TenantNormalizer { get; }
protected AbpAspNetCoreMultiTenancyOptions Options { get; }
public TenantSwitchModalModel(
ITenantStore tenantStore,
ITenantNormalizer tenantNormalizer,
IOptions<AbpAspNetCoreMultiTenancyOptions> options)
{
TenantStore = tenantStore;
TenantNormalizer = tenantNormalizer;
Options = options.Value;
LocalizationResourceType = typeof(AbpUiMultiTenancyResource);
}
@ -45,7 +48,7 @@ public class TenantSwitchModalModel : AbpPageModel
Guid? tenantId = null;
if (!Input.Name.IsNullOrEmpty())
{
var tenant = await TenantStore.FindAsync(Input.Name!);
var tenant = await TenantStore.FindAsync(TenantNormalizer.NormalizeName(Input.Name!)!);
if (tenant == null)
{
throw new UserFriendlyException(L["GivenTenantIsNotExist", Input.Name!]);

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

@ -103,6 +103,13 @@ var abp = abp || {};
abp.libs.sweetAlert.config.default.confirmButtonText = l('Ok');
abp.libs.sweetAlert.config.default.denyButtonText = l('No');
abp.libs.sweetAlert.config.default.cancelButtonText = l('Cancel');
abp.libs.sweetAlert.config.default.buttonsStyling = false;
abp.libs.sweetAlert.config.default.customClass = {
confirmButton: "btn btn-primary",
cancelButton: "btn btn-outline-primary mx-2",
denyButton: "btn btn-outline-primary mx-2"
};
abp.libs.sweetAlert.config.confirm.title = l('AreYouSure');
abp.libs.sweetAlert.config.confirm.confirmButtonText = l('Yes');
abp.libs.sweetAlert.config.confirm.showCancelButton = true;

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

@ -14,6 +14,7 @@ using System.Linq;
using System.Net;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.DataAnnotations;
using Microsoft.AspNetCore.Mvc.RazorPages;
@ -25,6 +26,7 @@ using Volo.Abp.ApiVersioning;
using Volo.Abp.Application;
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
using Volo.Abp.AspNetCore.Mvc.ApiExploring;
using Volo.Abp.AspNetCore.Mvc.ApplicationModels;
using Volo.Abp.AspNetCore.Mvc.Conventions;
using Volo.Abp.AspNetCore.Mvc.DataAnnotations;
using Volo.Abp.AspNetCore.Mvc.DependencyInjection;
@ -176,6 +178,7 @@ public class AbpAspNetCoreMvcModule : AbpModule
context.Services.Replace(ServiceDescriptor.Singleton<IValidationAttributeAdapterProvider, AbpValidationAttributeAdapterProvider>());
context.Services.AddSingleton<ValidationAttributeAdapterProvider>();
context.Services.TryAddEnumerable(ServiceDescriptor.Transient<IActionDescriptorProvider, AbpMvcActionDescriptorProvider>());
context.Services.AddOptions<MvcOptions>()
.Configure<IServiceProvider>((mvcOptions, serviceProvider) =>
{
@ -247,7 +250,7 @@ public class AbpAspNetCoreMvcModule : AbpModule
.Distinct();
AddToApplicationParts(partManager, controllerAssemblies);
var additionalAssemblies = moduleContainer
.Modules
.SelectMany(m => m.GetAdditionalAssemblies())

31
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcActionDescriptorProvider.cs

@ -0,0 +1,31 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
using Volo.Abp.AspNetCore.Filters;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationModels;
public class AbpMvcActionDescriptorProvider : IActionDescriptorProvider
{
public virtual int Order => -1000 + 10;
public virtual void OnProvidersExecuting(ActionDescriptorProviderContext context)
{
}
public virtual void OnProvidersExecuted(ActionDescriptorProviderContext context)
{
foreach (var action in context.Results.Where(x => x is ControllerActionDescriptor).Cast<ControllerActionDescriptor>())
{
var disableAbpFeaturesAttribute = action.ControllerTypeInfo.GetCustomAttribute<DisableAbpFeaturesAttribute>(true);
if (disableAbpFeaturesAttribute != null && disableAbpFeaturesAttribute.DisableMvcFilters)
{
action.FilterDescriptors.RemoveAll(x => x.Filter is ServiceFilterAttribute serviceFilterAttribute &&
typeof(IAbpFilter).IsAssignableFrom(serviceFilterAttribute.ServiceType));
}
}
}
}

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

@ -5,12 +5,13 @@ using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Volo.Abp.Aspects;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.Auditing;
public class AbpAuditActionFilter : IAsyncActionFilter, ITransientDependency
public class AbpAuditActionFilter : IAsyncActionFilter, IAbpFilter, ITransientDependency
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs

@ -5,12 +5,13 @@ using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Volo.Abp.Aspects;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.Auditing;
public class AbpAuditPageFilter : IAsyncPageFilter, ITransientDependency
public class AbpAuditPageFilter : IAsyncPageFilter, IAbpFilter, ITransientDependency
{
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
{

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs

@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
@ -18,7 +19,7 @@ using Volo.Abp.Json;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
public class AbpExceptionFilter : IAsyncExceptionFilter, ITransientDependency
public class AbpExceptionFilter : IAsyncExceptionFilter, IAbpFilter, ITransientDependency
{
public virtual async Task OnExceptionAsync(ExceptionContext context)
{

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs

@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
@ -18,7 +19,7 @@ using Volo.Abp.Json;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
public class AbpExceptionPageFilter : IAsyncPageFilter, ITransientDependency
public class AbpExceptionPageFilter : IAsyncPageFilter, IAbpFilter, ITransientDependency
{
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
{

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeatureActionFilter.cs

@ -2,12 +2,13 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Volo.Abp.Aspects;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
namespace Volo.Abp.AspNetCore.Mvc.Features;
public class AbpFeatureActionFilter : IAsyncActionFilter, ITransientDependency
public class AbpFeatureActionFilter : IAsyncActionFilter, IAbpFilter, ITransientDependency
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs

@ -2,12 +2,13 @@
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Volo.Abp.Aspects;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
namespace Volo.Abp.AspNetCore.Mvc.Features;
public class AbpFeaturePageFilter : IAsyncPageFilter, ITransientDependency
public class AbpFeaturePageFilter : IAsyncPageFilter, IAbpFilter, ITransientDependency
{
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
{

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeatureActionFilter.cs

@ -6,12 +6,13 @@ using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Aspects;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.DependencyInjection;
using Volo.Abp.GlobalFeatures;
namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures;
public class GlobalFeatureActionFilter : IAsyncActionFilter, ITransientDependency
public class GlobalFeatureActionFilter : IAsyncActionFilter, IAbpFilter, ITransientDependency
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeaturePageFilter.cs

@ -6,12 +6,13 @@ using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Aspects;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.DependencyInjection;
using Volo.Abp.GlobalFeatures;
namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures;
public class GlobalFeaturePageFilter : IAsyncPageFilter, ITransientDependency
public class GlobalFeaturePageFilter : IAsyncPageFilter, IAbpFilter, ITransientDependency
{
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
{

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Response/AbpNoContentActionFilter.cs

@ -2,11 +2,12 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.Response;
public class AbpNoContentActionFilter : IAsyncActionFilter, ITransientDependency
public class AbpNoContentActionFilter : IAsyncActionFilter, IAbpFilter, ITransientDependency
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs

@ -4,12 +4,13 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;
namespace Volo.Abp.AspNetCore.Mvc.Uow;
public class AbpUowActionFilter : IAsyncActionFilter, ITransientDependency
public class AbpUowActionFilter : IAsyncActionFilter, IAbpFilter, ITransientDependency
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs

@ -4,13 +4,14 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.AspNetCore.Uow;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;
namespace Volo.Abp.AspNetCore.Mvc.Uow;
public class AbpUowPageFilter : IAsyncPageFilter, ITransientDependency
public class AbpUowPageFilter : IAsyncPageFilter, IAbpFilter, ITransientDependency
{
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
{

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/AbpValidationActionFilter.cs

@ -3,13 +3,14 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Filters;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Reflection;
using Volo.Abp.Validation;
namespace Volo.Abp.AspNetCore.Mvc.Validation;
public class AbpValidationActionFilter : IAsyncActionFilter, ITransientDependency
public class AbpValidationActionFilter : IAsyncActionFilter, IAbpFilter, ITransientDependency
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{

5
framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpSerilogMiddleware.cs

@ -5,6 +5,7 @@ using Microsoft.Extensions.Options;
using Serilog.Context;
using Serilog.Core;
using Serilog.Core.Enrichers;
using Volo.Abp.AspNetCore.Middleware;
using Volo.Abp.Clients;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
@ -13,7 +14,7 @@ using Volo.Abp.Users;
namespace Volo.Abp.AspNetCore.Serilog;
public class AbpSerilogMiddleware : IMiddleware, ITransientDependency
public class AbpSerilogMiddleware : AbpMiddlewareBase, ITransientDependency
{
private readonly ICurrentClient _currentClient;
private readonly ICurrentTenant _currentTenant;
@ -35,7 +36,7 @@ public class AbpSerilogMiddleware : IMiddleware, ITransientDependency
_options = options.Value;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var enrichers = new List<ILogEventEnricher>();

5
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/AbpRequestLocalizationMiddleware.cs

@ -4,11 +4,12 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Middleware;
using Volo.Abp.DependencyInjection;
namespace Microsoft.AspNetCore.RequestLocalization;
public class AbpRequestLocalizationMiddleware : IMiddleware, ITransientDependency
public class AbpRequestLocalizationMiddleware : AbpMiddlewareBase, ITransientDependency
{
public const string HttpContextItemName = "__AbpSetCultureCookie";
@ -23,7 +24,7 @@ public class AbpRequestLocalizationMiddleware : IMiddleware, ITransientDependenc
_loggerFactory = loggerFactory;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var middleware = new RequestLocalizationMiddleware(
next,

7
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAuditingMiddleware.cs

@ -4,6 +4,7 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Middleware;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;
@ -11,7 +12,7 @@ using Volo.Abp.Users;
namespace Volo.Abp.AspNetCore.Auditing;
public class AbpAuditingMiddleware : IMiddleware, ITransientDependency
public class AbpAuditingMiddleware : AbpMiddlewareBase, ITransientDependency
{
private readonly IAuditingManager _auditingManager;
protected AbpAuditingOptions AuditingOptions { get; }
@ -34,9 +35,9 @@ public class AbpAuditingMiddleware : IMiddleware, ITransientDependency
AspNetCoreAuditingOptions = aspNetCoreAuditingOptions.Value;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (!AuditingOptions.IsEnabled || IsIgnoredUrl(context))
if (await ShouldSkipAsync(context, next) || !AuditingOptions.IsEnabled || IsIgnoredUrl(context))
{
await next(context);
return;

5
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs

@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Volo.Abp.AspNetCore.Middleware;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
@ -14,7 +15,7 @@ using Volo.Abp.Json;
namespace Volo.Abp.AspNetCore.ExceptionHandling;
public class AbpExceptionHandlingMiddleware : IMiddleware, ITransientDependency
public class AbpExceptionHandlingMiddleware : AbpMiddlewareBase, ITransientDependency
{
private readonly ILogger<AbpExceptionHandlingMiddleware> _logger;
@ -27,7 +28,7 @@ public class AbpExceptionHandlingMiddleware : IMiddleware, ITransientDependency
_clearCacheHeadersDelegate = ClearCacheHeaders;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
try
{

6
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Filters/IAbpFilter.cs

@ -0,0 +1,6 @@
namespace Volo.Abp.AspNetCore.Filters;
public interface IAbpFilter
{
}

19
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Middleware/AbpMiddlewareBase.cs

@ -0,0 +1,19 @@
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
namespace Volo.Abp.AspNetCore.Middleware;
public abstract class AbpMiddlewareBase : IMiddleware
{
protected Task<bool> ShouldSkipAsync(HttpContext context, RequestDelegate next)
{
var endpoint = context.GetEndpoint();
var controllerActionDescriptor = endpoint?.Metadata.GetMetadata<ControllerActionDescriptor>();
var disableAbpFeaturesAttribute = controllerActionDescriptor?.ControllerTypeInfo.GetCustomAttribute<DisableAbpFeaturesAttribute>();
return Task.FromResult(disableAbpFeaturesAttribute != null && disableAbpFeaturesAttribute.DisableMiddleware);
}
public abstract Task InvokeAsync(HttpContext context, RequestDelegate next);
}

5
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs

@ -5,11 +5,12 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Volo.Abp.AspNetCore.Middleware;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Security;
public class AbpSecurityHeadersMiddleware : IMiddleware, ITransientDependency
public class AbpSecurityHeadersMiddleware : AbpMiddlewareBase, ITransientDependency
{
public IOptions<AbpSecurityHeadersOptions> Options { get; set; }
protected const string ScriptSrcKey = "script-src";
@ -20,7 +21,7 @@ public class AbpSecurityHeadersMiddleware : IMiddleware, ITransientDependency
Options = options;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
/*X-Content-Type-Options header tells the browser to not try and “guess” what a mimetype of a resource might be, and to just take what mimetype the server has returned as fact.*/
AddHeader(context, "X-Content-Type-Options", "nosniff");

5
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs

@ -4,14 +4,15 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Middleware;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Security.Claims;
public class AbpClaimsMapMiddleware : IMiddleware, ITransientDependency
public class AbpClaimsMapMiddleware : AbpMiddlewareBase, ITransientDependency
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var currentPrincipalAccessor = context.RequestServices
.GetRequiredService<ICurrentPrincipalAccessor>();

5
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpDynamicClaimsMiddleware.cs

@ -2,14 +2,15 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Middleware;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Security.Claims;
public class AbpDynamicClaimsMiddleware : IMiddleware, ITransientDependency
public class AbpDynamicClaimsMiddleware : AbpMiddlewareBase, ITransientDependency
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (context.User.Identity?.IsAuthenticated == true)
{

5
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AbpCorrelationIdMiddleware.cs

@ -3,12 +3,13 @@ using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Middleware;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Tracing;
namespace Volo.Abp.AspNetCore.Tracing;
public class AbpCorrelationIdMiddleware : IMiddleware, ITransientDependency
public class AbpCorrelationIdMiddleware : AbpMiddlewareBase, ITransientDependency
{
private readonly AbpCorrelationIdOptions _options;
private readonly ICorrelationIdProvider _correlationIdProvider;
@ -20,7 +21,7 @@ public class AbpCorrelationIdMiddleware : IMiddleware, ITransientDependency
_correlationIdProvider = correlationIdProvider;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var correlationId = GetCorrelationIdFromRequest(context);
using (_correlationIdProvider.Change(correlationId))

7
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Uow/AbpUnitOfWorkMiddleware.cs

@ -3,12 +3,13 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Middleware;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;
namespace Volo.Abp.AspNetCore.Uow;
public class AbpUnitOfWorkMiddleware : IMiddleware, ITransientDependency
public class AbpUnitOfWorkMiddleware : AbpMiddlewareBase, ITransientDependency
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly AbpAspNetCoreUnitOfWorkOptions _options;
@ -21,9 +22,9 @@ public class AbpUnitOfWorkMiddleware : IMiddleware, ITransientDependency
_options = options.Value;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (IsIgnoredUrl(context))
if (await ShouldSkipAsync(context, next) || IsIgnoredUrl(context))
{
await next(context);
return;

12
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/WebContentFileProvider.cs

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
@ -34,7 +33,10 @@ public class WebContentFileProvider : IWebContentFileProvider, ISingletonDepende
public virtual IFileInfo GetFileInfo(string subpath)
{
Check.NotNullOrEmpty(subpath, nameof(subpath));
if (string.IsNullOrEmpty(subpath))
{
return new NotFoundFileInfo(subpath);
}
if (PathUtils.PathNavigatesAboveRoot(subpath))
{
@ -53,11 +55,9 @@ public class WebContentFileProvider : IWebContentFileProvider, ISingletonDepende
return _fileProvider.GetFileInfo(_rootPath + subpath);
}
public virtual IDirectoryContents GetDirectoryContents([NotNull] string subpath)
public virtual IDirectoryContents GetDirectoryContents(string subpath)
{
Check.NotNullOrEmpty(subpath, nameof(subpath));
if (PathUtils.PathNavigatesAboveRoot(subpath))
if (subpath == null || PathUtils.PathNavigatesAboveRoot(subpath))
{
return NotFoundDirectoryContents.Singleton;
}

26
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProviderManager.cs

@ -13,19 +13,31 @@ public class PermissionValueProviderManager : IPermissionValueProviderManager, I
private readonly Lazy<List<IPermissionValueProvider>> _lazyProviders;
protected AbpPermissionOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
public PermissionValueProviderManager(
IServiceProvider serviceProvider,
IOptions<AbpPermissionOptions> options)
{
Options = options.Value;
ServiceProvider = serviceProvider;
_lazyProviders = new Lazy<List<IPermissionValueProvider>>(
() => Options
.ValueProviders
.Select(c => serviceProvider.GetRequiredService(c) as IPermissionValueProvider)
.ToList()!,
true
);
_lazyProviders = new Lazy<List<IPermissionValueProvider>>(GetProviders, true);
}
protected virtual List<IPermissionValueProvider> GetProviders()
{
var providers = Options
.ValueProviders
.Select(type => (ServiceProvider.GetRequiredService(type) as IPermissionValueProvider)!)
.ToList();
var multipleProviders = providers.GroupBy(p => p.Name).FirstOrDefault(x => x.Count() > 1);
if(multipleProviders != null)
{
throw new AbpException($"Duplicate permission value provider name detected: {multipleProviders.Key}. Providers:{Environment.NewLine}{multipleProviders.Select(p => p.GetType().FullName!).JoinAsString(Environment.NewLine)}");
}
return providers;
}
}

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

@ -1,12 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autofac.Core;
using Autofac.Extras.DynamicProxy;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.Autofac;
using Volo.Abp.Castle.DynamicProxy;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Modularity;
namespace Autofac.Builder;
@ -75,11 +78,15 @@ public static class AbpRegistrationBuilderExtensions
if (serviceRegistredArgs.Interceptors.Any())
{
registrationBuilder = registrationBuilder.AddInterceptors(
registrationActionList,
serviceType,
serviceRegistredArgs.Interceptors
);
var disableAbpFeaturesAttribute = serviceRegistredArgs.ImplementationType.GetCustomAttribute<DisableAbpFeaturesAttribute>(true);
if (disableAbpFeaturesAttribute == null || !disableAbpFeaturesAttribute.DisableInterceptors)
{
registrationBuilder = registrationBuilder.AddInterceptors(
registrationActionList,
serviceType,
serviceRegistredArgs.Interceptors
);
}
}
return registrationBuilder;

2
framework/src/Volo.Abp.BlazoriseUI/Components/UiMessageAlert.razor

@ -22,7 +22,7 @@
<ModalFooter class="d-flex justify-content-center">
@if ( IsConfirmation )
{
<Button Color="Color.Danger" Padding="Padding.Is3.OnX" Margin="Margin.Is1.OnX" Clicked="@OnCancelClicked">
<Button Color="Color.Primary" Outline Padding="Padding.Is3.OnX" Margin="Margin.Is1.OnX" Clicked="@OnCancelClicked">
@if ( Options?.CancelButtonIcon != null )
{
<Icon Name="@Options.CancelButtonIcon" Margin="Margin.Is2.FromEnd" />

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs

@ -1,4 +1,4 @@
using System.Text;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Commands.Internal;

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

@ -31,7 +31,6 @@
<PackageReference Include="System.Linq.Dynamic.Core" />
<PackageReference Include="System.Linq.Queryable" />
<PackageReference Include="JetBrains.Annotations" />
<PackageReference Include="Nito.AsyncEx.Coordination" />
<PackageReference Include="Nito.AsyncEx.Context" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETStandard' And $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '2.1')) ">

25
framework/src/Volo.Abp.Core/Volo/Abp/DisableAbpFeaturesAttribute.cs

@ -0,0 +1,25 @@
using System;
namespace Volo.Abp;
[AttributeUsage(AttributeTargets.Class)]
public class DisableAbpFeaturesAttribute : Attribute
{
/// <summary>
/// The framework will not register any interceptors for the class.
/// This will cause the all features that depend on interceptors to not work.
/// </summary>
public bool DisableInterceptors { get; set; } = true;
/// <summary>
/// The framework middleware will skip the class.
/// This will cause the all features that depend on middleware to not work.
/// </summary>
public bool DisableMiddleware { get; set; } = true;
/// <summary>
/// The framework will not remove all built-in filters for the class.
/// This will cause the all features that depend on filters to not work.
/// </summary>
public bool DisableMvcFilters { get; set; } = true;
}

1
framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj

@ -21,7 +21,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" VersionOverride="7.0.14" />
<PackageReference Include="Devart.Data.Oracle.EFCore" />
</ItemGroup>

20
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs

@ -12,34 +12,26 @@ public class FeatureChecker : FeatureCheckerBase
protected AbpFeatureOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
protected IFeatureDefinitionManager FeatureDefinitionManager { get; }
protected List<IFeatureValueProvider> Providers => _providers.Value;
private readonly Lazy<List<IFeatureValueProvider>> _providers;
protected IFeatureValueProviderManager FeatureValueProviderManager { get; }
public FeatureChecker(
IOptions<AbpFeatureOptions> options,
IServiceProvider serviceProvider,
IFeatureDefinitionManager featureDefinitionManager)
IFeatureDefinitionManager featureDefinitionManager,
IFeatureValueProviderManager featureValueProviderManager)
{
ServiceProvider = serviceProvider;
FeatureDefinitionManager = featureDefinitionManager;
FeatureValueProviderManager = featureValueProviderManager;
Options = options.Value;
_providers = new Lazy<List<IFeatureValueProvider>>(
() => Options
.ValueProviders
.Select(type => (ServiceProvider.GetRequiredService(type) as IFeatureValueProvider)!)
.ToList(),
true
);
}
public override async Task<string?> GetOrNullAsync(string name)
{
var featureDefinition = await FeatureDefinitionManager.GetAsync(name);
var providers = Enumerable
.Reverse(Providers);
var providers = FeatureValueProviderManager.ValueProviders
.Reverse();
if (featureDefinition.AllowedProviders.Any())
{

43
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureValueProviderManager.cs

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features;
public class FeatureValueProviderManager : IFeatureValueProviderManager, ISingletonDependency
{
public IReadOnlyList<IFeatureValueProvider> ValueProviders => _lazyProviders.Value;
private readonly Lazy<List<IFeatureValueProvider>> _lazyProviders;
protected AbpFeatureOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
public FeatureValueProviderManager(
IServiceProvider serviceProvider,
IOptions<AbpFeatureOptions> options)
{
Options = options.Value;
ServiceProvider = serviceProvider;
_lazyProviders = new Lazy<List<IFeatureValueProvider>>(GetProviders, true);
}
protected virtual List<IFeatureValueProvider> GetProviders()
{
var providers = Options
.ValueProviders
.Select(type => (ServiceProvider.GetRequiredService(type) as IFeatureValueProvider)!)
.ToList();
var multipleProviders = providers.GroupBy(p => p.Name).FirstOrDefault(x => x.Count() > 1);
if(multipleProviders != null)
{
throw new AbpException($"Duplicate feature value provider name detected: {multipleProviders.Key}. Providers:{Environment.NewLine}{multipleProviders.Select(p => p.GetType().FullName!).JoinAsString(Environment.NewLine)}");
}
return providers;
}
}

8
framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureValueProviderManager.cs

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Volo.Abp.Features;
public interface IFeatureValueProviderManager
{
IReadOnlyList<IFeatureValueProvider> ValueProviders { get; }
}

6
framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/ITenantNormalizer.cs

@ -0,0 +1,6 @@
namespace Volo.Abp.MultiTenancy;
public interface ITenantNormalizer
{
string? NormalizeName(string? name);
}

4
framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/ITenantStore.cs

@ -5,12 +5,12 @@ namespace Volo.Abp.MultiTenancy;
public interface ITenantStore
{
Task<TenantConfiguration?> FindAsync(string name);
Task<TenantConfiguration?> FindAsync(string normalizedName);
Task<TenantConfiguration?> FindAsync(Guid id);
[Obsolete("Use FindAsync method.")]
TenantConfiguration? Find(string name);
TenantConfiguration? Find(string normalizedName);
[Obsolete("Use FindAsync method.")]
TenantConfiguration? Find(Guid id);

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

@ -11,6 +11,8 @@ public class TenantConfiguration
public string Name { get; set; } = default!;
public string NormalizedName { get; set; } = default!;
public ConnectionStrings? ConnectionStrings { get; set; }
public bool IsActive { get; set; }
@ -30,4 +32,12 @@ public class TenantConfiguration
ConnectionStrings = new ConnectionStrings();
}
public TenantConfiguration(Guid id, [NotNull] string name, [NotNull] string normalizedName)
: this(id, name)
{
Check.NotNull(normalizedName, nameof(normalizedName));
NormalizedName = normalizedName;
}
}

11
framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/UpperInvariantTenantNormalizer.cs

@ -0,0 +1,11 @@
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.MultiTenancy;
public class UpperInvariantTenantNormalizer : ITenantNormalizer, ITransientDependency
{
public virtual string? NormalizeName(string? name)
{
return name?.Normalize().ToUpperInvariant();
}
}

8
framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ConfigurationStore/DefaultTenantStore.cs

@ -16,9 +16,9 @@ public class DefaultTenantStore : ITenantStore, ITransientDependency
_options = options.CurrentValue;
}
public Task<TenantConfiguration?> FindAsync(string name)
public Task<TenantConfiguration?> FindAsync(string normalizedName)
{
return Task.FromResult(Find(name));
return Task.FromResult(Find(normalizedName));
}
public Task<TenantConfiguration?> FindAsync(Guid id)
@ -26,9 +26,9 @@ public class DefaultTenantStore : ITenantStore, ITransientDependency
return Task.FromResult(Find(id));
}
public TenantConfiguration? Find(string name)
public TenantConfiguration? Find(string normalizedName)
{
return _options.Tenants?.FirstOrDefault(t => t.Name == name);
return _options.Tenants?.FirstOrDefault(t => t.NormalizedName == normalizedName);
}
public TenantConfiguration? Find(Guid id)

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

@ -10,6 +10,7 @@ public class TenantConfigurationProvider : ITenantConfigurationProvider, ITransi
{
protected virtual ITenantResolver TenantResolver { get; }
protected virtual ITenantStore TenantStore { get; }
protected virtual ITenantNormalizer TenantNormalizer { get; }
protected virtual ITenantResolveResultAccessor TenantResolveResultAccessor { get; }
protected virtual IStringLocalizer<AbpMultiTenancyResource> StringLocalizer { get; }
@ -17,10 +18,12 @@ public class TenantConfigurationProvider : ITenantConfigurationProvider, ITransi
ITenantResolver tenantResolver,
ITenantStore tenantStore,
ITenantResolveResultAccessor tenantResolveResultAccessor,
IStringLocalizer<AbpMultiTenancyResource> stringLocalizer)
IStringLocalizer<AbpMultiTenancyResource> stringLocalizer,
ITenantNormalizer tenantNormalizer)
{
TenantResolver = tenantResolver;
TenantStore = tenantStore;
TenantNormalizer = tenantNormalizer;
TenantResolveResultAccessor = tenantResolveResultAccessor;
StringLocalizer = stringLocalizer;
}
@ -69,7 +72,7 @@ public class TenantConfigurationProvider : ITenantConfigurationProvider, ITransi
}
else
{
return await TenantStore.FindAsync(tenantIdOrName);
return await TenantStore.FindAsync(TenantNormalizer.NormalizeName(tenantIdOrName)!);
}
}
}

27
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValueProviderManager.cs

@ -10,7 +10,9 @@ namespace Volo.Abp.Settings;
public class SettingValueProviderManager : ISettingValueProviderManager, ISingletonDependency
{
public List<ISettingValueProvider> Providers => _lazyProviders.Value;
protected AbpSettingOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
private readonly Lazy<List<ISettingValueProvider>> _lazyProviders;
public SettingValueProviderManager(
@ -19,13 +21,24 @@ public class SettingValueProviderManager : ISettingValueProviderManager, ISingle
{
Options = options.Value;
ServiceProvider = serviceProvider;
_lazyProviders = new Lazy<List<ISettingValueProvider>>(GetProviders, true);
}
protected virtual List<ISettingValueProvider> GetProviders()
{
var providers = Options
.ValueProviders
.Select(type => (ServiceProvider.GetRequiredService(type) as ISettingValueProvider)!)
.ToList();
var multipleProviders = providers.GroupBy(p => p.Name).FirstOrDefault(x => x.Count() > 1);
if(multipleProviders != null)
{
throw new AbpException($"Duplicate setting value provider name detected: {multipleProviders.Key}. Providers:{Environment.NewLine}{multipleProviders.Select(p => p.GetType().FullName!).JoinAsString(Environment.NewLine)}");
}
_lazyProviders = new Lazy<List<ISettingValueProvider>>(
() => Options
.ValueProviders
.Select(type => serviceProvider.GetRequiredService(type) as ISettingValueProvider)
.ToList()!,
true
);
return providers;
}
}

3
framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_WithDomainResolver_Tests.cs

@ -16,6 +16,7 @@ public class AspNetCoreMultiTenancy_WithDomainResolver_Tests : AspNetCoreMultiTe
{
private readonly Guid _testTenantId = Guid.NewGuid();
private readonly string _testTenantName = "acme";
private readonly string _testTenantNormalizedName = "ACME";
private readonly AbpAspNetCoreMultiTenancyOptions _options;
@ -30,7 +31,7 @@ public class AspNetCoreMultiTenancy_WithDomainResolver_Tests : AspNetCoreMultiTe
{
options.Tenants = new[]
{
new TenantConfiguration(_testTenantId, _testTenantName)
new TenantConfiguration(_testTenantId, _testTenantName, _testTenantNormalizedName)
};
});

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

Loading…
Cancel
Save