diff --git a/.github/workflows/auto-pr.yml b/.github/workflows/auto-pr.yml
index 5587120951..add8adb306 100644
--- a/.github/workflows/auto-pr.yml
+++ b/.github/workflows/auto-pr.yml
@@ -1,13 +1,13 @@
-name: Merge branch dev with rel-8.0
+name: Merge branch dev with prerel-8.1
on:
push:
branches:
- - rel-8.0
+ - prerel-8.1
permissions:
contents: read
jobs:
- merge-dev-with-rel-8-0:
+ merge-dev-with-prerel-8-1:
permissions:
contents: write # for peter-evans/create-pull-request to create branch
pull-requests: write # for peter-evans/create-pull-request to create a PR
@@ -18,19 +18,19 @@ jobs:
ref: dev
- name: Reset promotion branch
run: |
- git fetch origin rel-8.0:rel-8.0
- git reset --hard rel-8.0
+ git fetch origin prerel-8.1:prerel-8.1
+ git reset --hard prerel-8.1
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
- branch: auto-merge/rel-8-0/${{github.run_number}}
- title: Merge branch dev with rel-8.0
- body: This PR generated automatically to merge dev with rel-8.0. Please review the changed files before merging to prevent any errors that may occur.
+ branch: auto-merge/prerel-8-1/${{github.run_number}}
+ title: Merge branch dev with prerel-8.1
+ body: This PR generated automatically to merge dev with prerel-8.1. Please review the changed files before merging to prevent any errors that may occur.
reviewers: maliming
token: ${{ github.token }}
- name: Merge Pull Request
env:
GH_TOKEN: ${{ secrets.BOT_SECRET }}
run: |
- gh pr review auto-merge/rel-8-0/${{github.run_number}} --approve
- gh pr merge auto-merge/rel-8-0/${{github.run_number}} --merge --auto --delete-branch
+ gh pr review auto-merge/prerel-8-1/${{github.run_number}} --approve
+ gh pr merge auto-merge/prerel-8-1/${{github.run_number}} --merge --auto --delete-branch
\ No newline at end of file
diff --git a/Directory.Packages.props b/Directory.Packages.props
index ee37fea2e7..f5cf8871de 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,7 +6,7 @@
-
+
@@ -16,10 +16,10 @@
-
-
-
-
+
+
+
+
@@ -27,7 +27,7 @@
-
+
@@ -107,18 +107,17 @@
-
-
-
-
-
-
+
+
+
+
+
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
index bd795bb48c..ee660f72e4 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
+++ b/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"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
index 158685b84c..f8eb001da2 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
+++ b/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?",
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json
index 76e8bf2ded..43d4ff28a0 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json
+++ b/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 المستضافة على {1}.",
- "MyOrganizations_Detail_YourPrivateNugetSource": "مصدر NuGet الخاص بك هو {0}",
+ "MyOrganizations_Detail_ApiKeyDescription": "مفتاح API هو الرمز المميز لحزم PRO المستضافة على {1}.",
+ "MyOrganizations_Detail_YourPrivateNugetSource": "مصدر NuGet الخاص بك هو {0}",
"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} مطورين",
- "LicenseRemainingDays": "لمدة {0} يوم",
+ "LicenseRemainingDays": "لمدة {0} يوم",
"ExtendPaymentInfoSection_Description": "من خلال تمديد / تجديد رخصتك، ستستمر في الحصول على الدعم المتميز. ستتمكن أيضًا من الحصول على تحديثات رئيسية أو ثانوية للوحدات والسمات. ستتمكن من الاستمرار في إنشاء مشاريع جديدة. وستظل قادرًا على استخدام ABP Suite مما يسرع من تطويرك.",
"LicenseRenewalPrice": "سعر تجديد الرخصة",
"LicensePrice": "سعر الرخصة",
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
index c5a2608680..4c7fe13943 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
+++ b/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 {1}.",
- "MyOrganizations_Detail_YourPrivateNugetSource": "Your private NuGet source is {0}",
+ "MyOrganizations_Detail_ApiKeyDescription": "The API Key is the token of PRO packages hosted on {1}.",
+ "MyOrganizations_Detail_YourPrivateNugetSource": "Your private NuGet source is {0}",
"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} developers",
- "LicenseRemainingDays": "for {0} days",
+ "LicenseRemainingDays": "for {0} days",
"ExtendPaymentInfoSection_Description": "By extending/renewing your license, you will continue to get premium support. 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 ABP Suite 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:",
@@ -908,6 +908,7 @@
"NoLabels": "No labels",
"DoesTheSubscriptionRenewAutomatically": "Does the subscription renew automatically?",
"DoesTheSubscriptionRenewAutomaticallyExplanation": "The ABP Commercial does not have an auto-renewal billing model. Therefore your subscription will not be automatically renewed at the end of your license period. If you want to continue to have the benefits of ABP Commercial, you need to manually renew it at the organization management page. If you have multiple organizations, click the \"Manage\" button at your expiring organization and then click the \"Extend Now\" button to renew your license. You may also want to take a look at the What Happens When My License Ends? section.",
+ "DoesTheSubscriptionRenewAutomaticallyExplanationAutoRenewal": "ABP Commercial allows you to auto-renew your license. This is an optional service. You can toggle this feature when you purchase a new license or later enable it from your organization management page. Auto-renewal toggle is in the 'Payments Method' section of the organization management page. If you want to turn off auto-renewal, visit organization management page, go to the 'Payments Method' section and uncheck the 'Automatic Renewal' checkbox. When you turn off the auto-renewal feature, you must renew your license yourself.",
"ExtraQuestionCreditsFaqTitle": "Can I purchase extra support question credits?",
"ExtraQuestionCreditsFaqExplanation": "Yes, you can. To buy extra question credits, send an e-mail to info@abp.io with your organization's name. Here's the price list for the extra question credits:
50 questions pack $999
25 questions pack $625
15 questions pack $450
",
"AlreadyBetaTester": "You have already joined the beta tester program.",
@@ -1090,11 +1091,49 @@
"AbpStudioBetaAccessInfoTitle": "ABP Studio Beta Access",
"AbpStudioBetaAccessInfoText": "We're thrilled to share with you the beta version of ABP Studio! 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",
- "ManageLicense": "Manage License",
- "AbpPlatformLeptonXTheme": "LeptonX Theme for Your Admin Dashboard by ABP Platform"
+ "ManageLicense": "Manage License",
+ "AbpPlatformLeptonXTheme": "LeptonX Theme for Your Admin Dashboard by ABP Platform",
+ "NoActiveLicence": "You are not eligible for this action! You have no active license.",
+ "ABPStudioBetaTester": "To be able to submit your request, you must sign in",
+ "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 {2} 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"
}
-}
-
-
+}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json
index d22d20d1d4..6a9c7014e5 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json
+++ b/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 {1}-palvelussa isännöivien PRO-pakettien tunnus.",
- "MyOrganizations_Detail_YourPrivateNugetSource": "Yksityinen NuGet-lähteesi on {0}",
+ "MyOrganizations_Detail_ApiKeyDescription": "API-avain on {1}-palvelussa isännöivien PRO-pakettien tunnus.",
+ "MyOrganizations_Detail_YourPrivateNugetSource": "Yksityinen NuGet-lähteesi on {0}",
"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} kehittäjät",
- "LicenseRemainingDays": " {0} päivän ajan",
+ "LicenseRemainingDays": " {0} päivän ajan",
"ExtendPaymentInfoSection_Description": "Pidentämällä/uusimalla käyttölupaasi saat edelleen premium-tuen. Voit myös saada suurempia tai pieniä päivityksiä moduuleille ja teemoille. Voit jatkaa uusien projektien luomista. Voit silti käyttää ABP Suitea, joka nopeuttaa kehitystäsi.",
"LicenseRenewalPrice": "Lisenssin uusimisen hinta",
"LicensePrice": "Lisenssin hinta",
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json
index a0930f2267..7396ceea89 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json
+++ b/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 {1}.",
- "MyOrganizations_Detail_YourPrivateNugetSource": "Vaš privatni NuGet izvor je {0}",
+ "MyOrganizations_Detail_ApiKeyDescription": "API ključ je token PRO paketa hostiranih na {1}.",
+ "MyOrganizations_Detail_YourPrivateNugetSource": "Vaš privatni NuGet izvor je {0}",
"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} programeri",
- "LicenseRemainingDays": "za {0} dana",
+ "LicenseRemainingDays": "za {0} dana",
"ExtendPaymentInfoSection_Description": "Produljenjem/obnavljanjem licence i dalje ćete dobivati premium podršku . 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 ABP Suite koji ubrzava vaš razvoj.",
"LicenseRenewalPrice": "Cijena obnove licence",
"LicensePrice": "Cijena licence",
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json
index b12bd2d369..b6169dedb1 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json
+++ b/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) {1} webhelyen tárolt PRO-csomagok tokenje.",
- "MyOrganizations_Detail_YourPrivateNugetSource": "Az Ön privát NuGet-forrása {0}",
+ "MyOrganizations_Detail_ApiKeyDescription": "Az API-kulcs a(z) {1} webhelyen tárolt PRO-csomagok tokenje.",
+ "MyOrganizations_Detail_YourPrivateNugetSource": "Az Ön privát NuGet-forrása {0}",
"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} fejlesztő",
- "LicenseRemainingDays": "{0} napig",
+ "LicenseRemainingDays": "{0} napig",
"ExtendPaymentInfoSection_Description": "A licensz meghosszabbításával/megújításával továbbra is prémium támogatást 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 ABP Suite -ot, amely felgyorsítja a fejlesztést.",
"LicenseRenewalPrice": "Licensz megújítási ára",
"LicensePrice": "Licensz ára",
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ru.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ru.json
index 6649ec4ed9..782743ab76 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ru.json
+++ b/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-пакетов, размещенных на {1}.",
- "MyOrganizations_Detail_YourPrivateNugetSource": "Ваш личный источник NuGet: {0}",
+ "MyOrganizations_Detail_ApiKeyDescription": "Ключ API — это токен PRO-пакетов, размещенных на {1}.",
+ "MyOrganizations_Detail_YourPrivateNugetSource": "Ваш личный источник NuGet: {0}",
"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} Разработчики",
- "LicenseRemainingDays": "для {0} дни",
+ "LicenseRemainingDays": "для {0} дни",
"ExtendPaymentInfoSection_Description": "Продлив/продлив лицензию, вы продолжите получать премиум-поддержка. ABP Люкс что ускоряет ваше развитие.",
"LicenseRenewalPrice": "Стоимость продления лицензии",
"LicensePrice": "Цена лицензии",
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json
index 0db8e3c41e..723512e10c 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json
@@ -651,7 +651,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} geliştiriciler",
- "LicenseRemainingDays": " {0} gün boyunca",
+ "LicenseRemainingDays": " {0} gün boyunca",
"ExtendPaymentInfoSection_Description": "Lisansınızı uzatarak/yenileyerek premium destek 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 ABP Suite'i kullanmaya devam edebileceksiniz.",
"LicenseRenewalPrice": "Lisans yenileme fiyatı",
"LicensePrice": "Lisans fiyatı",
@@ -999,12 +999,5 @@
"MyOrganizations_Detail_LicenseExpiryDate": "Bitiş Tarihi",
"ManageLicense": "Lisansı Yönet",
"AbpPlatformLeptonXTheme": "ABP Platform Yönetici Kontrol Paneliniz için LeptonX Teması"
-
}
-}
-
-
-
-
-
-
+}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
index 72babe85fa..19aac6757d 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
+++ b/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 密钥是 {1}. 上托管的 PRO 包的标记。",
- "MyOrganizations_Detail_YourPrivateNugetSource": "您的 NuGet 私有源是 {0}",
+ "MyOrganizations_Detail_ApiKeyDescription": "API 密钥是 {1}. 上托管的 PRO 包的标记。",
+ "MyOrganizations_Detail_YourPrivateNugetSource": "您的 NuGet 私有源是 {0}",
"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": "为 {0} 天",
+ "LicenseRemainingDays": "为 {0} 天",
"ExtendPaymentInfoSection_Description": "延长/续订许可证后,您将继续获得高级支持。您还可以获得模块和主题的主要或次要更新。您还可以继续创建新项目。您还可以使用 ABP Suite 来加快开发速度。",
"LicenseRenewalPrice": "许可证续期价格",
"LicensePrice": "许可证价格",
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json
index 50bc6be27c..b1e8e87a21 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json
+++ b/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 金鑰是{1} 上託管的 PRO 包的令牌。",
- "MyOrganizations_Detail_YourPrivateNugetSource": "您的私有 NuGet 來源是{0}",
+ "MyOrganizations_Detail_YourPrivateNugetSource": "您的私有 NuGet 來源是{0}",
"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}開發者",
- "LicenseRemainingDays": "{0}天",
+ "LicenseRemainingDays": "{0}天",
"ExtendPaymentInfoSection_Description": "透過延長/更新您的許可證,您將繼續獲得高級支援。您還可以獲得模組和主題的主要或次要更新。您將能夠繼續建立新專案。而且您仍然可以使用ABP Suite來加快您的開發速度。",
"LicenseRenewalPrice": "許可證更新價格",
"LicensePrice": "許可價格",
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json
index 963546d9a3..7151345ea9 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json
@@ -255,6 +255,8 @@
"LatestArticles": "أحدث المقالات",
"RaffleHeader": "مرحبًا عضو مجتمع برنامج الجسر الأكاديمي!",
"RafflesInfo": " هذه هي صفحة السحب المخصصة لإظهار تقديرنا لك لكونك عضوًا نشطًا في المجتمع. نحن نقوم بإجراء محادثات مجتمعية لبرنامج ABP، ومؤتمر ABP .NET ، ونحضر أو نرعى الأحداث المتعلقة بـ .NET والتي نقدم فيها بعض الهدايا.
يمكنك متابعة هذه الصفحة لرؤية السحوبات القادمة أو حضورها أو الاطلاع على السحوبات السابقة التي قمنا بسحبها بما في ذلك الفائزين.
شكرا لكونك عضوا نشطا! نراكم في السحوبات القادمة.",
- "RafflesInfoTitle": "سحوبات مجتمع ABP"
+ "RafflesInfoTitle": "سحوبات مجتمع ABP",
+ "MarkdownSupported": "Markdown أيد.",
+ "Preview": "معاينة"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json
index 418cf5aef1..252b0dcb2b 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json
@@ -255,6 +255,8 @@
"LatestArticles": "poslední články",
"RaffleHeader": "Dobrý den, člen komunity ABP!",
"RafflesInfo": " 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 ABP Community Talks, ABP .NET Conference , účastníme se nebo sponzorujeme akce související s .NET, ve kterých rozdáváme nějaké dárky.
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ů.
Děkujeme, že jste aktivním členem! Uvidíme se v nadcházejícím slosování.",
- "RafflesInfoTitle": "Komunitní tomboly ABP"
+ "RafflesInfoTitle": "Komunitní tomboly ABP",
+ "MarkdownSupported": "Markdown podporováno.",
+ "Preview": "Náhled"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json
index 021c19b3ff..4326ff88ef 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json
@@ -255,6 +255,8 @@
"LatestArticles": "Neueste Artikel",
"RaffleHeader": "Hallo ABP-Community-Mitglied!",
"RafflesInfo": " Auf dieser Verlosungsseite möchten wir Ihnen unsere Wertschätzung dafür zeigen, dass Sie ein aktives Community-Mitglied sind. Wir veranstalten ABP Community Talksund ABP .NET Conferences , nehmen an .NET-bezogenen Veranstaltungen teil oder sponsern diese, bei denen wir einige Geschenke verschenken.
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.
Vielen Dank, dass Sie ein aktives Mitglied sind! Wir sehen uns bei den kommenden Gewinnspielen.",
- "RafflesInfoTitle": "ABP-Community- Verlosungen"
+ "RafflesInfoTitle": "ABP-Community- Verlosungen",
+ "MarkdownSupported": "Markdown unterstützt.",
+ "Preview": "Vorschau"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en-GB.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en-GB.json
index 0283b963bc..347b18ce4b 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en-GB.json
+++ b/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": "Markdown supported.",
+ "Preview": "Preview"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
index 8b17b6b85b..2329272217 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
@@ -256,6 +256,8 @@
"RaffleHeader": "Hello ABP Community Member!",
"RafflesInfo": " This is the raffle page dedicated to show our appreciation towards you for being an active Community Member. We do ABP Community Talks,ABP .NET Conference, attend or sponsor to the .NET-related events in which we give away some gifts.
You can follow this page to see the upcoming raffles, attend them, or see previous raffles we draw including the winners.
Thank you for being an active member! See you in the upcoming raffles.",
"RafflesInfoTitle": "ABP Community Raffles",
- "ToLuckyWinner": "to 1 lucky winner"
+ "ToLuckyWinner": "to 1 lucky winner",
+ "MarkdownSupported": "Markdown supported.",
+ "Preview": "Preview"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json
index c4e178bbd9..c53b45ab81 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json
+++ b/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": " Esta es la página del sorteo dedicada a mostrarle nuestro agradecimiento por ser un miembro activo de la comunidad. Realizamos charlas comunitarias de ABP, conferencias ABP .NET , asistimos o patrocinamos eventos relacionados con .NET en los que regalamos algunos obsequios.
Puedes seguir esta página para ver los próximos sorteos, asistir a ellos o ver sorteos anteriores que sorteamos incluyendo a los ganadores.
¡Gracias por ser un miembro activo! Nos vemos en los próximos sorteos.",
- "RafflesInfoTitle": "Rifas de la Comunidad ABP"
+ "RafflesInfoTitle": "Rifas de la Comunidad ABP",
+ "MarkdownSupported": "Markdown soportado.",
+ "Preview": "Avance"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json
index ac509875d6..abd8cdd21f 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json
+++ b/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": " Tämä on arvontasivu, joka on omistettu osoittamaan kiitollisuuttamme sinua kohtaan, että olet aktiivinen yhteisön jäsen. Teemme ABP Community Talksia, ABP .NET -konferenssia , osallistumme tai sponsoroimme .NET-tapahtumia, joissa annamme lahjoja.
Voit seurata tätä sivua nähdäksesi tulevat arvonnat, osallistua niihin tai nähdäksesi aiemmat arvonnamme, mukaan lukien voittajat.
Kiitos aktiivisesta jäsenyydestäsi! Nähdään tulevissa arvonnassa.",
- "RafflesInfoTitle": "ABP-yhteisön arpajaiset"
+ "RafflesInfoTitle": "ABP-yhteisön arpajaiset",
+ "MarkdownSupported": "Markdown tuettu.",
+ "Preview": "Esikatselu"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json
index c98a48d93e..1f259aac6d 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json
@@ -255,6 +255,8 @@
"LatestArticles": "Derniers articles",
"RaffleHeader": "Bonjour membre de la communauté ABP !",
"RafflesInfo": " Ceci est la page de tirage au sort dédiée à vous montrer notre gratitude pour votre participation active à la communauté. Nous organisons des discussions communautaires ABP, des conférences ABP .NET , assistons ou sponsorisons des événements liés à .NET au cours desquels nous offrons des cadeaux.
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.
Merci d'être un membre actif ! Rendez-vous lors des prochains tirages au sort.",
- "RafflesInfoTitle": "Tirages au sort communautaires ABP"
+ "RafflesInfoTitle": "Tirages au sort communautaires ABP",
+ "MarkdownSupported": "Markdown prise en charge.",
+ "Preview": "Aperçu"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json
index f3d300e8a2..b868260ae6 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json
@@ -255,6 +255,8 @@
"LatestArticles": "नवीनतम लेख",
"RaffleHeader": "नमस्ते एबीपी समुदाय सदस्य!",
"RafflesInfo": " यह रैफ़ल पेज है जो एक सक्रिय समुदाय सदस्य होने के लिए आपके प्रति हमारी सराहना दिखाने के लिए समर्पित है। हम एबीपी सामुदायिक वार्ता, एबीपी .NET सम्मेलन करते हैं, .NET से संबंधित कार्यक्रमों में भाग लेते हैं या प्रायोजित करते हैं जिसमें हम कुछ उपहार देते हैं।
आप आगामी रैफ़ल देखने, उनमें भाग लेने, या विजेताओं सहित हमारे द्वारा निकाले गए पिछले रैफ़ल देखने के लिए इस पृष्ठ का अनुसरण कर सकते हैं।
सक्रिय सदस्य बनने के लिए धन्यवाद! आगामी रैफ़ल में मिलते हैं।",
- "RafflesInfoTitle": "एबीपी कम्युनिटी रैफल्स"
+ "RafflesInfoTitle": "एबीपी कम्युनिटी रैफल्स",
+ "MarkdownSupported": "Markdown का समर्थन किया।",
+ "Preview": "पूर्व दर्शन"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json
index 5b3546a78b..eb3b66d9c8 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json
@@ -255,6 +255,8 @@
"LatestArticles": "Najnoviji članci",
"RaffleHeader": "Pozdrav članu ABP zajednice!",
"RafflesInfo": " Ovo je stranica za nagradnu igru namijenjena da vam pokažemo našu zahvalnost što ste aktivni član zajednice. Vodimo razgovore o ABP zajednici, ABP .NET konferenciji , prisustvujemo ili sponzoriramo događaje vezane uz .NET na kojima dijelimo neke darove.
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.
Hvala vam što ste aktivan član! Vidimo se u nadolazećim nagradnim igrama.",
- "RafflesInfoTitle": "Izvlačenje nagradne igre zajednice ABP"
+ "RafflesInfoTitle": "Izvlačenje nagradne igre zajednice ABP",
+ "MarkdownSupported": "Markdown podržan.",
+ "Preview": "Pregled"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json
index 1f74f9e7a9..08917573ff 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json
+++ b/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": " 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. Az ABP Community Talks-okat, az ABP .NET konferenciákat szervezzük, részt veszünk vagy szponzorálunk olyan .NET-hez kapcsolódó eseményeket, amelyeken ajándékokat adunk.
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.
Köszönjük, hogy aktív tag vagy! Találkozunk a sorsoláson.",
- "RafflesInfoTitle": "ABP közösségi tombola"
+ "RafflesInfoTitle": "ABP közösségi tombola",
+ "MarkdownSupported": "Markdown támogatott.",
+ "Preview": "Előnézet"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json
index a991c387d4..0d8eb6cb48 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json
@@ -255,6 +255,8 @@
"LatestArticles": "Nýjustu greinar",
"RaffleHeader": "Halló ABP samfélagsmeðlimur!",
"RafflesInfo": " Þetta er happdrættisíðan sem er tileinkuð þér að þakka þér fyrir að vera virkur samfélagsmeðlimur. Við gerum ABP Community Talks, ABP .NET ráðstefnu , sækjum eða styrkjum .NET tengda viðburði þar sem við gefum nokkrar gjafir.
Þú 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.
Þakka þér fyrir að vera virkur meðlimur! Sjáumst í komandi happdrætti.",
- "RafflesInfoTitle": "ABP samfélagshappdrætti"
+ "RafflesInfoTitle": "ABP samfélagshappdrætti",
+ "MarkdownSupported": "Markdown stutt.",
+ "Preview": "Forskoðun"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json
index 901c356c9a..a301a7f482 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json
@@ -255,6 +255,8 @@
"LatestArticles": "Articoli Recenti",
"RaffleHeader": "Ciao membro della comunità ABP!",
"RafflesInfo": " Questa è la pagina della lotteria dedicata a mostrare il nostro apprezzamento nei tuoi confronti per essere un membro attivo della comunità. Organizziamo ABP Community Talks, ABP .NET Conference , partecipiamo o sponsorizziamo eventi relativi a .NET in cui regaliamo alcuni regali.
Puoi seguire questa pagina per vedere le prossime lotterie, parteciparvi o vedere le lotterie precedenti che estraiamo, compresi i vincitori.
Grazie per essere un membro attivo! Ci vediamo alle prossime lotterie.",
- "RafflesInfoTitle": "Lotterie comunitarie ABP"
+ "RafflesInfoTitle": "Lotterie comunitarie ABP",
+ "MarkdownSupported": "Markdown supportato.",
+ "Preview": "Anteprima"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json
index 4ee8e44a09..45b3024c96 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json
@@ -255,6 +255,8 @@
"LatestArticles": "Laatste artikels",
"RaffleHeader": "Hallo ABP Community-lid!",
"RafflesInfo": " 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 ABP Community Talks, ABP .NET Conference , wonen of sponsoren de .NET-gerelateerde evenementen bij waarin we een aantal geschenken weggeven.
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.
Bedankt dat u een actief lid bent! Tot ziens bij de komende loterijen.",
- "RafflesInfoTitle": "ABP- gemeenschaploterijen"
+ "RafflesInfoTitle": "ABP- gemeenschaploterijen",
+ "MarkdownSupported": "Markdown ondersteund.",
+ "Preview": "Voorbeeld"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json
index 2df6780f03..124f910ac0 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json
+++ b/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": " To jest strona loterii, której celem jest wyrażenie naszego uznania dla Ciebie za bycie aktywnym członkiem społeczności. Prowadzimy rozmowy społecznościowe ABP, konferencje ABP .NET , uczestniczymy lub sponsorujemy wydarzenia związane z .NET, podczas których rozdajemy prezenty.
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.
Dziękujemy za bycie aktywnym członkiem! Do zobaczenia w nadchodzących loteriach.",
- "RafflesInfoTitle": "Loterie społecznościowe ABP"
+ "RafflesInfoTitle": "Loterie społecznościowe ABP",
+ "MarkdownSupported": "Markdown utrzymany.",
+ "Preview": "Zapowiedź"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json
index 9f2ce433d9..419ef77140 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json
+++ b/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": " Esta é a página do sorteio dedicada a mostrar nosso agradecimento por você ser um membro ativo da comunidade. Fazemos ABP Community Talks, ABP .NET Conference , participamos ou patrocinamos eventos relacionados ao .NET nos quais distribuímos alguns presentes.
Você pode seguir esta página para ver os próximos sorteios, participar deles ou ver os sorteios anteriores que sorteamos, incluindo os vencedores.
Obrigado por ser um membro ativo! Nos vemos nos próximos sorteios.",
- "RafflesInfoTitle": "Sorteios da Comunidade ABP"
+ "RafflesInfoTitle": "Sorteios da Comunidade ABP",
+ "MarkdownSupported": "Markdown suportado.",
+ "Preview": "Visualização"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json
index c2862d1fd7..47e513f125 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json
+++ b/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": " 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 ABP Community Talks, ABP .NET Conference , participăm sau sponsorizăm evenimentele legate de .NET în care oferim câteva cadouri.
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.
Vă mulțumim că sunteți un membru activ! Ne vedem la tombolele viitoare.",
- "RafflesInfoTitle": "Tombole comunitare ABP"
+ "RafflesInfoTitle": "Tombole comunitare ABP",
+ "MarkdownSupported": "Markdown sprijinit.",
+ "Preview": "previzualizare"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ru.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ru.json
index 27f2b1203a..b91893ce94 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ru.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ru.json
@@ -258,6 +258,8 @@
"LatestArticles": "Последние статьи",
"RaffleHeader": "Привет, участник сообщества ABP!",
"RafflesInfo": " Эта страница розыгрыша посвящена тому, чтобы выразить нашу признательность вам за то, что вы являетесь активным членом сообщества. Обсуждения сообщества ABP,Конференция ABP .NET, посещать или спонсировать мероприятия, связанные с .NET, на которых мы раздаем подарки.
Вы можете подписаться на эту страницу, чтобы увидеть предстоящие розыгрыши, посетить их или просмотреть предыдущие розыгрыши, которые мы проводим, включая победителей.
Спасибо за активное участие! ",
- "RafflesInfoTitle": "Сообщество АБП Розыгрыши"
+ "RafflesInfoTitle": "Сообщество АБП Розыгрыши",
+ "MarkdownSupported": "Markdown поддерживается.",
+ "Preview": "Предварительный просмотр"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json
index 6ab3c9d074..25a73a6bd7 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json
+++ b/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": " Toto je stránka žrebovania venovaná na vyjadrenie nášho uznania vám za to, že ste aktívnym členom komunity. Robíme ABP Community Talks, ABP .NET konferenciu , zúčastňujeme sa alebo sponzorujeme podujatia súvisiace s .NET, na ktorých rozdávame nejaké darčeky.
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.
Ďakujeme, že ste aktívnym členom! Vidíme sa v najbližšom žrebovaní.",
- "RafflesInfoTitle": "Komunitné tomboly ABP"
+ "RafflesInfoTitle": "Komunitné tomboly ABP",
+ "MarkdownSupported": "Markdown podporované.",
+ "Preview": "Náhľad"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json
index 23763c6f78..b6192142c6 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json
@@ -255,6 +255,8 @@
"LatestArticles": "Najnovejši članki",
"RaffleHeader": "Pozdravljeni član skupnosti ABP!",
"RafflesInfo": " To je stran za nagradno igro, namenjena izkazovanju naše hvaležnosti do vas, ker ste aktivni član skupnosti. Izvajamo ABP Community Talks, ABP .NET Conference , se udeležujemo ali sponzoriramo dogodke, povezane z .NET, na katerih podarimo nekaj daril.
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.
Hvala, ker ste aktivni član! Se vidimo na prihajajočih nagradnih igrah.",
- "RafflesInfoTitle": "Žrebanje skupnosti ABP"
+ "RafflesInfoTitle": "Žrebanje skupnosti ABP",
+ "MarkdownSupported": "Markdown podprt.",
+ "Preview": "Predogled"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json
index 5ebc449cff..062f4f328e 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json
@@ -207,9 +207,8 @@
"NoActiveRaffleDescription": "Şu anda aktif bir çekiliş mevcut değildir.",
"RaffleSubscriptionCodeInputMessage": "Bu çekiliş bir kayıt kodu gerektiriyor. Lütfen kayıt kodunu aşağıya girin:",
"RaffleSubscriptionCodeInputErrorMessage": "Kayıt kodu hatalı. Lütfen tekrar deneyin.",
- "GoodJob!": "İyi iş!"
-
-
-
+ "GoodJob!": "İyi iş!",
+ "MarkdownSupported": "Markdown destekleniyor.",
+ "Preview": "Ön izleme"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json
index a36ec9b0e8..e195a80fa5 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json
+++ b/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": " Đâ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 các buổi nói chuyện cộng đồng ABP, Hội nghị ABP .NET , 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.
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.
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": "Xổ số cộng đồng ABP"
+ "RafflesInfoTitle": "Xổ số cộng đồng ABP",
+ "MarkdownSupported": "Markdown được hỗ trợ.",
+ "Preview": "Xem trước"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
index e899a94872..fd1130b894 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
@@ -255,6 +255,8 @@
"LatestArticles": "最新文章",
"RaffleHeader": "你好,ABP 社区成员!",
"RafflesInfo": " 这是一个抽奖页面,旨在感谢您成为活跃的社区成员。我们举办 ABP 社区讲座、ABP .NET 会议,参加或赞助与 .NET 相关的活动,并在活动中赠送一些礼品。
您可以关注此页面,查看即将举行的抽奖活动、参加这些活动或查看我们以前举行的抽奖活动(包括获奖者)。
感谢您成为我们的活跃成员!在即将举行的抽奖活动中再见。",
- "RafflesInfoTitle": "ABP 社区来抽奖来抽奖Markdown 支持的。",
+ "Preview": "预览"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json
index ebec42dd8a..e5725ce996 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json
@@ -255,6 +255,8 @@
"LatestArticles": "最新的文章",
"RaffleHeader": "ABP 社群成員您好!",
"RafflesInfo": " 這是抽獎頁面,旨在表達我們對您作為活躍社區成員的感謝。我們舉辦ABP 社群講座、ABP .NET 會議、參加或贊助 .NET 相關活動,並在活動中贈送一些禮物。
您可以關注此頁面查看即將舉行的抽獎活動、參加抽獎活動或查看我們之前抽獎的抽獎活動(包括獲獎者)。
感謝您成為活躍會員!在即將到來的抽獎活動中再見。",
- "RafflesInfoTitle": "ABP 社區萊佛士"
+ "RafflesInfoTitle": "ABP 社區萊佛士",
+ "MarkdownSupported": "Markdown 支持的。",
+ "Preview": "预览"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
index e1179bd70b..19fbda3790 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
+++ b/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.",
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
index 9fb6a08b32..88d56a6303 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
+++ b/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 框架构建稳健、可维护、模块化和可扩展的软件解决方案。",
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hant.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hant.json
index 81664b5f8d..dc396ce8b9 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hant.json
+++ b/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 框架建構健壯、可維護、模組化和可擴展的軟體解決方案。",
diff --git a/common.props b/common.props
index d976cecd0d..de934818f0 100644
--- a/common.props
+++ b/common.props
@@ -1,7 +1,7 @@
latest
- 8.1.0
+ 8.2.0$(NoWarn);CS1591;CS0436https://abp.io/assets/abp_nupkg.pnghttps://abp.io/
diff --git a/docs/en/API/API-Versioning.md b/docs/en/API/API-Versioning.md
index 68343417de..d0f94f2259 100644
--- a/docs/en/API/API-Versioning.md
+++ b/docs/en/API/API-Versioning.md
@@ -1,6 +1,6 @@
# API Versioning System
-ABP Framework integrates the [ASPNET-API-Versioning](https://github.com/dotnet/aspnet-api-versioning/wiki) feature and adapts to C# and JavaScript Static Client Proxies and [Auto API Controller](API/Auto-API-Controllers.md).
+ABP Framework integrates the [ASPNET-API-Versioning](https://github.com/dotnet/aspnet-api-versioning/wiki) feature and adapts to C# and JavaScript Static Client Proxies and [Auto API Controller](../API/Auto-API-Controllers.md).
## Enable API Versioning
diff --git a/docs/en/Community-Articles/2024-01-04-CLI-Online-Translate/Post.md b/docs/en/Community-Articles/2024-01-04-CLI-Online-Translate/Post.md
new file mode 100644
index 0000000000..285d7d5dc9
--- /dev/null
+++ b/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:
+
+
+
+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!
diff --git a/docs/en/Community-Articles/2024-01-04-CLI-Online-Translate/deepl.jpg b/docs/en/Community-Articles/2024-01-04-CLI-Online-Translate/deepl.jpg
new file mode 100644
index 0000000000..c54b9840f0
Binary files /dev/null and b/docs/en/Community-Articles/2024-01-04-CLI-Online-Translate/deepl.jpg differ
diff --git a/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/POST.md b/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/POST.md
new file mode 100644
index 0000000000..a5f2c694ed
--- /dev/null
+++ b/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/POST.md
@@ -0,0 +1,198 @@
+# ABP Now Supports Keyed Services!
+
+In this post, I describe the new **"keyed service"** support for the dependency injection container, which came with .NET 8.0. Then, I'll show you an example of usage within the ABP Framework.
+
+## What Are Keyed Services?
+
+ASP.NET Core ships with a built-in dependency injection container, which is a pretty basic DI container that supports minimal features a dependency injection container is supposed to have. For that reason, most of the .NET users use third-party containers like [Autofac](https://autofac.org/), or Ninject.
+
+> ABP Framework uses Autofac by default in startup templates, and it supports more features. For example, the built-in DI container does not natively support property injection but Autofac does.
+
+[**Keyed dependency injection (DI) services**](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0#keyed-services) were added to the built-in DI container as a new feature with .NET 8.0. This is an important feature, which allows for registering and retrieving DI services using keys/names.
+
+Prior to this version, you could register multiple services as follows:
+
+```csharp
+var builder = WebApplication.CreateBuilder(args);
+
+//service registrations
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+```
+
+But without keyed services, you could only retrieve all of the services like this:
+
+```csharp
+public class BookService(IEnumerable services) {}
+```
+
+Or the last registered one (`MyServiceThree` in our case):
+
+```csharp
+public class BookService(IMyService service) {}
+```
+
+There was not a simple way to retrieve a specific implementation type directly. With keyed services, now we can register services with a key and then resolve them by the key anywhere we want and retrieve the only one that we want to use.
+
+## ABP Framework Now Supports Keyed Services!
+
+Autofac was already supporting the named/keyed DI services for a long time and with v9.0.0, they made it compatible with the `Microsoft.Extensions.DependencyInjection` package (including the keyed service support).
+
+After the v9.0.0 was released for Autofac, then the ABP Framework Core team immediately updated the `Autofac.Extensions.DependencyInjection` package to `v9.0.0`, made the related changes in its own Autofac package and included in the v8.0.2 release.
+
+You can see the related changes in the following PRs:
+
+* https://github.com/abpframework/abp/pull/18775
+* https://github.com/abpframework/abp/pull/18777
+
+## Using Keyed Services with ABP Framework
+
+After the quick background of the process and the feature itself, now let's see the keyed services in action.
+
+First, update the ABP CLI to v8.0.2 with the following command (if your CLI version is newer, no need to apply this):
+
+```bash
+dotnet tool update -g Volo.Abp.Cli --version 8.0.2
+```
+
+Then, we can create an application template (single-layer) with the following command (MVC as the UI option and EF Core as the DB provider):
+
+```bash
+abp new KeyedServiceDemo -t app-nolayers -csf --version 8.0.2
+```
+
+After the application is created, we can open it in an IDE and start developing...
+
+**Example**:
+
+Let's assume that we have an interface called `IMyService`, which has three implementation types `MyServiceOne`, `MyServiceTwo` and `MyServiceThree`:
+
+```csharp
+public interface IMyService
+{
+ string GetMessage();
+}
+
+public class MyServiceOne : IMyService
+{
+ public string GetMessage() => "MyServiceOne";
+}
+
+public class MyServiceTwo : IMyService
+{
+ public string GetMessage() => "MyServiceTwo";
+}
+
+public class MyServiceThree : IMyService
+{
+ public string GetMessage() => "MyServiceThree";
+}
+```
+
+After creating our services, now we can register them in our module class. So, open the module class and add the following lines in the `ConfigureServices` method:
+
+```csharp
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ //code abbreviation for clarity...
+
+ context.Services.AddKeyedTransient("myserviceone");
+ context.Services.AddKeyedTransient("myservicetwo");
+ context.Services.AddKeyedTransient("myservicethree");
+ }
+```
+
+* To register a keyed service, you can use one of the `AddKeyedTransient`, `AddKeyedScoped`, or `AddKeyedSingleton` overloads, and provide a key for the registration.
+* In the example above, I used `AddKeyedTransient` overload, and registered all of these services as in the `Transient` lifetime.
+
+Then, when you want to retrieve a keyed service, you can use the `[FromKeyedServices(object key)]` attribute:
+
+```csharp
+public class NotificationService([FromKeyedServices("myserviceone")] IMyService myServiceOne)
+{
+ public string Notify() => myServiceOne.GetMessage();
+}
+```
+
+With this kind of use, you can be certain that not the latest registered service is resolved and instead the keyed service will be resolved, and then you can use it.
+
+Also, you can use the `FromKeyedServicesAttribute` with minimal APIs as follows:
+
+```csharp
+public override void OnApplicationInitialization(ApplicationInitializationContext context)
+{
+ //code abbreviation for clarity...
+
+ app.UseConfiguredEndpoints(endpoints =>
+ {
+ endpoints.MapGet("/my-service", ([FromKeyedServices("myserviceone")] IMyService myservice) => myservice.GetMessage());
+ });
+}
+```
+
+That's how it's easy to register multiple services in a keyed fashion and resolve them by keys!
+
+## Advanced
+
+In this section, I want to briefly mention some advanced topics (relatively 😀), such as what happens if I register more than one service with the same key, or is there a way to get the keyed services from the `ServiceProvider` etc.
+
+### Registering Multiple Services with the Same Key
+
+Let's assume that you mistakenly registered multiple services with the same key:
+
+```csharp
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ //code abbreviation for clarity...
+
+ context.Services.AddKeyedTransient("myserviceone");
+ context.Services.AddKeyedTransient("myserviceone");
+ }
+```
+
+In that case, when you try to resolve the dependency with the key, then the last registered one will be used without having a problem:
+
+```csharp
+//it will resolve the `MyServiceTwo` service!
+endpoints.MapGet("/my-service", ([FromKeyedServices("myserviceone")] IMyService myservice) => myservice.GetMessage());
+```
+
+Also, since you used the same key for multiple services, then you can inject the `IEnumerable` to get all services for a certain key and use them:
+
+```csharp
+//it will resolve both `MyServiceOne` and `MyServiceTwo` services!
+endpoints.MapGet("/my-service", ([FromKeyedServices("myserviceone")] IEnumerable myservices) =>
+{
+ var sb = new StringBuilder();
+ foreach (var myService in myservices)
+ {
+ sb.AppendLine(myService.GetMessage());
+ }
+
+ return sb.ToString();
+});
+```
+
+### Resolving Keyed Services from ServiceProvider & LazyServiceProvider
+
+You can resolve keyed services by using one of the extension methods (`.GetKeyedServices`, `.GetKeyedService<>`, `.GetRequiredKeyedService<>`, ...):
+
+```csharp
+//uses the `MyServiceTwo` service!
+var myService = ServiceProvider.GetRequiredKeyedService("myserviceone");
+```
+
+On the other hand, resolving keyed services from `LazyServiceProvider` is not supported in v8.0.2, but there is a PR, which you can find at https://github.com/abpframework/abp/pull/18792, it will fix this problem and it will be included in the next version.
+
+### Automatically Registering Keyed Services
+
+Currently, if you want to register a keyed service, you need to do it manually as we see in the previous sections by using one of the overloads (`.AddKeyedTransient`, `.AddKeyedScoped` and `.AddKeyedSingleton`).
+
+It would be good if we could make this process automatically and not need to manually register services, and for that purpose, I have [created an issue](https://github.com/abpframework/abp/issues/18794) that aims to introduce an attribute, which allows us to automatically register multiple services as keyed services.
+
+You can [follow the issue](https://github.com/abpframework/abp/issues/18794) if you are considering using keyed services in your application and don't want to register them manually.
+
+## Summary
+
+In this post, I described the new **"keyed service"** support added to the built-in dependency injection container that was released in .NET 8, and wanted to announce it's being supported from v8.0.2 for ABP Framework. It's a really good addition to the built-in dependency injection container and can be pretty useful for certain use-cases.
diff --git a/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/cover-image.png b/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/cover-image.png
new file mode 100644
index 0000000000..56b04d353c
Binary files /dev/null and b/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/cover-image.png differ
diff --git a/docs/en/Dependency-Injection.md b/docs/en/Dependency-Injection.md
index 3e49c0814f..826d360e22 100644
--- a/docs/en/Dependency-Injection.md
+++ b/docs/en/Dependency-Injection.md
@@ -143,6 +143,50 @@ public class TaxCalculator : ITaxCalculator, ITransientDependency
}
````
+### ExposeKeyedService Attribute
+
+`ExposeKeyedServiceAttribute` is used to control which keyed services are provided by the related class. Example:
+
+````C#
+[ExposeKeyedService("taxCalculator")]
+[ExposeKeyedService("calculator")]
+public class TaxCalculator: ICalculator, ITaxCalculator, ICanCalculate, ITransientDependency
+{
+}
+````
+
+In the example above, the `TaxCalculator` class exposes the `ITaxCalculator` interface with the key `taxCalculator` and the `ICalculator` interface with the key `calculator`. That means you can get keyed services from the `IServiceProvider` as shown below:
+
+````C#
+var taxCalculator = ServiceProvider.GetRequiredKeyedService("taxCalculator");
+var calculator = ServiceProvider.GetRequiredKeyedService("calculator");
+````
+
+Also, you can use the [`FromKeyedServicesAttribute`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.fromkeyedservicesattribute?view=dotnet-plat-ext-8.0) to resolve a certain keyed service in the constructor:
+
+```csharp
+public class MyClass
+{
+ //...
+
+ public MyClass([FromKeyedServices("taxCalculator")] ITaxCalculator taxCalculator)
+ {
+ TaxCalculator = taxCalculator;
+ }
+}
+```
+
+> Notice that the `ExposeKeyedServiceAttribute` only exposes the keyed services. So, you can not inject the `ITaxCalculator` or `ICalculator` interfaces in your application without using the `FromKeyedServicesAttribute` as shown in the example above. If you want to expose both keyed and non-keyed services, you can use the `ExposeServicesAttribute` and `ExposeKeyedServiceAttribute` attributes together as shown below:
+
+````C#
+[ExposeKeyedService("taxCalculator")]
+[ExposeKeyedService("calculator")]
+[ExposeServices(typeof(ITaxCalculator), typeof(ICalculator))]
+public class TaxCalculator: ICalculator, ITaxCalculator, ICanCalculate, ITransientDependency
+{
+}
+````
+
### Manually Registering
In some cases, you may need to register a service to the `IServiceCollection` manually, especially if you need to use custom factory methods or singleton instances. In that case, you can directly add services just as [Microsoft documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection) describes. Example:
diff --git a/docs/en/Getting-Started-Create-Solution-Single-Layer.md b/docs/en/Getting-Started-Create-Solution-Single-Layer.md
index d6ed14600d..8c4a6d0d88 100644
--- a/docs/en/Getting-Started-Create-Solution-Single-Layer.md
+++ b/docs/en/Getting-Started-Create-Solution-Single-Layer.md
@@ -34,7 +34,7 @@ The solution structure is based on the [Single-Layer Startup Template](Startup-T
## MongoDB Transactions
-The [startup template](Startup-templates/Index.md) **disables** transactions in the `.MongoDB` project by default. If your MongoDB server supports transactions, you can enable it in the *YourProjectModule* class's `ConfigureMongoDB` method:
+The [startup template](Startup-Templates/Index.md) **disables** transactions in the `.MongoDB` project by default. If your MongoDB server supports transactions, you can enable it in the *YourProjectModule* class's `ConfigureMongoDB` method:
```csharp
Configure(options =>
diff --git a/docs/en/Getting-Started-Create-Solution.md b/docs/en/Getting-Started-Create-Solution.md
index 650d3163a0..45689b45fc 100644
--- a/docs/en/Getting-Started-Create-Solution.md
+++ b/docs/en/Getting-Started-Create-Solution.md
@@ -55,7 +55,7 @@ The solution has a layered structure (based on the [Domain Driven Design](Domain
## MongoDB Transactions
-The [startup template](Startup-templates/Index.md) **disables** transactions in the `.MongoDB` project by default. If your MongoDB server supports transactions, you can enable it in the *YourProjectMongoDbModule* class's `ConfigureServices` method:
+The [startup template](Startup-Templates/Index.md) **disables** transactions in the `.MongoDB` project by default. If your MongoDB server supports transactions, you can enable it in the *YourProjectMongoDbModule* class's `ConfigureServices` method:
```csharp
Configure(options =>
diff --git a/docs/en/Getting-Started-Running-Solution.md b/docs/en/Getting-Started-Running-Solution.md
index 921fb2b247..d71c0a64da 100644
--- a/docs/en/Getting-Started-Running-Solution.md
+++ b/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.
diff --git a/docs/en/Migration-Guides/Abp-8_0.md b/docs/en/Migration-Guides/Abp-8_0.md
index 0ab44b30c8..09e44b7ec8 100644
--- a/docs/en/Migration-Guides/Abp-8_0.md
+++ b/docs/en/Migration-Guides/Abp-8_0.md
@@ -171,17 +171,17 @@ You can see the following list of NuGet libraries that have been upgraded with .
| AWSSDK.SecurityToken | 3.7.1.151 | 3.7.300.2 |
| Azure.Messaging.ServiceBus | 7.8.1 | 7.17.0 |
| Azure.Storage.Blobs | 12.15.0 | 12.19.1 |
-| Blazorise | 1.3.1 | 1.3.2 |
-| Blazorise.Bootstrap5 | 1.3.1 | 1.3.2 |
-| Blazorise.Icons.FontAwesome | 1.3.1 | 1.3.2 |
-| Blazorise.Components | 1.3.1 | 1.3.2 |
-| Blazorise.DataGrid | 1.3.1 | 1.3.2 |
-| Blazorise.Snackbar | 1.3.1 | 1.3.2 |
+| Blazorise | 1.3.1 | 1.4.1 |
+| Blazorise.Bootstrap5 | 1.3.1 | 1.4.1 |
+| Blazorise.Icons.FontAwesome | 1.3.1 | 1.4.1 |
+| Blazorise.Components | 1.3.1 | 1.4.1 |
+| Blazorise.DataGrid | 1.3.1 | 1.4.1 |
+| Blazorise.Snackbar | 1.3.1 | 1.4.1 |
| Confluent.Kafka | 1.8.2 | 2.3.0 |
| 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 |
diff --git a/docs/en/Migration-Guides/Abp-8_1.md b/docs/en/Migration-Guides/Abp-8_1.md
new file mode 100644
index 0000000000..6f4c316d3f
--- /dev/null
+++ b/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
+///
+public partial class Add_NormalizedName : Migration
+{
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ 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");
+ }
+
+ ///
+ 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.
+
diff --git a/docs/en/MongoDB.md b/docs/en/MongoDB.md
index 372ad74ae6..0faa6a7d26 100644
--- a/docs/en/MongoDB.md
+++ b/docs/en/MongoDB.md
@@ -281,7 +281,7 @@ public class BookService
### Transactions
-MongoDB supports multi-document transactions starting from the version 4.0 and the ABP Framework supports it. However, the [startup template](Startup-templates/Index.md) **disables** transactions by default. If your MongoDB **server** supports transactions, you can enable the it in the *YourProjectMongoDbModule* class:
+MongoDB supports multi-document transactions starting from the version 4.0 and the ABP Framework supports it. However, the [startup template](Startup-Templates/Index.md) **disables** transactions by default. If your MongoDB **server** supports transactions, you can enable the it in the *YourProjectMongoDbModule* class:
```csharp
Configure(options =>
diff --git a/docs/en/Multi-Tenancy.md b/docs/en/Multi-Tenancy.md
index 661d99da16..922976767d 100644
--- a/docs/en/Multi-Tenancy.md
+++ b/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..."
}
diff --git a/docs/en/Themes/LeptonXLite/AspNetCore.md b/docs/en/Themes/LeptonXLite/AspNetCore.md
index 1c6baeddf9..8bdf85704f 100644
--- a/docs/en/Themes/LeptonXLite/AspNetCore.md
+++ b/docs/en/Themes/LeptonXLite/AspNetCore.md
@@ -94,7 +94,7 @@ public class MyProjectNameMainToolbarContributor : IToolbarContributor
ABP **helps** you make **highly customizable UI**. You can easily **customize** your themes to fit your needs. **The Virtual File System** makes it possible to **manage files** that **do not physically** exist on the **file system** (disk). It's mainly used to embed **(js, css, image..)** files into assemblies and **use them like** physical files at runtime. An application (or another module) can **override** a **virtual file of a module** just like placing a file with the **same name** and **extension** into the **same folder** of the **virtual file**.
-LeptonX Lite is built on the [Abp Framework](https://abp.io/), so you can **easily** customize your Asp.Net Core MVC user interface by following [Abp Mvc UI Customization](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-xUser-Interface).
+LeptonX Lite is built on the [Abp Framework](https://abp.io/), so you can **easily** customize your Asp.Net Core MVC user interface by following [Abp Mvc UI Customization](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface).
## Branding Component
diff --git a/docs/en/UI/Angular/HTTP-Error-Handling.md b/docs/en/UI/Angular/HTTP-Error-Handling.md
new file mode 100644
index 0000000000..606211bfeb
--- /dev/null
+++ b/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:
+
+
+
+### 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.
diff --git a/docs/en/UI/Angular/HTTP-Requests.md b/docs/en/UI/Angular/HTTP-Requests.md
index a0e53df9c9..9bef5cb8f2 100644
--- a/docs/en/UI/Angular/HTTP-Requests.md
+++ b/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`. 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` 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` 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` 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 = {
@@ -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:
-
- 
-
- - 404 not found errors is handled. When a 404 error occurs, `Error404Component` will be appended to the `` as shown below:
-
-
-
- - 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)
diff --git a/docs/en/UI/Angular/Page-Component.md b/docs/en/UI/Angular/Page-Component.md
index 912b516baf..81a2a2c5db 100644
--- a/docs/en/UI/Angular/Page-Component.md
+++ b/docs/en/UI/Angular/Page-Component.md
@@ -209,4 +209,4 @@ export class DashboardModule {}
## See Also
-- [Page Toolbar Extensions for Angular UI](./Page-Page-Toolbar-Extensions.md)
+- [Page Toolbar Extensions for Angular UI](./Page-Toolbar-Extensions.md)
\ No newline at end of file
diff --git a/docs/en/UI/Angular/Service-Proxies.md b/docs/en/UI/Angular/Service-Proxies.md
index 43b8dc8b0f..f6a34b76b8 100644
--- a/docs/en/UI/Angular/Service-Proxies.md
+++ b/docs/en/UI/Angular/Service-Proxies.md
@@ -85,9 +85,9 @@ export const environment: Config.Environment = {
### Services
-The `generate-proxy` command generates one service per back-end controller and a method (property with a function value actually) for each action in the controller. These methods call backend APIs via [RestService](./Http-Requests#restservice).
+The `generate-proxy` command generates one service per back-end controller and a method (property with a function value actually) for each action in the controller. These methods call backend APIs via [RestService](./HTTP-Requests#restservice).
-A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's `RemoteServiceName`. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./Http-Requests#how-to-get-a-specific-api-endpoint-from-application-config)
+A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's `RemoteServiceName`. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./HTTP-Requests#how-to-get-a-specific-api-endpoint-from-application-config)
The `providedIn` property of the services is defined as `'root'`. Therefore there is no need to provide them in a module. You can use them directly by injecting them into the constructor as shown below:
diff --git a/docs/en/UI/Angular/Theming.md b/docs/en/UI/Angular/Theming.md
index 435acc6930..a9d29bea02 100644
--- a/docs/en/UI/Angular/Theming.md
+++ b/docs/en/UI/Angular/Theming.md
@@ -20,7 +20,7 @@ Currently, three themes are **officially provided**:
* The [Basic Theme](Basic-Theme.md) is the minimalist theme with the plain Bootstrap style. It is **open source and free**.
* The [Lepton Theme](https://commercial.abp.io/themes) is a **commercial** theme developed by the core ABP team and is a part of the [ABP Commercial](https://commercial.abp.io/) license.
-* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has both [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/commercial/angular) and [lite](../../Themes/LeptonXLite/Angular.md) choices.
+* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has both [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/angular) and [lite](../../Themes/LeptonXLite/Angular.md) choices.
## Overall
diff --git a/docs/en/UI/AspNetCore/Customization-User-Interface.md b/docs/en/UI/AspNetCore/Customization-User-Interface.md
index 8b5a065e86..991f1247b8 100644
--- a/docs/en/UI/AspNetCore/Customization-User-Interface.md
+++ b/docs/en/UI/AspNetCore/Customization-User-Interface.md
@@ -63,7 +63,7 @@ The account module defines a `Login.cshtml` file under the `Pages/Account` folde
You typically want to copy the original `.cshtml` file of the module, then make the necessary changes. You can find the original file [here](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml). Do not copy the `Login.cshtml.cs` file which is the code behind file for the razor page and we don't want to override it yet (see the next section).
-> Don't forget to add [_ViewImports.cshtml](https://learn.microsoft.com/en-us/aspnet/core/mvc/views/layout?view=aspnetcore-7.0#importing-shared-directives) if the page you want to override contains [ABP Tag Helpers](../Tag-Helpers/Index.md).
+> Don't forget to add [_ViewImports.cshtml](https://learn.microsoft.com/en-us/aspnet/core/mvc/views/layout?view=aspnetcore-7.0#importing-shared-directives) if the page you want to override contains [ABP Tag Helpers](../AspNetCore/Tag-Helpers/Index.md).
````csharp
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
diff --git a/docs/en/UI/AspNetCore/Modals.md b/docs/en/UI/AspNetCore/Modals.md
index b0a0ba922e..95eaf2e404 100644
--- a/docs/en/UI/AspNetCore/Modals.md
+++ b/docs/en/UI/AspNetCore/Modals.md
@@ -186,7 +186,7 @@ For this example, creating a new Razor Page, named `ProductCreateModal.cshtml`,
````
* The `abp-modal` has been wrapped by the `form`. This is needed to place the `Save` and the `Cancel` buttons into the form. In this way, the `Save` button acts as the `submit` button for the `form`.
-* Used the [abp-input tag helpers](Tag-Helpers/Form-Elements.md) to simplify to create the form elements. Otherwise, you need to write more HTML.
+* Used the [abp-input tag helpers](Tag-Helpers/Form-elements.md) to simplify to create the form elements. Otherwise, you need to write more HTML.
**ProductCreateModal.cshtml.cs Content:**
@@ -247,7 +247,7 @@ namespace MyProject.Web.Pages.Products
}
````
-* `abp-input` Tag Helper can understand the data annotation attributes and uses them to shape and validate the form elements. See the [abp-input tag helpers](Tag-Helpers/Form-Elements.md) document to learn more.
+* `abp-input` Tag Helper can understand the data annotation attributes and uses them to shape and validate the form elements. See the [abp-input tag helpers](Tag-Helpers/Form-elements.md) document to learn more.
#### Defining the Modal Manager
diff --git a/docs/en/UI/AspNetCore/Overall.md b/docs/en/UI/AspNetCore/Overall.md
index bc24c56fb8..0c7e05c105 100644
--- a/docs/en/UI/AspNetCore/Overall.md
+++ b/docs/en/UI/AspNetCore/Overall.md
@@ -36,7 +36,7 @@ Currently, three themes are **officially provided**:
* The [Basic Theme](Basic-Theme.md) is the minimalist theme with the plain Bootstrap style. It is **open source and free**.
* The [Lepton Theme](https://commercial.abp.io/themes) is a **commercial** theme developed by the core ABP team and is a part of the [ABP Commercial](https://commercial.abp.io/) license.
-* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has both [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/commercial/mvc) and [lite](../../Themes/LeptonXLite/AspNetCore.md) choices.
+* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has both [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/mvc) and [lite](../../Themes/LeptonXLite/AspNetCore.md) choices.
There are also some community-driven themes for the ABP Framework (you can search on the web).
diff --git a/docs/en/UI/AspNetCore/Static-JavaScript-Proxies.md b/docs/en/UI/AspNetCore/Static-JavaScript-Proxies.md
index 20b87aa89b..fffc53cf83 100644
--- a/docs/en/UI/AspNetCore/Static-JavaScript-Proxies.md
+++ b/docs/en/UI/AspNetCore/Static-JavaScript-Proxies.md
@@ -49,7 +49,7 @@ Open a command-line terminal in the root folder of your web project (`.csproj`)
abp generate-proxy -t js -u https://localhost:53929/
````
-> If you haven't installed yet, you should install the [ABP CLI](../CLI.md). Change the example URL to your application's root URL.
+> If you haven't installed yet, you should install the [ABP CLI](../../CLI.md). Change the example URL to your application's root URL.
This command should generate the following files under the `ClientProxies` folder:
diff --git a/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md b/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
index 5be57bd4bc..7f22bf9b8f 100644
--- a/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
+++ b/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
@@ -58,7 +58,7 @@ Model:
### Attributes
-You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
+You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-Forms.md), then you can only set these properties via property attributes.
#### Property Attributes
@@ -103,7 +103,7 @@ You can set the label of the input in several ways:
`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [ASP.NET Core Select Tag Helper](https://docs.microsoft.com/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of ASP.NET Core is also valid for `abp-select`.
-`abp-select` tag needs a list of `Microsoft.AspNetCore.Mvc.Rendering.SelectListItem ` to work. It can be provided by `asp-items` attriube on the tag or `[SelectItems()]` attribute on c# property. (if you are using [abp-dynamic-form](Dynamic-forms.md), c# attribute is the only way.)
+`abp-select` tag needs a list of `Microsoft.AspNetCore.Mvc.Rendering.SelectListItem ` to work. It can be provided by `asp-items` attriube on the tag or `[SelectItems()]` attribute on c# property. (if you are using [abp-dynamic-form](Dynamic-Forms.md), c# attribute is the only way.)
`abp-select` supports multiple selection.
@@ -171,7 +171,7 @@ Model:
### Attributes
-You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
+You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-Forms.md), then you can only set these properties via property attributes.
#### Property Attributes
@@ -252,7 +252,7 @@ Model:
### Attributes
-You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
+You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-Forms.md), then you can only set these properties via property attributes.
#### Property Attributes
@@ -325,7 +325,7 @@ Model:
### Attributes
-You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
+You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-Forms.md), then you can only set these properties via property attributes.
#### Property Attributes
diff --git a/docs/en/UI/Blazor/Overall.md b/docs/en/UI/Blazor/Overall.md
index f38c0c257b..ac032e3b23 100644
--- a/docs/en/UI/Blazor/Overall.md
+++ b/docs/en/UI/Blazor/Overall.md
@@ -79,7 +79,7 @@ Currently, three themes are **officially provided**:
* The [Basic Theme](Basic-Theme.md) is the minimalist theme with the plain Bootstrap style. It is **open source and free**.
* The [Lepton Theme](https://commercial.abp.io/themes) is a **commercial** theme developed by the core ABP team and is a part of the [ABP Commercial](https://commercial.abp.io/) license.
-* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has both [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/commercial/blazor) and [lite](../../Themes/LeptonXLite/Blazor.md) choices.
+* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has both [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/blazor) and [lite](../../Themes/LeptonXLite/Blazor.md) choices.
### Base Libraries
diff --git a/docs/en/UI/Blazor/Theming.md b/docs/en/UI/Blazor/Theming.md
index 2496df9b20..50bb0d454e 100644
--- a/docs/en/UI/Blazor/Theming.md
+++ b/docs/en/UI/Blazor/Theming.md
@@ -27,7 +27,7 @@ Currently, three themes are **officially provided**:
* The [Basic Theme](Basic-Theme.md) is the minimalist theme with the plain Bootstrap style. It is **open source and free**.
* The [Lepton Theme](https://commercial.abp.io/themes) is a **commercial** theme developed by the core ABP team and is a part of the [ABP Commercial](https://commercial.abp.io/) license.
-* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has a [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/commercial/blazor) and a [lite](../../Themes/LeptonXLite/Blazor.md) version.
+* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has a [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/blazor) and a [lite](../../Themes/LeptonXLite/Blazor.md) version.
## Overall
diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json
index 8e766c7430..eeefa3d06e 100644
--- a/docs/en/docs-nav.json
+++ b/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",
@@ -1085,7 +1094,7 @@
},
{
"text": "Account Module",
- "path": "UI/Angular/Account-Module"
+ "path": "UI/Angular/Account-Module.md"
}
]
},
@@ -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"
}
]
},
diff --git a/docs/pt-BR/MongoDB.md b/docs/pt-BR/MongoDB.md
index bc4a7882a7..c7ad2ef1c3 100644
--- a/docs/pt-BR/MongoDB.md
+++ b/docs/pt-BR/MongoDB.md
@@ -279,7 +279,7 @@ public class BookService
### Transactions
-O MongoDB oferece suporte multi-document transactions a partir da versão 4.0 e o ABP Framework oferece suporte para isso. No entanto, o [startup template](Startup-templates/Index.md) **desativa** transactions por padrão. Se o seu **servidor** MongoDB suportar transactions, você pode habilitar esse recurso na classe *YourProjectMongoDbModule*:
+O MongoDB oferece suporte multi-document transactions a partir da versão 4.0 e o ABP Framework oferece suporte para isso. No entanto, o [startup template](Startup-Templates/Index.md) **desativa** transactions por padrão. Se o seu **servidor** MongoDB suportar transactions, você pode habilitar esse recurso na classe *YourProjectMongoDbModule*:
```csharp
Configure(options =>
diff --git a/docs/zh-Hans/Getting-Started-Create-Solution.md b/docs/zh-Hans/Getting-Started-Create-Solution.md
index 55906add7e..5b83f594e9 100644
--- a/docs/zh-Hans/Getting-Started-Create-Solution.md
+++ b/docs/zh-Hans/Getting-Started-Create-Solution.md
@@ -55,7 +55,7 @@ abp new Acme.BookStore{{if UI == "NG"}} -u angular{{else if UI == "Blazor"}} -u
#### MongoDB 事务
-[启动模板](Startup-templates/Index.md) 默认在`.MongoDB`项目中**禁用**事务. 如果你的MongoDB服务器支持事务, 你可以在*YourProjectMongoDbModule*类中的`ConfigureServices`方法开启它:
+[启动模板](Startup-Templates/Index.md) 默认在`.MongoDB`项目中**禁用**事务. 如果你的MongoDB服务器支持事务, 你可以在*YourProjectMongoDbModule*类中的`ConfigureServices`方法开启它:
```csharp
Configure(options =>
diff --git a/docs/zh-Hans/MongoDB.md b/docs/zh-Hans/MongoDB.md
index 87c99d3207..540793dd1f 100644
--- a/docs/zh-Hans/MongoDB.md
+++ b/docs/zh-Hans/MongoDB.md
@@ -238,7 +238,7 @@ public class BookService
MongoDB在4.0版本开始支持事务, ABP在3.2版本加入了对MongoDb事务的支持. 如果你升级到3.2版本,需要将[MongoDbSchemaMigrator](https://github.com/abpframework/abp/blob/dev/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MongoDbMyProjectNameDbSchemaMigrator.cs)添加到你的 `.MongoDB` 项目中.
-[启动模板](Startup-templates/Index.md)默认在 `.MongoDB` 项目中**禁用**了工作单元事务. 如果你的MongoDB服务器支持事务,你可以手动启用工作单元的事务:
+[启动模板](Startup-Templates/Index.md)默认在 `.MongoDB` 项目中**禁用**了工作单元事务. 如果你的MongoDB服务器支持事务,你可以手动启用工作单元的事务:
```csharp
Configure(options =>
diff --git a/docs/zh-Hans/UI/Angular/Service-Proxies.md b/docs/zh-Hans/UI/Angular/Service-Proxies.md
index cc07a24175..17b04823f3 100644
--- a/docs/zh-Hans/UI/Angular/Service-Proxies.md
+++ b/docs/zh-Hans/UI/Angular/Service-Proxies.md
@@ -26,7 +26,7 @@ abp generate-proxy -t ng
### Services
-每个生成的服务都与后端控制器匹配. 服务方法通过[RestService](./Http-Requests#restservice)调用后端API.
+每个生成的服务都与后端控制器匹配. 服务方法通过[RestService](./HTTP-Requests#restservice)调用后端API.
在每个服务中都定义了一个名为 `apiName` 的变量(自v2.4起可用). `apiName` 与模块的 `RemoteServiceName` 匹配. 在每次请求时该变量将作为参数传递给 `RestService`. 如果环境中未定义微服务API, `RestService` 使用默认值. 请参阅[从应用程序配置中获取特定的API端点](./Http-Requests#how-to-get-a-specific-api-endpoint-from-application-config)
@@ -65,4 +65,4 @@ const instance = new IdentityRoleCreateDto({name: 'Role 1', isDefault: false, is
## 下一步是什么?
-* [HTTP请求](./Http-Requests)
\ No newline at end of file
+* [HTTP请求](./HTTP-Requests)
diff --git a/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Form-elements.md b/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Form-elements.md
index a5629fb16a..57e66cc888 100644
--- a/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Form-elements.md
+++ b/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Form-elements.md
@@ -59,7 +59,7 @@ Model:
### 特性(Attributes)
-你可以在你的c#属性上设置一些特性,或者直接在html标签上设置。如果您要在[abp-dynamic-form](Dynamic-forms.md)中使用此属性,则只能通过属性特性设置这些属性。
+你可以在你的c#属性上设置一些特性,或者直接在html标签上设置。如果您要在[abp-dynamic-form](Dynamic-Forms.md)中使用此属性,则只能通过属性特性设置这些属性。
#### 属性特性(Property Attributes)
@@ -104,7 +104,7 @@ Model:
`abp-select` 标签为给定的 C# 属性创建了一个 Bootstrap 表单选择器。它在后台使用 [Asp.Net Core 选择标签助手](https://docs.microsoft.com/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-select-tag-helper),因此 `Asp.Net Core` 的 `select` 标签助手的每个数据注释属性也适用于 `abp-select`。
-`abp-select` 标签需要一个 `Microsoft.AspNetCore.Mvc.Rendering.SelectListItem` 的列表来工作。它可以通过标签上的 `asp-items` 属性或 C# 属性上的 `[SelectItems()]` 属性来提供(如果您使用的是 [abp-dynamic-form](Dynamic-forms.md),则只能使用 C# 属性的方式。)
+`abp-select` 标签需要一个 `Microsoft.AspNetCore.Mvc.Rendering.SelectListItem` 的列表来工作。它可以通过标签上的 `asp-items` 属性或 C# 属性上的 `[SelectItems()]` 属性来提供(如果您使用的是 [abp-dynamic-form](Dynamic-Forms.md),则只能使用 C# 属性的方式。)
`abp-select` 支持多重选择。
@@ -172,7 +172,7 @@ Model:
### 属性
-您可以在 C# 属性上或直接在 HTML 标签上设置一些属性。如果您将在 [abp-dynamic-form](Dynamic-forms.md) 中使用此属性,则只能通过属性属性设置这些属性。
+您可以在 C# 属性上或直接在 HTML 标签上设置一些属性。如果您将在 [abp-dynamic-form](Dynamic-Forms.md) 中使用此属性,则只能通过属性属性设置这些属性。
#### 属性特性(Property Attributes)
@@ -255,7 +255,7 @@ Model:
### 特性(attributes)
-您可以在C#属性或直接在HTML标签上设置一些属性。如果您将在[abp-dynamic-form](Dynamic-forms.md)中使用此属性,则只能通过属性属性设置这些属性。
+您可以在C#属性或直接在HTML标签上设置一些属性。如果您将在[abp-dynamic-form](Dynamic-Forms.md)中使用此属性,则只能通过属性属性设置这些属性。
#### 属性特性(Property Attributes)
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorRemoteTenantStore.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorRemoteTenantStore.cs
index d17a2b6f87..3873b48db3 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorRemoteTenantStore.cs
+++ b/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 FindAsync(string name)
+ public async Task 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}";
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/MultiTenancyMiddleware.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/MultiTenancyMiddleware.cs
index 310add4bfc..520e2944f6 100644
--- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/MultiTenancyMiddleware.cs
+++ b/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 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
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcRemoteTenantStore.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcRemoteTenantStore.cs
index e0c450b8b4..1bc7856ca6 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcRemoteTenantStore.cs
+++ b/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 FindAsync(string name)
+ public async Task 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);
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/FindTenantResultDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/FindTenantResultDto.cs
index 599b0b8a67..1de5f942dc 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/FindTenantResultDto.cs
+++ b/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; }
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs
index 671b00e094..8b46f6e78c 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs
@@ -90,13 +90,13 @@ public class AbpModalFooterTagHelperService : AbpTagHelperService 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
};
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs
index f6711be826..94a6760df3 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs
+++ b/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 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!]);
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js
index 51c7db5cd9..4a11feeda2 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js
+++ b/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;
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
index c9b5984843..a2f2c1c989 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
+++ b/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());
context.Services.AddSingleton();
+ context.Services.TryAddEnumerable(ServiceDescriptor.Transient());
context.Services.AddOptions()
.Configure((mvcOptions, serviceProvider) =>
{
@@ -247,7 +250,7 @@ public class AbpAspNetCoreMvcModule : AbpModule
.Distinct();
AddToApplicationParts(partManager, controllerAssemblies);
-
+
var additionalAssemblies = moduleContainer
.Modules
.SelectMany(m => m.GetAdditionalAssemblies())
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcActionDescriptorProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcActionDescriptorProvider.cs
new file mode 100644
index 0000000000..1ae2180e47
--- /dev/null
+++ b/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())
+ {
+ var disableAbpFeaturesAttribute = action.ControllerTypeInfo.GetCustomAttribute(true);
+ if (disableAbpFeaturesAttribute != null && disableAbpFeaturesAttribute.DisableMvcFilters)
+ {
+ action.FilterDescriptors.RemoveAll(x => x.Filter is ServiceFilterAttribute serviceFilterAttribute &&
+ typeof(IAbpFilter).IsAssignableFrom(serviceFilterAttribute.ServiceType));
+ }
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs
index 07101da0cb..d982adb12a 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs
index 8445be60ae..6b97ac980a 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs
index 61e37104ee..a63e557198 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs
index 31784cb6ea..97c21268fe 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeatureActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeatureActionFilter.cs
index b4b70a5e1a..35bb9ba572 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeatureActionFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs
index 3c586f762b..a35764c43c 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeatureActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeatureActionFilter.cs
index aae85e4d25..34f125f228 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeatureActionFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeaturePageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeaturePageFilter.cs
index 46455730a4..3c43301221 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeaturePageFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Response/AbpNoContentActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Response/AbpNoContentActionFilter.cs
index b0565cbc4d..455dd52f24 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Response/AbpNoContentActionFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs
index 14032b52d5..4a6a04a554 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs
index 7cfc1f0492..f4a7be3333 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/AbpValidationActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/AbpValidationActionFilter.cs
index e88cee4eb6..f866c85585 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/AbpValidationActionFilter.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpSerilogMiddleware.cs b/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpSerilogMiddleware.cs
index 1df327b742..52ee2e6b76 100644
--- a/framework/src/Volo.Abp.AspNetCore.Serilog/Volo/Abp/AspNetCore/Serilog/AbpSerilogMiddleware.cs
+++ b/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();
diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/AbpRequestLocalizationMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/AbpRequestLocalizationMiddleware.cs
index 6fd02a0470..cb531762ba 100644
--- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/AbpRequestLocalizationMiddleware.cs
+++ b/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,
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAuditingMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAuditingMiddleware.cs
index 0987a8fd8f..86cc997ae7 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAuditingMiddleware.cs
+++ b/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;
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs
index b9d3b83068..7d5ef610e5 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs
+++ b/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 _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
{
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Filters/IAbpFilter.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Filters/IAbpFilter.cs
new file mode 100644
index 0000000000..dbd55a8ed4
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Filters/IAbpFilter.cs
@@ -0,0 +1,6 @@
+namespace Volo.Abp.AspNetCore.Filters;
+
+public interface IAbpFilter
+{
+
+}
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Middleware/AbpMiddlewareBase.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Middleware/AbpMiddlewareBase.cs
new file mode 100644
index 0000000000..52c9990a4f
--- /dev/null
+++ b/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 ShouldSkipAsync(HttpContext context, RequestDelegate next)
+ {
+ var endpoint = context.GetEndpoint();
+ var controllerActionDescriptor = endpoint?.Metadata.GetMetadata();
+ var disableAbpFeaturesAttribute = controllerActionDescriptor?.ControllerTypeInfo.GetCustomAttribute();
+ return Task.FromResult(disableAbpFeaturesAttribute != null && disableAbpFeaturesAttribute.DisableMiddleware);
+ }
+
+ public abstract Task InvokeAsync(HttpContext context, RequestDelegate next);
+}
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs
index ae2e3b173b..4542dd32e1 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs
+++ b/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 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");
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs
index ab23bdb944..bb2bedf901 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs
+++ b/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();
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpDynamicClaimsMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpDynamicClaimsMiddleware.cs
index 777ca7744a..9c62fa8b25 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpDynamicClaimsMiddleware.cs
+++ b/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)
{
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AbpCorrelationIdMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AbpCorrelationIdMiddleware.cs
index a5f954c260..38356f8151 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AbpCorrelationIdMiddleware.cs
+++ b/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))
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Uow/AbpUnitOfWorkMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Uow/AbpUnitOfWorkMiddleware.cs
index 186501b4ac..013b3dad98 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Uow/AbpUnitOfWorkMiddleware.cs
+++ b/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;
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/WebContentFileProvider.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/WebContentFileProvider.cs
index ba9240d48b..fffc35792a 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/WebContentFileProvider.cs
+++ b/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;
}
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProviderManager.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProviderManager.cs
index 84d0ad0b2c..aebd14f36e 100644
--- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProviderManager.cs
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProviderManager.cs
@@ -13,19 +13,31 @@ public class PermissionValueProviderManager : IPermissionValueProviderManager, I
private readonly Lazy> _lazyProviders;
protected AbpPermissionOptions Options { get; }
+ protected IServiceProvider ServiceProvider { get; }
public PermissionValueProviderManager(
IServiceProvider serviceProvider,
IOptions options)
{
Options = options.Value;
+ ServiceProvider = serviceProvider;
- _lazyProviders = new Lazy>(
- () => Options
- .ValueProviders
- .Select(c => serviceProvider.GetRequiredService(c) as IPermissionValueProvider)
- .ToList()!,
- true
- );
+ _lazyProviders = new Lazy>(GetProviders, true);
+ }
+
+ protected virtual List 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;
}
}
diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs
index 043f824b1a..53216b03b9 100644
--- a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs
+++ b/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(true);
+ if (disableAbpFeaturesAttribute == null || !disableAbpFeaturesAttribute.DisableInterceptors)
+ {
+ registrationBuilder = registrationBuilder.AddInterceptors(
+ registrationActionList,
+ serviceType,
+ serviceRegistredArgs.Interceptors
+ );
+ }
}
return registrationBuilder;
diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs b/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs
index 17fd94c467..7804cbbd98 100644
--- a/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs
+++ b/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs
@@ -298,32 +298,4 @@ public static class AutofacRegistration
.ConfigureLifecycle(descriptor.Lifetime, null);
}
}
-
- ///
- /// Normalizes the implementation instance data between keyed and not keyed services.
- ///
- ///
- /// The to normalize.
- ///
- ///
- /// The appropriate implementation instance from the service descriptor.
- ///
- public static object? NormalizedImplementationInstance(this ServiceDescriptor descriptor)
- {
- return !descriptor.IsKeyedService ? descriptor.ImplementationInstance : descriptor.KeyedImplementationInstance;
- }
-
- ///
- /// Normalizes the implementation type data between keyed and not keyed services.
- ///
- ///
- /// The to normalize.
- ///
- ///
- /// The appropriate implementation type from the service descriptor.
- ///
- public static Type? NormalizedImplementationType(this ServiceDescriptor descriptor)
- {
- return !descriptor.IsKeyedService ? descriptor.ImplementationType : descriptor.KeyedImplementationType;
- }
}
diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/UiMessageAlert.razor b/framework/src/Volo.Abp.BlazoriseUI/Components/UiMessageAlert.razor
index 82fc576b7e..9951e616e2 100644
--- a/framework/src/Volo.Abp.BlazoriseUI/Components/UiMessageAlert.razor
+++ b/framework/src/Volo.Abp.BlazoriseUI/Components/UiMessageAlert.razor
@@ -22,7 +22,7 @@
@if ( IsConfirmation )
{
-