diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..9805011759 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,137 @@ +# EditorConfig is awesome: https://EditorConfig.org +# Please feel free to update it (by considering the code style of ABP Team). +# top-most EditorConfig file +root = true + +[*.cs] + +#Namespace settings +csharp_style_namespace_declarations = file_scoped +dotnet_diagnostic.IDE0161.severity = warning + +#Core editorconfig formatting - indentation + +#use tabs for indentation +indent_style = tabs + +#Formatting - indentation options + +#indent switch case contents. +csharp_indent_case_contents = true +#indent switch labels +csharp_indent_switch_labels = true + +#Formatting - new line options + +#place catch statements on a new line +csharp_new_line_before_catch = true +#place else statements on a new line +csharp_new_line_before_else = true +#require members of object intializers to be on separate lines +csharp_new_line_before_members_in_object_initializers = true +#require braces to be on a new line for object_collection_array_initializers, methods, control_blocks, types, and lambdas (also known as "Allman" style) +csharp_new_line_before_open_brace = object_collection_array_initializers, methods, control_blocks, types, lambdas + +#Formatting - organize using options + +#sort System.* using directives alphabetically, and place them before other usings +dotnet_sort_system_directives_first = true + +#Formatting - spacing options + +#require NO space between a cast and the value +csharp_space_after_cast = false +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_after_colon_in_inheritance_clause = true +#require a space after a keyword in a control flow statement such as a for loop +csharp_space_after_keywords_in_control_flow_statements = true +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_before_colon_in_inheritance_clause = true +#remove space within empty argument list parentheses +csharp_space_between_method_call_empty_parameter_list_parentheses = false +#remove space between method call name and opening parenthesis +csharp_space_between_method_call_name_and_opening_parenthesis = false +#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call +csharp_space_between_method_call_parameter_list_parentheses = false +#remove space within empty parameter list parentheses for a method declaration +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. +csharp_space_between_method_declaration_parameter_list_parentheses = false + +#Formatting - wrapping options + +#leave code block on single line +csharp_preserve_single_line_blocks = true + +#Style - Code block preferences + +#prefer curly braces even for one line of code +csharp_prefer_braces = true:suggestion + +#Style - expression bodied member options + +#prefer block bodies for constructors +csharp_style_expression_bodied_constructors = false:suggestion +#prefer block bodies for methods +csharp_style_expression_bodied_methods = false:suggestion +#prefer expression-bodied members for properties +csharp_style_expression_bodied_properties = true:suggestion + +#Style - expression level options + +#prefer out variables to be declared inline in the argument list of a method call when possible +csharp_style_inlined_variable_declaration = true:suggestion +#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_member_access = true:suggestion + +#Style - Expression-level preferences + +#prefer default over default(T) +csharp_prefer_simple_default_expression = true:suggestion +#prefer objects to be initialized using object initializers when possible +dotnet_style_object_initializer = true:suggestion +#prefer inferred tuple element names +dotnet_style_prefer_inferred_tuple_names = true:suggestion + +#Style - implicit and explicit types + +#prefer var over explicit type in all cases, unless overridden by another code style rule +csharp_style_var_elsewhere = true:suggestion +#prefer var is used to declare variables with built-in system types such as int +csharp_style_var_for_built_in_types = true:suggestion +#prefer var when the type is already mentioned on the right-hand side of a declaration expression +csharp_style_var_when_type_is_apparent = true:suggestion + +#Style - language keyword and framework type options + +#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion + +#Style - Miscellaneous preferences + +#prefer local functions over anonymous functions +csharp_style_pattern_local_over_anonymous_function = true:suggestion + +#Style - modifier options + +#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +#Style - Modifier preferences + +#when this rule is set to a list of modifiers, prefer the specified ordering. +csharp_preferred_modifier_order = public,protected,private,virtual,async,readonly,static,override,abstract:suggestion + +#Style - Pattern matching + +#prefer pattern matching instead of is expression with type casts +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion + +#Style - qualification options + +#prefer fields not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_field = false:suggestion +#prefer methods not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_method = false:suggestion +#prefer properties not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_property = false:suggestion diff --git a/.github/workflows/auto-pr.yml b/.github/workflows/auto-pr.yml index d524dbc519..c73819de4b 100644 --- a/.github/workflows/auto-pr.yml +++ b/.github/workflows/auto-pr.yml @@ -1,10 +1,10 @@ -name: Merge branch rel-5.1 with rel-5.0 +name: Merge branch dev with rel-5.1 on: push: branches: - - rel-5.0 + - rel-5.1 jobs: - merge-rel-5-1-with-rel-5-0: + merge-dev-with-rel-5-1: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -12,13 +12,13 @@ jobs: ref: rel-5.1 - name: Reset promotion branch run: | - git fetch origin rel-5.0:rel-5.0 - git reset --hard rel-5.0 + git fetch origin rel-5.1:rel-5.1 + git reset --hard rel-5.1 - name: Create Pull Request uses: peter-evans/create-pull-request@v3 with: - branch: auto-merge/rel-5-0/${{github.run_number}} - title: Merge branch rel-5.1 with rel-5.0 - body: This PR generated automatically to merge rel-5.1 with rel-5.0. Please review the changed files before merging to prevent any errors that may occur. + branch: auto-merge/rel-5-1/${{github.run_number}} + title: Merge branch dev with rel-5.1 + body: This PR generated automatically to merge dev with rel-5.1. Please review the changed files before merging to prevent any errors that may occur. reviewers: ${{github.actor}} token: ${{ github.token }} diff --git a/.gitignore b/.gitignore index 79d0ea6388..1faf2fac91 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ *.user *.userosscache *.sln.docstates -*.editorconfig + # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en-GB.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en-GB.json index 4ea99af7e1..f64b385908 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en-GB.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en-GB.json @@ -210,6 +210,10 @@ "Completed": "Completed", "Failed": "Failed", "PaymentRequestDeletionWarningMessage": "This payment request will be deleted. Do you confirm that?", - "Payment": "Payment" + "Payment": "Payment", + "Permission:SendWelcomeEmail": "Send Welcome Email", + "SendWelcomeEmail": "Send Welcome Email", + "SendWelcomeEmailWarningMessage": "Are you sure you want to send welcome email to the organization members?", + "SendWelcomeEmailSuccessMessage": "Welcome email sent successfully!" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index 923535cfdd..f926259297 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -352,6 +352,11 @@ "Volo.AbpIo.Commercial:030008": "Purchase date can be set only when status is Purchased!", "Volo.AbpIo.Commercial:030009": "User not found!", "Volo.AbpIo.Commercial:030010": "To purchase the trial license, first you need to activate your trial license!", - "Volo.AbpIo.Commercial:030011": "You cannot delete a trial license when it is purchased!" + "Volo.AbpIo.Commercial:030011": "You cannot delete a trial license when it is purchased!", + "Permission:SendWelcomeEmail": "Send Welcome Email", + "SendWelcomeEmail": "Send Welcome Email", + "SendWelcomeEmailWarningMessage": "Are you sure you want to send welcome email to the organization members?", + "SendWelcomeEmailSuccessMessage": "Welcome email sent successfully!", + "PaymentRequestId": "Payment Request Id" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json index 1bba0bb964..5942fca747 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json @@ -348,6 +348,10 @@ "Volo.AbpIo.Commercial:030008": "Satın alma tarihi yalnızca durum Satın Alındı olduğunda ayarlanabilir!", "Volo.AbpIo.Commercial:030009": "Kullanıcı bulunamadı!", "Volo.AbpIo.Commercial:030010": "Deneme lisansını satın almak için önce deneme lisansınızı etkinleştirmeniz gerekir!", - "Volo.AbpIo.Commercial:030011": "Satın alındığında bir deneme lisansını silemezsiniz!" + "Volo.AbpIo.Commercial:030011": "Satın alındığında bir deneme lisansını silemezsiniz!", + "Permission:SendWelcomeEmail": "Hoşgeldin Emaili Gönder", + "SendWelcomeEmail": "Hoşgeldin Emaili Gönder", + "SendWelcomeEmailWarningMessage": "Organizasyon üyelerine hoşgeldin emaili göndermek istediğinden emin misin?", + "SendWelcomeEmailSuccessMessage": "Hoşgeldin emaili başarıyla gönderilmiştir!" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json index 552e9bd889..5f76244a3c 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json @@ -197,14 +197,13 @@ "WhatHappensWhenLicenseEndsExplanation4": "لا يمكنك تثبيت وحدات وسمات جديدة مضافة إلى منصة ABP Commercial بعد انتهاء ترخيصك.", "WhatHappensWhenLicenseEndsExplanation5": "لا يمكنك استخدام مجموعة ABP.", "WhatHappensWhenLicenseEndsExplanation6": "لا يمكنك الحصول على الدعم المتميز بعد الآن.", - "WhatHappensWhenLicenseEndsExplanation7": "يمكنك تجديد اشتراكك إذا كنت ترغب في الاستمرار في الحصول على هذه المزايا. إذا قمت بتجديد اشتراكك في غضون شهر واحد بعد انتهاء صلاحية ترخيصك ، فسيتم تطبيق خصم بنسبة 20٪.", + "WhatHappensWhenLicenseEndsExplanation7": "يمكنك تمديد (تجديد) الترخيص الخاص بك إذا كنت ترغب في الاستمرار في الحصول على هذه المزايا. في حالة تمديد ترخيصك في غضون شهر واحد بعد انتهاء صلاحية الترخيص ، فسيتم تطبيق الخصومات التالية: ترخيص الفريق {0}٪ خصم ، ترخيص تجاري {1}٪ خصم ، ترخيص Enterprise {2}٪ خصم .", "WhenShouldIRenewMyLicense": "متى يجب أن أجدد رخصتي؟", - "WhenShouldIRenewMyLicenseExplanation1": "إذا جددت ترخيصك في غضون شهر واحد بعد انتهاء صلاحية الترخيص ، فسيتم تطبيق خصم بنسبة 20٪.", - "WhenShouldIRenewMyLicenseExplanation2": "إذا قمت بتجديد ترخيصك بعد شهر واحد من تاريخ انتهاء صلاحية الترخيص الخاص بك ، فسيكون سعر التجديد هو نفسه سعر شراء الترخيص ولن يكون هناك خصم على التجديد.", + "WhenShouldIRenewMyLicenseExplanation": "إذا جددت ترخيصك في غضون شهر واحد بعد انتهاء صلاحية الترخيص ، فسيتم تطبيق الخصومات التالية: ترخيص الفريق {0}٪ خصم ، ترخيص تجاري {1}٪ خصم ، ترخيص Enterprise {2}٪ خصم . إذا قمت بتجديد ترخيصك بعد شهر واحد من تاريخ انتهاء صلاحية الترخيص ، فسيكون سعر التجديد هو نفسه سعر شراء الترخيص ولن يكون هناك خصم على التجديد.", "TrialPlan": "هل لديك خطة تجريبية؟", "TrialPlanExplanation": "في الوقت الحالي ، لا تمتلك ABP Commercial خطة تجريبية. بالنسبة لتراخيص الفريق ، نقدم ضمان استرداد الأموال لمدة 30 يومًا. يمكنك فقط طلب استرداد الأموال في أول 30 يومًا. بالنسبة إلى تراخيص الأعمال والمؤسسات ، نقدم استردادًا بنسبة 60٪ في غضون 30 يومًا. وذلك لأن تراخيص Business and Enterprise تتضمن كود المصدر الكامل لجميع الوحدات والسمات.", "DoYouAcceptBankWireTransfer": "هل تقبل التحويل البنكي؟", - "DoYouAcceptBankWireTransferExplanation": "نعم ، نحن نقبل التحويل المصرفي.
بعد إرسال رسوم الترخيص عبر التحويل المصرفي ، أرسل لنا بريدًا إلكترونيًا على info@abp.io إيصالك ونوع الترخيص المطلوب. معلومات حسابنا المصرفي الدولي:", + "DoYouAcceptBankWireTransferExplanation": "نعم ، نحن نقبل التحويل البنكي.
بعد إرسال رسوم الترخيص عبر التحويل المصرفي ، أرسل لنا بريدًا إلكترونيًا على accounting@abp.io إيصالك ونوع الترخيص المطلوب. معلومات حسابنا المصرفي الدولي:", "HowToUpgrade": "كيفية ترقية التطبيقات الموجودة عند توفر إصدار جديد؟", "HowToUpgradeExplanation1": "عند إنشاء تطبيق جديد باستخدام ABP Commercial ، يتم استخدام جميع الوحدات والقوالب كحزم NuGet و NPM. لذلك ، يمكنك ترقية الحزم بسهولة عند توفر إصدار جديد.", "HowToUpgradeExplanation2": "بالإضافة إلى ترقيات NuGet/NPM القياسية ، يوفر ABP CLI أمر تحديث يقوم تلقائيًا بالعثور على جميع الحزم المتعلقة بـ ABP في الحل الخاص بك وترقيتها.", @@ -390,6 +389,8 @@ "TrialLicenseExpireMessage": "أنت تستخدم الترخيص التجريبي وستنتهي صلاحية الترخيص التجريبي في {0}.", "TryForFree": "حاول مجانا", "TrialLicenseExpiredInfo": "انتهت فترة الترخيص التجريبي الخاص بك!", - "CommercialNewsletterConfirmationMessage": "أوافق على البنود والشروط و سياسة الخصوصية ." + "CommercialNewsletterConfirmationMessage": "أوافق على البنود والشروط و سياسة الخصوصية .", + "RenewLicenseEarly": "إذا قمت بتجديد رخصتي في وقت مبكر ، هل سأحصل على السنة كاملة؟", + "RenewLicenseEarylExplanation": "عند تجديد الترخيص الخاص بك قبل تاريخ انتهاء الترخيص الخاص بك ، ستتم إضافة سنة واحدة إلى تاريخ انتهاء الترخيص الخاص بك. على سبيل المثال ، إذا انتهت صلاحية ترخيصك في {0} -06-06 وقمت بتجديده في {0} -01-01 ، فسيكون تاريخ انتهاء صلاحية الترخيص الجديد {1} -06-06." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json index 793a35f5ef..16d061bd48 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Jaký je rozdíl mezi rozšířením licence a upgradem?", "LicenseExtendUpgradeDiffExplanation": "Prodloužení: Prodloužením/obnovením licence budete i nadále získávat prémiovou podporu a důležité aktualizace pro moduly a motivy. Kromě toho budete moci pokračovat ve vytváření nových projektů. A stále budete moci používat ABP Suite, který urychlí váš vývoj.
Upgrade: Upgradováním licence povýšíte na vyšší licenční plán, který vám umožní získat další výhody . Rozdíly mezi licenčními plány naleznete v tabulce porovnání licencí.Na druhou stranu, když upgradujete, datum vypršení platnosti vaší licence se nezmění! >Chcete-li prodloužit datum ukončení licence, musíte prodloužit licenci.", "LicenseRenewalCost": "Kolik stojí obnovení licence po 1 roce?", - "LicenseRenewalCostExplanation": "Míra obnovení (prodloužení) všech trvalých licencí ABP Commercial je {0} z ceníkové ceny licencí. Cena obnovení standardní týmové licence je ${1}, standardní obchodní licence je ${2} a standardní podnikové licence je ${3}. Pokud již jste zákazníkem, přihlaste se ke svému účtu a zkontrolujte dostupné ceny za obnovení.", + "LicenseRenewalCostExplanation": "Cena za obnovení (prodloužení) standardní týmové licence je ${0}, standardní obchodní licence je ${1} a standardní podnikové licence je ${2}. Pokud již jste zákazníkem, přihlaste se ke svému účtu a zkontrolujte dostupné ceny za obnovení.", "HowDoIRenewMyLicense": "Jak obnovím licenci?", "HowDoIRenewMyLicenseExplanation": "Licenci můžete obnovit tak, že přejdete na stránku správy organizace. Abyste mohli využít našich zvýhodněných sazeb za předčasné obnovení, ujistěte se, že jste licenci obnovili před vypršením platnosti. Nebojte se však, že byste nevěděli, kdy vaše příležitost k předčasnému obnovení skončí. Před vypršením předplatného obdržíte 3 e-maily s upomínkou. Odešleme je 30 dní, 7 dní a 1 den před vypršením platnosti.", "IsSourceCodeIncluded": "Obsahuje moje licence zdrojový kód komerčních modulů a témat?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Mohu v budoucnu změnit registrované vývojáře své organizace?", "ChangingDevelopersExplanation": "Kromě přidání nových vývojářů do vaší licence můžete také změnit stávající vývojáře (můžete odebrat vývojáře a přidat nového na stejné místo) bez jakýchkoli dalších nákladů.", "WhatHappensWhenLicenseEnds": "Co se stane, když moje licenční období skončí?", - "WhatHappensWhenLicenseEndsExplanation1": "Typ komerční licence ABP je trvalá licence. Po vypršení platnosti licence můžete pokračovat ve vývoji projektu. A nejste povinni prodlužovat licenci. Vaše licence je dodávána s ročním plánem aktualizací a podpory ihned po vybalení. Chcete-li i nadále dostávat nové funkce, vylepšení výkonu, opravy chyb, podporu a nadále používat ABP Suite, obnovte svůj plán každý rok. Po vypršení platnosti vaší licence nebudete moci získat další z následujících výhod;", + "WhatHappensWhenLicenseEndsExplanation1": "Typ komerční licence ABP je trvalá licence. Po vypršení platnosti licence můžete pokračovat ve vývoji projektu. A nejste povinni prodlužovat licenci. Vaše licence je dodávána s roční aktualizací a plánem podpory ihned po vybalení. Chcete-li i nadále získávat nové funkce, vylepšení výkonu, opravy chyb, podporu a nadále používat ABP Suite, musíte si obnovit licenci. Po vypršení platnosti vaší licence nezískáte následující výhody;", "WhatHappensWhenLicenseEndsExplanation2": "Pomocí ABP Commercial nemůžete vytvářet nová řešení, ale můžete pokračovat ve vývoji svých stávajících aplikací navždy.", - "WhatHappensWhenLicenseEndsExplanation3": "Budete moci získat aktualizace pro moduly a témata ve vaší HLAVNÍ verzi. Například; pokud používáte v3.2.0 modulu, stále můžete získat aktualizace pro v3.x.x (v3.3.0, v3.5.2... atd.) tohoto modulu. Nemůžete však získat aktualizace pro další hlavní verzi (jako v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "Budete moci získat aktualizace pro moduly a motivy ve vaší MAJOR verzi (kromě RC nebo Preview verzí). Například; pokud používáte v3.2.0 modulu, stále můžete získat aktualizace pro v3.x.x (v3.3.0, v3.5.2... atd.) tohoto modulu. Nemůžete však získat aktualizace pro další hlavní verzi (jako v4.x, v5.x). Když například vypršela platnost vaší licence, nejnovější verze byla v4.4.3 a nejnovější verze náhledu byla v5.0.0-rc.2, máte přístup k verzi 4.X.X, ale nemáte přístup k verzi 5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Po skončení licence nemůžete instalovat nové moduly a motivy přidané do platformy ABP Commercial.", "WhatHappensWhenLicenseEndsExplanation5": "Nemůžete používat ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "Již nemůžete získat prémiovou podporu.", - "WhatHappensWhenLicenseEndsExplanation7": "Pokud chcete i nadále využívat tyto výhody, můžete si předplatné obnovit. Pokud předplatné obnovíte do 1 měsíce po vypršení platnosti licence, uplatní se sleva 20 %.", + "WhatHappensWhenLicenseEndsExplanation7": "Pokud chcete i nadále využívat tyto výhody, můžete svou licenci prodloužit (obnovit). Pokud prodloužíte licenci do 1 měsíce po vypršení platnosti licence, budou uplatněny následující slevy: Týmová licence {0}% sleva, Obchodní licence {1}% sleva, Enterprise License {2}% sleva .", "WhenShouldIRenewMyLicense": "Kdy bych měl obnovit licenci?", - "WhenShouldIRenewMyLicenseExplanation1": "Pokud svou licenci obnovíte do 1 měsíce po vypršení platnosti licence, uplatní se sleva 20 %.", - "WhenShouldIRenewMyLicenseExplanation2": "Pokud obnovíte licenci po 1 měsíci od data vypršení platnosti licence, cena za obnovení bude stejná jako cena za zakoupení licence a za obnovení nebude poskytnuta žádná sleva.", + "WhenShouldIRenewMyLicenseExplanation": "Pokud si licenci obnovíte do 1 měsíce po vypršení platnosti licence, budou uplatněny následující slevy: Týmová licence {0}% sleva, Obchodní licence {1}% sleva, Enterprise License {2}% sleva . Pokud si licenci obnovíte 1 měsíc po datu vypršení platnosti vaší licence, cena za obnovení bude stejná jako cena nákupu licence a na obnovení nebude poskytnuta žádná sleva.", "TrialPlan": "Máte zkušební plán?", "TrialPlanExplanation": "ABP Commercial zatím nemá zkušební plán. Pro týmové licence poskytujeme 30denní záruku vrácení peněz. Během prvních 30 dnů můžete pouze požádat o vrácení peněz. U licencí Business a Enterprise poskytujeme vrácení 60 % do 30 dnů. Je to proto, že licence Business a Enterprise obsahují úplný zdrojový kód všech modulů a témat.", "DoYouAcceptBankWireTransfer": "Přijímáte bankovní převod?", - "DoYouAcceptBankWireTransferExplanation": "Ano, přijímáme bankovní převod.
Po zaslání licenčního poplatku bankovním převodem nám zašlete e-mail na adresu info@abp.io potvrzení a požadovaný typ licence. Informace o našem mezinárodním bankovním účtu:", + "DoYouAcceptBankWireTransferExplanation": "Ano, přijímáme bankovní převod.
Po zaslání licenčního poplatku bankovním převodem nám zašlete e-mail na adresu accounting@abp.io potvrzení a požadovaný typ licence. Informace o našem mezinárodním bankovním účtu:", "HowToUpgrade": "Jak upgradovat stávající aplikace, když je k dispozici nová verze?", "HowToUpgradeExplanation1": "Když vytvoříte novou aplikaci pomocí ABP Commercial, všechny moduly a téma se použijí jako balíčky NuGet a NPM. Takže můžete snadno upgradovat balíčky, když je k dispozici nová verze.", "HowToUpgradeExplanation2": "Kromě standardních upgradů NuGet/NPM poskytuje ABP CLI aktualizační příkaz, který automaticky vyhledá a upgraduje všechny balíčky související s ABP ve vašem řešení.", @@ -390,6 +389,9 @@ "TrialLicenseExpireMessage": "Používáte zkušební licenci a platnost vaší zkušební licence vyprší dne {0}.", "TryForFree": "Zkus zadarmo", "TrialLicenseExpiredInfo": "Vaše zkušební licenční období vypršelo!", - "CommercialNewsletterConfirmationMessage": "I agree to the Terms & Conditions and Privacy Policy." + "CommercialNewsletterConfirmationMessage": "I agree to the Terms & Conditions and Privacy Policy.", + "ContinueWithNewOrganization": "Pokračujte s novou organizací", + "RenewLicenseEarly": "Pokud si předčasně obnovím licenci, dostanu celý rok?", + "RenewLicenseEarylExplanation": "Když obnovíte licenci před datem vypršení platnosti licence, bude k datu vypršení platnosti licence přidán 1 rok. Pokud například platnost vaší licence vyprší dne {0}-06-06 a obnovíte ji dne {0}-01-01, bude vaše nové datum vypršení platnosti licence {1}-06-06." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de-DE.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de-DE.json index a85f96c4d4..e95bc6a159 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de-DE.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de-DE.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Was ist der Unterschied zwischen Lizenzverlängerung und Upgrade?", "LicenseExtendUpgradeDiffExplanation": "Verlängern: Durch die Verlängerung/Erneuerung Ihrer Lizenz erhalten Sie weiterhin Premium-Support und wichtige Updates für die Module und Themen. Außerdem können Sie weiterhin neue Projekte erstellen. Und Sie können weiterhin die ABP Suite verwenden, was Ihre Entwicklung beschleunigt.
Upgrade: Durch das Upgrade Ihrer Lizenz steigen Sie zu einem höheren Lizenzplan auf, mit dem Sie zusätzliche Vorteile erhalten . Sehen Sie sich die Lizenzvergleichstabelle an, um die Unterschiede zwischen den Lizenzplänen zu überprüfen.Andererseits ändert sich das Ablaufdatum Ihrer Lizenz bei einem Upgrade nicht!Um das Enddatum Ihrer Lizenz zu verlängern, müssen Sie Ihre Lizenz verlängern.", "LicenseRenewalCost": "Wie hoch sind die Kosten für die Lizenzverlängerung nach 1 Jahr?", - "LicenseRenewalCostExplanation": "Die Verlängerungsrate (Verlängerung) aller unbefristeten Lizenzen von ABP Commercial beträgt {0} des Lizenzlistenpreises. Der Verlängerungspreis der Standard-Team-Lizenz beträgt ${1}, die Standard-Business-Lizenz ${2} und die Standard-Unternehmens-Lizenz ${3}. Wenn Sie bereits Kunde sind, melden Sie sich in Ihrem Konto an, um die verfügbaren Verlängerungspreise zu überprüfen.", + "LicenseRenewalCostExplanation": "Der Verlängerungspreis (Erweiterung) der Standard-Teamlizenz beträgt {0}$, die Standard-Business-Lizenz beträgt ${1} und die Standard-Enterprise-Lizenz beträgt ${2}. Wenn Sie bereits Kunde sind, melden Sie sich in Ihrem Konto an, um die verfügbaren Verlängerungspreise zu überprüfen.", "HowDoIRenewMyLicense": "Wie erneuere ich meine Lizenz?", "HowDoIRenewMyLicenseExplanation": "Sie können Ihre Lizenz erneuern, indem Sie zur Organisationsverwaltungsseite navigieren. Um von unseren ermäßigten Tarifen für die vorzeitige Verlängerung zu profitieren, vergewissern Sie sich, dass Sie vor Ablauf Ihrer Lizenz verlängern. Machen Sie sich jedoch keine Sorgen, dass Sie nicht wissen, wann Ihre Gelegenheit zur vorzeitigen Verlängerung endet. Sie erhalten 3 Erinnerungs-E-Mails, bevor Ihr Abonnement abläuft. Wir versenden sie 30 Tage, 7 Tage und 1 Tag vor Ablauf.", "IsSourceCodeIncluded": "Enthält meine Lizenz den Quellcode der kommerziellen Module und Themes?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Kann ich die registrierten Entwickler meiner Organisation in Zukunft ändern?", "ChangingDevelopersExplanation": "Sie können nicht nur neue Entwickler zu Ihrer Lizenz hinzufügen, sondern auch die vorhandenen Entwickler ändern (Sie können einen Entwickler entfernen und einen neuen zum selben Arbeitsplatz hinzufügen) ohne zusätzliche Kosten.", "WhatHappensWhenLicenseEnds": "Was passiert, wenn mein Lizenzzeitraum endet?", - "WhatHappensWhenLicenseEndsExplanation1": "Der kommerzielle ABP-Lizenztyp ist unbefristete Lizenz. Nach Ablauf Ihrer Lizenz können Sie Ihr Projekt weiter entwickeln. Und Sie sind nicht verpflichtet, Ihre Lizenz zu erneuern. Im Lieferumfang Ihrer Lizenz ist ein einjähriger Update- und Supportplan enthalten. Um weiterhin neue Funktionen, Leistungsverbesserungen, Fehlerkorrekturen und Support zu erhalten und die ABP Suite weiterhin zu nutzen, sollten Sie Ihren Plan jedes Jahr erneuern. Wenn Ihre Lizenz abläuft, können Sie die folgenden Vorteile nicht mehr nutzen;", + "WhatHappensWhenLicenseEndsExplanation1": "Der kommerzielle ABP-Lizenztyp ist unbefristete Lizenz. Nach Ablauf Ihrer Lizenz können Sie Ihr Projekt weiter entwickeln. Und Sie sind nicht verpflichtet, Ihre Lizenz zu erneuern. Ihre Lizenz wird standardmäßig mit einem einjährigen Update- und Supportplan geliefert. Um weiterhin neue Funktionen, Leistungsverbesserungen, Fehlerkorrekturen und Support zu erhalten und die ABP Suite weiterhin nutzen zu können, müssen Sie Ihre Lizenz erneuern. Wenn Ihre Lizenz abläuft, erhalten Sie die folgenden Vorteile nicht;", "WhatHappensWhenLicenseEndsExplanation2": "Sie können mit dem ABP Commercial keine neuen Lösungen erstellen, aber Sie können Ihre bestehenden Anwendungen für immer weiterentwickeln.", - "WhatHappensWhenLicenseEndsExplanation3": "Sie können Updates für die Module und Themen Ihrer MAJOR-Version erhalten. Zum Beispiel; Wenn Sie v3.2.0 eines Moduls verwenden, können Sie weiterhin Updates für v3.x.x (v3.3.0, v3.5.2... etc.) dieses Moduls erhalten. Sie können jedoch keine Updates für die nächste Hauptversion (wie v4.x, v5.x) erhalten.", + "WhatHappensWhenLicenseEndsExplanation3": "Sie können Updates für die Module und Themen Ihrer MAJOR-Version (außer RC- oder Preview-Versionen) erhalten. Beispielsweise; Wenn Sie v3.2.0 eines Moduls verwenden, können Sie immer noch Updates für v3.x.x (v3.3.0, v3.5.2... etc.) dieses Moduls erhalten. Sie können jedoch keine Updates für die nächste Hauptversion (wie v4.x, v5.x) erhalten. Wenn beispielsweise Ihre Lizenz abgelaufen ist, die neueste Version v4.4.3 und die neueste Vorschauversion v5.0.0-rc.2 war, können Sie auf v4.X.X zugreifen, aber nicht auf v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Sie können nach Ablauf Ihrer Lizenz keine neuen Module und Themen installieren, die der ABP Commercial-Plattform hinzugefügt wurden.", "WhatHappensWhenLicenseEndsExplanation5": "Sie können die ABP-Suite nicht verwenden.", "WhatHappensWhenLicenseEndsExplanation6": "Sie können den Premium-Support nicht mehr erhalten.", - "WhatHappensWhenLicenseEndsExplanation7": "Sie können Ihr Abonnement verlängern, wenn Sie diese Vorteile weiterhin nutzen möchten. Wenn Sie Ihr Abonnement innerhalb von 1 Monat nach Ablauf Ihrer Lizenz verlängern, wird ein Rabatt von 20 % gewährt.", + "WhatHappensWhenLicenseEndsExplanation7": "Sie können Ihre Lizenz verlängern (erneuern), wenn Sie diese Vorteile weiterhin nutzen möchten. Wenn Sie Ihre Lizenz innerhalb von 1 Monat nach Ablauf Ihrer Lizenz verlängern, werden die folgenden Rabatte gewährt: Team-Lizenz {0}% Rabatt, Business-Lizenz {1}% Rabatt, Enterprise-Lizenz {2}% Rabatt .", "WhenShouldIRenewMyLicense": "Wann sollte ich meine Lizenz erneuern?", - "WhenShouldIRenewMyLicenseExplanation1": "Wenn Sie Ihre Lizenz innerhalb von 1 Monat nach Ablauf Ihrer Lizenz erneuern, wird ein Rabatt von 20 % gewährt.", - "WhenShouldIRenewMyLicenseExplanation2": "Wenn Sie Ihre Lizenz 1 Monat nach dem Ablaufdatum Ihrer Lizenz verlängern, entspricht der Verlängerungspreis dem Lizenzkaufpreis und es wird kein Rabatt für Ihre Verlängerung gewährt.", + "WhenShouldIRenewMyLicenseExplanation": "Wenn Sie Ihre Lizenz innerhalb von 1 Monat nach Ablauf Ihrer Lizenz erneuern, werden die folgenden Rabatte gewährt: Team-Lizenz {0}% Rabatt, Business-Lizenz {1}% Rabatt, Enterprise-Lizenz {2}% Rabatt . Wenn Sie Ihre Lizenz 1 Monat nach dem Ablaufdatum Ihrer Lizenz verlängern, entspricht der Verlängerungspreis dem Lizenzkaufpreis und es wird kein Rabatt auf Ihre Verlängerung gewährt.", "TrialPlan": "Hast du einen Probeplan?", "TrialPlanExplanation": "Derzeit hat ABP Commercial keinen Testplan. Für die Team-Lizenzen bieten wir eine 30-tägige Geld-zurück-Garantie. Sie können nur innerhalb der ersten 30 Tage eine Rückerstattung beantragen. Für die Business- und Enterprise-Lizenzen gewähren wir innerhalb von 30 Tagen eine Rückerstattung von 60 %. Dies liegt daran, dass Business- und Enterprise-Lizenzen den vollständigen Quellcode aller Module und Themes enthalten.", "DoYouAcceptBankWireTransfer": "Akzeptieren Sie Banküberweisungen?", - "DoYouAcceptBankWireTransferExplanation": "Ja, wir akzeptieren Banküberweisungen.
Nachdem Sie die Lizenzgebühr per Banküberweisung gesendet haben, senden Sie uns Ihre Quittung und den gewünschten Lizenztyp per E-Mail an info@abp.io. Unsere internationale Bankverbindung:", + "DoYouAcceptBankWireTransferExplanation": "Ja, wir akzeptieren Banküberweisungen.
Nachdem Sie die Lizenzgebühr per Banküberweisung gesendet haben, senden Sie uns Ihre Quittung und den gewünschten Lizenztyp per E-Mail an accounting@abp.io. Unsere internationale Bankverbindung:", "HowToUpgrade": "Wie aktualisiere ich bestehende Anwendungen, wenn eine neue Version verfügbar ist?", "HowToUpgradeExplanation1": "Wenn Sie mit ABP Commercial eine neue Anwendung erstellen, werden alle Module und das Design als NuGet- und NPM-Pakete verwendet. So können Sie die Pakete problemlos aktualisieren, wenn eine neue Version verfügbar ist.", "HowToUpgradeExplanation2": "Zusätzlich zu den standardmäßigen NuGet/NPM-Upgrades bietet die ABP CLI einen Update-Befehl, der automatisch alle ABP-bezogenen Pakete in Ihrer Lösung findet und aktualisiert.", @@ -390,6 +389,9 @@ "TrialLicenseExpireMessage": "Sie verwenden die Testlizenz und Ihre Testlizenz läuft am {0} ab.", "TryForFree": "Kostenlos testen", "TrialLicenseExpiredInfo": "Ihr Testlizenzzeitraum ist abgelaufen!", - "CommercialNewsletterConfirmationMessage": "Ich stimme den Allgemeinen Geschäftsbedingungen und der Datenschutzerklärung . zu ." + "CommercialNewsletterConfirmationMessage": "Ich stimme den Allgemeinen Geschäftsbedingungen und der Datenschutzerklärung . zu .", + "ContinueWithNewOrganization": "Weiter mit einer neuen Organisation", + "RenewLicenseEarly": "Erhalte ich das ganze Jahr, wenn ich meine Lizenz vorzeitig erneuere?", + "RenewLicenseEarylExplanation": "Wenn Sie Ihre Lizenz vor dem Ablaufdatum Ihrer Lizenz erneuern, wird Ihr Lizenzablaufdatum um 1 Jahr verlängert. Wenn Ihre Lizenz beispielsweise am {0}-06-06 abläuft und Sie sie am {0}-01-01 erneuern, ist das neue Ablaufdatum der Lizenz der {1}-06-06." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en-GB.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en-GB.json index 87b934f8c5..8466e89eeb 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en-GB.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en-GB.json @@ -102,6 +102,12 @@ "InvalidFormInputs": "Please, type the valid information specified on the form.", "DDDBookEmailBody": "Thank you.
To download your book, click here.", "FreeDDDEBook": "Free DDD E-Book", - "CommercialNewsletterConfirmationMessage": "I agree to the Terms & Conditions and Privacy Policy." + "CommercialNewsletterConfirmationMessage": "I agree to the Terms & Conditions and Privacy Policy.", + "RemoveCurrentUserFromOrganizationWarningMessage": "You are removing yourself from your own organization. You will no longer be able to manage this organization, do you confirm?", + "RenewExistingOrganizationOrCreateNewOneMessage": "You can renew the license of your organization(s) by clicking the below \"Extend Now\" button(s) and thus you can extend the license expiry date by 1 year. If you continue to checkout, you will have a new organization. Do you want to continue with a new organization?", + "ExtendNow": "Extend Now", + "ContinueWithNewOrganization": "Continue with a new organization", + "RenewLicenseEarly": "If I renew my license early, will I get the full year?", + "RenewLicenseEarylExplanation": "When you renew your license before your license expiry date, 1 year will be added to your license expiry date. For example, if your license expires on {0}-06-06 and you renew it on {0}-01-01, your new license expiry date will be {1}-06-06." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json index f4a8153ec1..a5251b601e 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json @@ -155,83 +155,82 @@ "WeAreHereToHelp": "We are Here to Help", "BrowseOrAskQuestion": "You can browse our help topics or search in frequently asked questions, or you can ask us a question by using the contact form.", "SearchQuestionPlaceholder": "Search in frequently asked questions", - "WhatIsTheABPCommercial": "What is the ABP Commercial?", - "WhatAreDifferencesThanAbpFramework": "What are the differences between the open source ABP Framework and the ABP Commercial?", - "ABPCommercialExplanation": "ABP Commercial is a set of premium modules, tools, themes and services built on top of the open source ABP framework. ABP Commercial is being developed and supported by the same team behind the ABP framework.", - "WhatAreDifferencesThanABPFrameworkExplanation": "

ABP framework is a modular, themeable, micro-service compatible application development framework for ASP.NET Core. It provides a complete architecture and a strong infrastructure to make you focusing on your own business code rather than repeating yourself for every new project. It is based on software development best practices and popular tools you already know.

ABP framework is completely free, open source and community-driven. It also provides a free theme and some pre-built modules (e.g. identity management and tenant management).

", - "VisitTheFrameworkVSCommercialDocument": "Visit the following link, for more information {1} ", - "ABPCommercialFollowingBenefits": "ABP Commercial adds the following benefits on top of the ABP framework;", + "WhatIsTheABPCommercial": "What is ABP Commercial?", + "WhatAreDifferencesThanAbpFramework": "What are the differences between the open source ABP Framework and ABP Commercial?", + "ABPCommercialExplanation": "ABP Commercial is a set of premium modules, tools, themes and services that are built on top of the open source ABP framework. ABP Commercial is being developed and supported by the same team behind the ABP framework.", + "WhatAreDifferencesThanABPFrameworkExplanation": "

ABP framework is a modular, themeable, micro-service compatible application development framework for ASP.NET Core. It provides a complete architecture and a strong infrastructure to let you focus on your own business code rather than repeating yourself for every new project. It is based on the best practices of software development and popular tools you already know.

ABP framework is completely free, open source and community-driven. It also provides a free theme and some pre-built modules (e.g. identity management and tenant management).

", + "VisitTheFrameworkVSCommercialDocument": "Visit the following link for more information {1} ", + "ABPCommercialFollowingBenefits": "ABP Commercial adds the following benefits on top of the ABP framework:", "Professional": "Professional", - "UIThemes": "UI themes", - "EnterpriseModules": "Enterprise ready, feature rich, pre-built application modules (e.g. Identity Server management, SaaS management, language management)", + "UIThemes": "UI Themes", + "EnterpriseModules": "Enterprise ready, feature rich, pre-built Application Modules (e.g. Identity Server management, SaaS management, language management)", "ToolingToSupport": "Tooling to support your development productivity (e.g. ABP Suite)", - "PremiumSupportLink": "Premium support", + "PremiumSupportLink": "Premium Support", "WhatDoIDownloadABPCommercial": "What do I download when I purchase the ABP Commercial?", "CreateUnlimitedSolutions": "Once you purchase an ABP Commercial license, you will be able to create unlimited solutions like described in the Getting Started document.", - "ABPCommercialSolutionExplanation": "When you create a new application, you get a Visual Studio solution (a startup template) based on your preferences. The downloaded solution has commercial modules and themes already installed and configured for you. You can remove a pre-installed module or add another module if you like. All modules and themes are used a NuGet/NPM packages by default.", - "StartDevelopWithTutorials": "The downloaded solution is well architected and documented. You can start to develop your own business code based on it following the tutorials", - "TryTheCommercialDemo": "You can try the demo to see a sample application created using the ABP Commercial startup template.", + "ABPCommercialSolutionExplanation": "When you create a new application, you get a Visual Studio solution (a startup template) based on your preferences. The downloaded solution has commercial modules and themes already installed and configured for you. You can remove a pre-installed module or add another module if you like. All modules and themes use NuGet/NPM packages by default.", + "StartDevelopWithTutorials": "The downloaded solution is well architected and documented. You can start developing your own business code based on it following the tutorials.", + "TryTheCommercialDemo": "You can try the Live Demo to see a sample application created using the ABP Commercial startup template.", "HowManyProducts": "How many different products/solutions can I build using the ABP Commercial?", - "HowManyProductsExplanation": "There is no limit to create an ABP project. You can create as many project as you want, develop and upload them to different servers.", + "HowManyProductsExplanation": "You can create as many projects as you want during your active license period, there is no limit! After your license expires, you cannot create new projects, but you can continue to develop the projects you have downloaded and deploy them to an unlimited count of servers.", "HowManyDevelopers": "How many developers can work on the ABP Commercial?", - "HowManyDevelopersExplanation": "ABP Commercial licenses are per developer. Different license types have different developer limits. However, you can add more developers to any license type whenever you need. See the prices page for license types, developer limits and additional developer costs.", + "HowManyDevelopersExplanation": "ABP Commercial licenses are per developer. Different license types have different developer limits. However, you can add more developers to any license type whenever you need. Check out the Plans & Pricing page for license types, developer limits and additional developer costs.", "ChangingLicenseType": "Can I upgrade my license type later?", - "ChangingLicenseTypeExplanation": "You can upgrade to a higher license by paying the difference within your active license period. When you upgrade to a higher license plan, you get the benefits of the new plan, but the license upgrade does not change the license expiry date. Besides, you can also add new developer seats to your existing license, see \"How many developers can work on the ABP Commercial?\"", + "ChangingLicenseTypeExplanation": "You can upgrade to a higher license by paying the difference within your active license period. When you upgrade to a higher license plan, you get the benefits of the new plan, but the license upgrade does not change the license expiry date. Besides, you can also add new developer seats to your existing license, check out the \"How many developers can work on the ABP Commercial?\" FAQ.", "LicenseExtendUpgradeDiff": "What is the difference between license extend and upgrade?", - "LicenseExtendUpgradeDiffExplanation": "Extending: By extending/renewing your license, you will continue to get premium support and get major updates for the modules and themes. Besides, you will be able to continue creating new projects. And you will still be able to use ABP Suite which speeds up your development.
Upgrading: By upgrading your license, you will promote to a higher license plan which will allow you to get additional benefits. See the license comparison table to check the differences between the license plans.On the other hand, when you upgrade, your license expiry date will not change!To extend your license end date, you need to extend your license.", + "LicenseExtendUpgradeDiffExplanation": "Extending: By extending/renewing your license, you will continue to get premium support and get major updates for the modules and themes. Besides, you will be able to continue creating new projects. And you will still be able to use ABP Suite which speeds up your development. When you extend your license, 1 year is added to your license expiry date.
Upgrading: By upgrading your license, you will promote to a higher license plan which will allow you to get additional benefits. Check out the license comparison table to see the differences between the license plans. On the other hand, when you upgrade, your license expiry date will not change! To extend your license end date, you need to extend your license.", "LicenseRenewalCost": "What is the license renewal cost after 1 year?", - "LicenseRenewalCostExplanation": "The renewal (extend) rate of all ABP Commercial perpetual licenses is {0} of the license list price. The renewal price of the standard Team License is ${1}, standard Business License is ${2} and standard Enterprise License is ${3}. If you are already a customer, log into your account to review the available renewal pricing.", + "LicenseRenewalCostExplanation": "The renewal (extend) price of the standard Team License is ${0}, standard Business License is ${1} and standard Enterprise License is ${2}. If you are already a customer, log into your account to review the current renewal pricing.", "HowDoIRenewMyLicense": "How do I renew my license?", - "HowDoIRenewMyLicenseExplanation": "You can renew your license by navigating to the organization management page. In order to take advantage of our discounted Early Renewal rates, make sure you renew before your license expires. Don't worry about not knowing when your Early Renewal opportunity closes, however. You'll receive 3 reminder e-mails before your subscription expires. We'll send them at 30 days, 7 days and 1 day before expiration.", + "HowDoIRenewMyLicenseExplanation": "You can renew your license by navigating to the organization management page. In order to take advantage of our discounted Early Renewal rates, make sure you renew before your license expires. Don't worry about not knowing when your Early Renewal opportunity closes, you'll however receive 3 reminder e-mails before your subscription expires. We'll send them 30 days, 7 days and 1 day before expiration.", "IsSourceCodeIncluded": "Does my license include the source code of the commercial modules and themes?", "IsSourceCodeIncludedExplanation1": "Depends on the license type you've purchased:", - "IsSourceCodeIncludedExplanation2": "Team: Your solution uses the modules and the themes as NuGet and NPM packages. It doesn't include their source code. In this way, you can easily upgrade these modules and themes whenever a new version is available. However, you can not get the source code of the modules and the themes.", + "IsSourceCodeIncludedExplanation2": "Team: Your solution uses the modules and themes as NuGet and NPM packages. It doesn't include their source code. In this way, you can easily upgrade these modules and themes whenever a new version is available. However, you can not get the source code of these modules and themes.", "IsSourceCodeIncludedExplanation3": "Business/Enterprise: In addition to the Team license, you are able to download the source code of any module or theme you need. You can even remove the NuGet/NPM package references for a particular module and add its source code directly to your solution to fully change it.", - "IsSourceCodeIncludedExplanation4": "

Including the source code of a module to your solution gives you the maximum freedom to customize that module. However, then it will not be possible to automatically upgrade the module when a new version is released.

None of the licenses include the ABP Suite source code, which is an external tool that generates code for you and assist to your development.

See the pricing page for other differences between the license types.

", + "IsSourceCodeIncludedExplanation4": "

Including the source code of a module to your solution gives you the maximum freedom to customize that module. However, it will then not be possible to automatically upgrade the module when a new version is released.

None of the licenses include the ABP Suite source code, which is an external tool that generates code for you and assists your development.

Check out the Plans & Pricing page for other differences between the license types.

", "ChangingDevelopers": "Can I change the registered developers of my organization in the future?", - "ChangingDevelopersExplanation": "In addition to add new developers to your license, you can also change the existing developers (you can remove a developer and add a new one to the same seat) without any additional cost.", + "ChangingDevelopersExplanation": "In addition to adding new developers to your license, you can also change the existing developers (you can remove a developer and add a new one to the same seat) without any additional cost.", "WhatHappensWhenLicenseEnds": "What happens when my license period ends?", - "WhatHappensWhenLicenseEndsExplanation1": "ABP Commercial license type is perpetual license. After your license expires, you can continue developing your project. And you are not obliged to renew your license. Your license comes with a one-year Updates and Support plan out of the box. To continue to receive new features, performance enhancements, bug fixes, support and continue to use ABP Suite, make sure to renew your plan each year. When your license expires, you will not be able to get more of the following benefits;", - "WhatHappensWhenLicenseEndsExplanation2": "You can not create new solutions using the ABP Commercial, but you can continue to develop your existing applications forever.", - "WhatHappensWhenLicenseEndsExplanation3": "You will be able to get updates for the modules and themes within your MAJOR version. For example; if you are using v3.2.0 of a module, you can still get updates for v3.x.x (v3.3.0, v3.5.2... etc.) of that module. But you cannot get updates for the next major version (like v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation1": "The ABP Commercial license is a perpetual license. After your license expires, you can continue developing your project. And you are not obliged to renew your license. Your license comes with a one-year update and support plan out of the box. In order to continue to get new features, performance enhancements, bug fixes, support and continue using ABP Suite, you need to renew your license. When your license expires, you will not get the following benefits:", + "WhatHappensWhenLicenseEndsExplanation2": "You can not create new solutions using the ABP Commercial, but you can continue developing your existing applications forever.", + "WhatHappensWhenLicenseEndsExplanation3": "You will be able to get updates for the modules and themes within your MAJOR version (except RC or Preview versions). For example: if you are using v3.2.0 of a module, you can still get updates for v3.x.x (v3.3.0, v3.5.2... etc.) of that module. But you cannot get updates for the next major version (like v4.x, v5.x). For example, when your license expired, the latest release was v4.4.3 and the latest preview version was v5.0.0-rc.2, you would be able to access the v4.X.X but you wouldn't be access the v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "You can not install new modules and themes added to the ABP Commercial platform after your license ends.", "WhatHappensWhenLicenseEndsExplanation5": "You can not use the ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "You can not get the premium support anymore.", - "WhatHappensWhenLicenseEndsExplanation7": "You can renew your subscription if you want to continue to get these benefits. If you renew your subscription within 1 month after your license expires, a 20% discount is applied.", + "WhatHappensWhenLicenseEndsExplanation7": "You can extend (renew) your license if you want to continue getting these benefits. If you extend your license within 1 month after your license expires, the following discounts will be applied: Team License {0}% discount, Business License {1}% discount, Enterprise License {2}% discount.", "WhenShouldIRenewMyLicense": "When should I renew my license?", - "WhenShouldIRenewMyLicenseExplanation1": "If you renew your license within 1 month after your license expires, a %20 discount is be applied.", - "WhenShouldIRenewMyLicenseExplanation2": "If you renew your license after 1 month from your license expire date, the renew price will be same as license purchase price and there will be no discount for your renewal.", + "WhenShouldIRenewMyLicenseExplanation": "If you renew your license within 1 month after your license expires, the following discounts will be applied: Team License {0}% discount, Business License {1}% discount, Enterprise License {2}% discount. However, if you renew your license after 1 month since the expiry date of your license, the renewal price will be the same as the license purchase price and there will be no discount on your renewal.", "TrialPlan": "Do you have a trial plan?", "TrialPlanExplanation": "For now, ABP Commercial doesn't have a trial plan. For the Team licenses we provide 30 days money back guarantee. You can just request a refund in the first 30 days. For the Business and Enterprise licenses, we provide 60% refund in 30 days. This is because Business and Enterprise licenses include the full source code of all the modules and the themes.", - "DoYouAcceptBankWireTransfer": "Do you accept bank wire transfer?", - "DoYouAcceptBankWireTransferExplanation": "Yes, we accept bank wire transfer.
After sending the license fee via bank transfer, email us at info@abp.io your receipt and the type of license requested. Our international bank account information:", + "DoYouAcceptBankWireTransfer": "Do you accept bank wire transfers?", + "DoYouAcceptBankWireTransferExplanation": "Yes, we accept bank wire transfers.
After sending the license fee via bank transfer, email us your receipt and the type of license requested at accounting@abp.io, here's our international bank account information:", "HowToUpgrade": "How to upgrade existing applications when a new version is available?", - "HowToUpgradeExplanation1": "When you create a new application using ABP Commercial, all the modules and the theme are used as NuGet and NPM packages. So, you can easily upgrade the packages when a new version is available.", + "HowToUpgradeExplanation1": "When you create a new application using ABP Commercial, all the modules and theme are used as NuGet and NPM packages. So, you can easily upgrade the packages when a new version is available.", "HowToUpgradeExplanation2": "In addition to the standard NuGet/NPM upgrades, ABP CLI provides an update command that automatically finds and upgrades all ABP related packages in your solution.", "DatabaseSupport": "Which database systems are supported?", - "DatabaseSupportExplanation": "ABP Framework itself is database agnostic and can work with any database provider by its nature. See the data access document for a list of currently implemented providers.", + "DatabaseSupportExplanation": "ABP Framework itself is database agnostic and can work with any database provider by its nature. Check out the Data Access document for a list of currently implemented providers.", "UISupport": "Which UI frameworks are supported?", "Supported": "Supported", - "UISupportExplanation": "ABP Framework itself is UI framework agnostic and can work with any UI framework. However, startup templates, module UIs and themes were not implemented for all UI frameworks. See the getting started document for the up-to-date list of UI options.", + "UISupportExplanation": "ABP Framework itself is UI framework agnostic and can work with any UI framework. However, startup templates, module UIs and themes were not implemented for all UI frameworks. Check out the Getting Started document for the up-to-date list of UI options.", "MicroserviceSupport": "Does it support the micro-service architecture?", - "MicroserviceSupportExplanation1": "One of the major goals of the ABP framework is to provide a convenient infrastructure to create micro-service solutions. See the micro-service architecture document to understand how it helps to create micro-service systems.", - "MicroserviceSupportExplanation2": "All the ABP Commercial modules are designed to support micro-service deployment scenarios (with its own API and database) by following the module development best practices document.", - "MicroserviceSupportExplanation3": "We provide a sample micro-service demo solutionthat demonstrates a micro-service architecture implementation to help you to create your own solution.", - "MicroserviceSupportExplanation4": "So, the short answer is \"yes, it supports micro-service architecture\".", + "MicroserviceSupportExplanation1": "One of the major goals of the ABP framework is to provide a convenient infrastructure to create micro-service solutions. Check out the Micro-service Architecture document to understand how it helps with creating micro-service systems.", + "MicroserviceSupportExplanation2": "All the ABP Commercial modules are designed to support micro-service deployment scenarios (with its own API and database) by following the Module Development Best Practices document.", + "MicroserviceSupportExplanation3": "We provide a sample Micro-service Demo Solution that demonstrates a micro-service architecture implementation to help you create your own solution.", + "MicroserviceSupportExplanation4": "So, the short answer is: \"Yes, it supports micro-service architecture\".", "MicroserviceSupportExplanation5": "However, a micro-service system is a solution and every solution will have different requirements, network topology, communication scenarios, authentication possibilities, database separation/sharing decisions, runtime configurations, 3rd party system integrations and many more.", - "MicroserviceSupportExplanation6": "The ABP Framework and the ABP Commercial provides infrastructure for micro-service scenarios, micro-service compatible modules, samples and documentation to help you to build your own solution. But don't expect to directly download your dream solution pre-built for you. You will need to understand it and bring some parts together based on your requirements.", - "WhereCanIDownloadSourceCode": "Where can I download source-code?", - "WhereCanIDownloadSourceCodeExplanation": "You can download the source code of all ABP modules, Angular packages and themes via ABP Suite or ABP CLI. See How to download source-code?", + "MicroserviceSupportExplanation6": "The ABP Framework and ABP Commercial provide infrastructure for micro-service scenarios, micro-service compatible modules, samples and documentation to help you build your own solution. But don't expect to directly download your dream solution pre-built for you. You will need to understand it and bring specific parts together based on your requirements.", + "WhereCanIDownloadSourceCode": "Where can I download the source-code?", + "WhereCanIDownloadSourceCodeExplanation": "You can download the source code of all the ABP modules, Angular packages and themes via ABP Suite or ABP CLI. Check out How to download the source-code?", "ComputerLimitation": "How many computers can a developer login when developing ABP?", "ComputerLimitationExplanation": "We specifically permit {0} computers per individual/licensed developer. Whenever there is a need for a developer to develop ABP Commercial products on a third machine, an e-mail should be sent to license@abp.io explaining the situation and we will then make the appropriate allocation in our system.", "RefundPolicy": "Do you have a refund policy?", - "RefundPolicyExplanation": "You can request a refund within 30 days of your license purchase. The Business and Enterprise license types have source-code download option, therefore refunds are not available for the Business and Enterprise (and any licenses that include a right to receive source-code). In addition, no refunds are made for renewals and second license purchases.", + "RefundPolicyExplanation": "You can request a refund within 30 days of your license purchase. The Business and Enterprise license types have source-code download option, therefore refunds are not available for the Business and Enterprise (along with any licenses that include a right to receive source-code). In addition, no refunds are made for renewals and second license purchases.", "HowCanIRefundVat": "How can I refund VAT?", "HowCanIRefundVatExplanation1": "If you made the payment using 2Checkout, you can refund VAT via your 2Checkout account:", - "HowCanIRefundVatExplanation2": "Log in into your 2Checkout account", + "HowCanIRefundVatExplanation2": "Log in to your 2Checkout account", "HowCanIRefundVatExplanation3": "Find the appropriate order and press \"Refund Belated VAT\" (enter your VAT ID)", "HowCanIGetMyInvoice": "How can I get my invoice?", - "HowCanIGetMyInvoiceExplanation": "There are 2 payment gateways for purchasing a license: Iyzico and 2Checkout. If you purchase your license through the 2Checkout gateway, it sends the PDF invoice to your email address, see 2Checkout invoicing. If you purchase through the Iyzico gateway, with custom purchase link or via bank wire transfer, we will prepare and send your invoice. You can request or download your invoice from the organization management page. Before contacting us for the invoice, check your organization management page!", + "HowCanIGetMyInvoiceExplanation": "There are 2 payment gateways for purchasing a license: Iyzico and 2Checkout. If you purchase your license through the 2Checkout gateway, it sends the PDF invoice to your email address, check out 2Checkout invoicing. If you purchase through the Iyzico gateway, with a custom purchase link or via a bank wire transfer, we will prepare and send your invoice. You can request or download your invoice from the organization management page. Before contacting us for the invoice, check your organization management page!", "Forum": "Forum", "SupportExplanation": "ABP Commercial licenses provides a premium forum support by a team consists of the ABP Framework experts.", "PrivateTicket": "Private Ticket", @@ -290,7 +289,7 @@ "PrivateTicketEmailSupport": "Private ticket & email support", "BuyNow": "Buy Now", "PayViaAmexCard": "How can I pay via my AMEX card?", - "PayViaAmexCardDescription": "The default payment gateway 'Iyzico' may decline some AMEX credit cards due to the security measures. In this case, you can pay through the alternative payment gateway '2Checkout'.", + "PayViaAmexCardDescription": "The default payment gateway 'Iyzico' may decline some AMEX credit cards due to security measures. In this case, you can pay through the alternative payment gateway '2Checkout'.", "ThankYou": "Thank you", "InvalidReCaptchaErrorMessage": "There was an error verifying reCAPTCHA. Please try again.", "CompanyName": "Company name", @@ -390,6 +389,19 @@ "TrialLicenseExpireMessage": "You are using the trial license and your trial license will expire on {0}.", "TryForFree": "Try For Free", "TrialLicenseExpiredInfo": "Your trial license period has expired!", - "CommercialNewsletterConfirmationMessage": "I agree to the Terms & Conditions and Privacy Policy." + "CommercialNewsletterConfirmationMessage": "I agree to the Terms & Conditions and Privacy Policy.", + "DowngradeLicensePlan": "Can I downgrade to a lower license plan in the future?", + "DowngradeLicensePlanExplanation": "You cannot downgrade your existing license plan. But you can purchase a new lower license plan and continue your development on the new license. After you purchase a lower license, you just need to login to your new license plan via ABP CLI command: ` abp login -o `.", + "LicenseTransfer": "Can a license be transferred from one developer to another?", + "LicenseTransferExplanation": "Yes! When you purchase a license, you become the license holder, hence you will have access to the organization management page. An organization has owner and developer roles. Owners can manage the developer seats and assign developers. Each assigned developer will login via ABP CLI command into the system and will have development and support permissions.", + "UserOwnerDescription": "The 'Owner' of the organization is the admin of this account. He/she manages the organization by purchasing licenses, allocating developers. An 'Owner' cannot write code in the ABP Commercial projects, cannot download the ABP sample projects, or cannot ask questions on the support website. If you want to do all these, you have to add yourself as a developer too.", + "UserDeveloperDescription": "The 'Developers' can write code in the ABP Commercial projects, download the ABP sample projects, or ask questions on the support website. On the other hand, the 'Developers' cannot manage this organization.", + "RemoveCurrentUserFromOrganizationWarningMessage": "You are removing yourself from your own organization. You will no longer be able to manage this organization, do you confirm?", + "RenewExistingOrganizationOrCreateNewOneMessage": "You can renew the license of your organization(s) by clicking the below \"Extend Now\" button(s) and thus you can extend the license expiry date by 1 year. If you continue to checkout, you will have a new organization. Do you want to continue with a new organization?", + "ExtendNow": "Extend Now", + "ContinueWithNewOrganization": "Continue with a new organization", + "RenewLicenseEarly": "If I renew my license early, will I get the full year?", + "RenewLicenseEarylExplanation": "When you renew your license before your license expiry date, 1 year will be added to your license expiry date. For example, if your license expires on {0}-06-06 and you renew it on {0}-01-01, your new license expiry date will be {1}-06-06." + } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json index 63a240242a..d14e93d051 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "¿Cuál es la diferencia entre ampliar y actualizar la licencia?", "LicenseExtendUpgradeDiffExplanation": "Extending: By extending/renewing your license, you will continue to get premium support and get major updates for the modules and themes. Besides, you will be able to continue creating new projects. And you will still be able to use ABP Suite which speeds up your development.
Upgrading: By upgrading your license, you will promote to a higher license plan which will allow you to get additional benefits. See the license comparison table to check the differences between the license plans.On the other hand, when you upgrade, your license expiry date will not change!To extend your license end date, you need to extend your license.", "LicenseRenewalCost": "¿Cuál es el costo de renovación de la licencia después de 1 año?", - "LicenseRenewalCostExplanation": "La tasa de renovación (extensión) de todas las licencias perpetuas comerciales de ABP es {0} del precio de lista de la licencia. El precio de renovación de la licencia de equipo estándar es de $ {1}, la licencia comercial estándar es de $ {2} y la licencia empresarial estándar es de $ {3}. Si ya es cliente, inicie sesión en su cuenta para revisar los precios de renovación disponibles.", + "LicenseRenewalCostExplanation": "El precio de renovación (extensión) de la Licencia de equipo estándar es de $ {0}, la Licencia comercial estándar es de $ {1} y la Licencia empresarial estándar es de $ {2}. Si ya es cliente, inicie sesión en su cuenta para revisar los precios de renovación disponibles.", "HowDoIRenewMyLicense": "¿Cómo renuevo mi licencia?", "HowDoIRenewMyLicenseExplanation": "Puede renovar su licencia navegando a la página de administración de la organización . Para aprovechar nuestras tarifas de renovación anticipada con descuento, asegúrese de renovar antes de que expire su licencia. Sin embargo, no se preocupe por no saber cuándo se cierra su oportunidad de Renovación Temprana. Recibirá 3 correos electrónicos de recordatorio antes de que expire su suscripción. Los enviaremos 30 días, 7 días y 1 día antes del vencimiento.", "IsSourceCodeIncluded": "¿Mi licencia incluye el código fuente de los módulos y temas comerciales?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "¿Puedo cambiar los desarrolladores registrados de mi organización en el futuro?", "ChangingDevelopersExplanation": "Además de agregar nuevos desarrolladores a su licencia, también puede cambiar los desarrolladores existentes (puede eliminar un desarrollador y agregar uno nuevo al mismo puesto) sin ningún costo adicional.", "WhatHappensWhenLicenseEnds": "¿Qué sucede cuando finaliza mi período de licencia?", - "WhatHappensWhenLicenseEndsExplanation1": "El tipo de licencia comercial de ABP es licencia perpetua . Una vez que expire su licencia, puede continuar desarrollando su proyecto. Y no está obligado a renovar su licencia. Su licencia viene con un plan de soporte y actualizaciones de un año listo para usar. Para continuar recibiendo nuevas funciones, mejoras de rendimiento, corrección de errores, soporte y continuar usando ABP Suite, asegúrese de renovar su plan cada año. Cuando su licencia expire, no podrá obtener más de los siguientes beneficios;", + "WhatHappensWhenLicenseEndsExplanation1": "El tipo de licencia comercial de ABP es licencia perpetua . Una vez que expire su licencia, puede continuar desarrollando su proyecto. Y no está obligado a renovar su licencia. Su licencia viene con una actualización de un año y un plan de soporte listo para usar. Para continuar obteniendo nuevas funciones, mejoras de rendimiento, corrección de errores, soporte y continuar usando ABP Suite, necesita renovar su licencia. Cuando su licencia expire, no obtendrá los siguientes beneficios;", "WhatHappensWhenLicenseEndsExplanation2": "No puede crear nuevas soluciones utilizando ABP Commercial, pero puede continuar desarrollando sus aplicaciones existentes para siempre.", - "WhatHappensWhenLicenseEndsExplanation3": "Podrá obtener actualizaciones para los módulos y temas dentro de su versión PRINCIPAL. Por ejemplo; si está utilizando la v3.2.0 de un módulo, aún puede obtener actualizaciones para la v3.x.x (v3.3.0, v3.5.2 ... etc.) de ese módulo. Pero no puede obtener actualizaciones para la próxima versión principal (como v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "Podrá obtener actualizaciones para los módulos y temas dentro de su versión PRINCIPAL (excepto las versiones RC o Preview). Por ejemplo; Si está utilizando la v3.2.0 de un módulo, aún puede obtener actualizaciones para la v3.x.x (v3.3.0, v3.5.2 ... etc.) de ese módulo. Pero no puede obtener actualizaciones para la próxima versión principal (como v4.x, v5.x). Por ejemplo, cuando su licencia expiró, la última versión fue la v4.4.3 y la última versión preliminar fue la v5.0.0-rc.2, puede acceder a la v4.X.X pero no a la v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "No puede instalar nuevos módulos y temas agregados a la plataforma comercial de ABP después de que finalice su licencia.", "WhatHappensWhenLicenseEndsExplanation5": "No puede utilizar ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "Ya no puede obtener la asistencia premium .", - "WhatHappensWhenLicenseEndsExplanation7": "Puede renovar su suscripción si desea continuar obteniendo estos beneficios. Si renueva su suscripción dentro de 1 mes después de que expire su licencia, se aplica un descuento del 20%.", + "WhatHappensWhenLicenseEndsExplanation7": "Puede extender (renovar) su licencia si desea continuar recibiendo estos beneficios. Si extiende su licencia dentro de 1 mes después de que caduque su licencia, se aplicarán los siguientes descuentos: Licencia de equipo {0}% de descuento, Licencia comercial {1}% de descuento, Licencia empresarial {2}% de descuento .", "WhenShouldIRenewMyLicense": "¿Cuándo debo renovar mi licencia?", - "WhenShouldIRenewMyLicenseExplanation1": "Si renueva su licencia dentro de 1 mes después de que caduque, se aplicará un descuento del 20%.", - "WhenShouldIRenewMyLicenseExplanation2": "Si renueva su licencia después de 1 mes desde la fecha de vencimiento de su licencia, el precio de renovación será el mismo que el precio de compra de la licencia y no habrá descuento por su renovación.", + "WhenShouldIRenewMyLicenseExplanation": "Si renueva su licencia dentro de 1 mes después de su vencimiento, se aplicarán los siguientes descuentos: Licencia de equipo {0}% de descuento, Licencia comercial {1}% de descuento, Licencia empresarial {2}% de descuento . Si renueva su licencia 1 mes después de la fecha de vencimiento de su licencia, el precio de renovación será el mismo que el precio de compra de la licencia y no habrá descuento en su renovación.", "TrialPlan": "¿Tiene un plan de prueba?", "TrialPlanExplanation": "Por ahora, ABP Commercial no tiene un plan de prueba. Para las licencias Team ofrecemos 30 días de garantía de devolución de dinero. Solo puede solicitar un reembolso en los primeros 30 días. Para las licencias Business y Enterprise, proporcionamos un reembolso del 60% en 30 días. Esto se debe a que las licencias Business y Enterprise incluyen el código fuente completo de todos los módulos y temas.", "DoYouAcceptBankWireTransfer": "¿Aceptan transferencia bancaria?", - "DoYouAcceptBankWireTransferExplanation": "Sí, aceptamos transferencia bancaria.
Después de enviar la tarifa de la licencia mediante transferencia bancaria, envíenos un correo electrónico a info@abp.io con su recibo y el tipo de licencia solicitada. Nuestra información de cuenta bancaria internacional:", + "DoYouAcceptBankWireTransferExplanation": "Sí, aceptamos transferencia bancaria.
Después de enviar la tarifa de la licencia mediante transferencia bancaria, envíenos un correo electrónico a accounting@abp.io con su recibo y el tipo de licencia solicitada. Nuestra información de cuenta bancaria internacional:", "HowToUpgrade": "¿Cómo actualizar las aplicaciones existentes cuando hay una nueva versión disponible?", "HowToUpgradeExplanation1": "Cuando crea una nueva aplicación utilizando ABP Commercial, todos los módulos y el tema se utilizan como paquetes NuGet y NPM. Por lo tanto, puede actualizar fácilmente los paquetes cuando haya una nueva versión disponible.", "HowToUpgradeExplanation2": "Además de las actualizaciones estándar de NuGet/NPM, ABP CLI proporciona un comando de actualización que busca y actualiza automáticamente todos los paquetes relacionados con ABP en su solución.", @@ -390,6 +389,9 @@ "TrialLicenseExpireMessage": "Está utilizando la licencia de prueba y su licencia de prueba caducará el {0}.", "TryForFree": "Prueba gratis", "TrialLicenseExpiredInfo": "¡Su período de licencia de prueba ha expirado!", - "CommercialNewsletterConfirmationMessage": "Acepto los Términos y condiciones y la Política de privacidad ." + "CommercialNewsletterConfirmationMessage": "Acepto los Términos y condiciones y la Política de privacidad .", + "ContinueWithNewOrganization": "Continuar con una nueva organización", + "RenewLicenseEarly": "Si renuevo mi licencia antes, ¿obtendré el año completo?", + "RenewLicenseEarylExplanation": "Cuando renueve su licencia antes de la fecha de vencimiento de su licencia, se agregará 1 año a la fecha de vencimiento de su licencia. Por ejemplo, si su licencia vence el {0} -06-06 y la renueva el {0} -01-01, la fecha de vencimiento de su nueva licencia será {1} -06-06." } } \ 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 507f2083f3..4437ed862d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Mitä eroa on lisenssin laajennuksella ja päivityksellä?", "LicenseExtendUpgradeDiffExplanation": " Extension: en prolongeant/renouvelant votre licence, vous continuerez à bénéficier d'une assistance premium et à des mises à jour majeures pour les modules et les thèmes. De plus, vous pourrez continuer à créer de nouveaux projets. Et vous pourrez toujours utiliser ABP Suite, ce qui accélère votre développement.
Mise à niveau: En mettant à jour votre licence, vous passerez à un plan de licence supérieur qui vous permettra d’obtenir des avantages supplémentaires . Consultez le tableau de comparaison des licences pour vérifier les différences entre les plans de licence. En revanche, lors de la mise à niveau, la date d'expiration de votre licence ne changera pas! Pour prolonger la date de fin de votre licence, vous devez prolonger votre licence.", "LicenseRenewalCost": "Mitkä ovat lisenssin uusimiskustannukset vuoden kuluttua?", - "LicenseRenewalCostExplanation": "Kaikkien ABP Commercial perpetual -lisenssien uusimis- (laajennus) hinta on {0} lisenssiluettelohinnasta. Tavallisen tiimilisenssin uusimishinta on $ {1}, tavallinen yrityslisenssi on $ {2} ja tavallinen yrityslisenssi on $ {3}. Jos olet jo asiakas, kirjaudu tilillesi ja tarkista käytettävissä oleva uusimishinta.", + "LicenseRenewalCostExplanation": "Standardin tiimilisenssin uusimishinta (pidennys) on ${0}, normaalin yrityslisenssin hinta on {1} $ ja normaalin yrityslisenssin hinta on {2} $. Jos olet jo asiakas, kirjaudu sisään tiliisi ja tarkista käytettävissä olevat uusimishinnat.", "HowDoIRenewMyLicense": "Kuinka uusin lisenssin?", "HowDoIRenewMyLicenseExplanation": "Voit uusia käyttöoikeutesi siirtymällä organisaation hallintasivulle . Jotta voisit hyödyntää alennettujen varhaisen uusimisen hintojamme, muista uusia ennen lisenssin voimassaolon päättymistä. Älä huoli siitä, ettet tiedä, milloin Varhaisen uudistamisen mahdollisuus sulkeutuu. Saat kaksi muistutussähköpostia ennen tilauksesi päättymistä. Lähetämme ne 30 päivää, 7 päivää ennen vanhenemista.", "IsSourceCodeIncluded": "Sisältääkö lisenssini kaupallisten moduulien ja teemojen lähdekoodin?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Voinko muuttaa organisaationi rekisteröityneitä kehittäjiä tulevaisuudessa?", "ChangingDevelopersExplanation": "Uusien kehittäjien lisäämisen lisenssiin lisäksi voit myös muuttaa olemassa olevia kehittäjiä (voit poistaa kehittäjän ja lisätä uuden samalle paikalle) ilman lisäkustannuksia.", "WhatHappensWhenLicenseEnds": "Mitä tapahtuu, kun lisenssikauteni päättyy?", - "WhatHappensWhenLicenseEndsExplanation1": "ABP Commercial -lisenssityyppi on jatkuva lisenssi . Kun lisenssi vanhenee, voit jatkaa projektisi kehittämistä. Ja sinun ei tarvitse uudistaa lisenssiäsi. Lisenssisi mukana toimitetaan yhden vuoden päivitys- ja tukisuunnitelma. Jos haluat edelleen saada uusia ominaisuuksia, suorituskyvyn parannuksia, virhekorjauksia, tukea ja jatkaa ABP Suite -sovelluksen käyttöä, muista uusia suunnitelma joka vuosi. Kun lisenssi vanhenee, et voi saada enemmän seuraavista eduista;", + "WhatHappensWhenLicenseEndsExplanation1": "ABP Commercial -lisenssityyppi on ikuinen lisenssi. Kun lisenssisi vanhenee, voit jatkaa projektisi kehittämistä. Et ole velvollinen uusimaan ajokorttiasi. Lisenssi sisältää yhden vuoden päivityksen ja tukisuunnitelman. Jotta voit jatkaa uusien ominaisuuksien, suorituskyvyn parannuksia, virheenkorjauksia, tukea ja jatkaa ABP Suiten käyttöä, sinun on uusittava lisenssisi. Kun lisenssisi vanhenee, et saa seuraavia etuja;", "WhatHappensWhenLicenseEndsExplanation2": "Et voi luoda uusia ratkaisuja ABP Commercial -sovelluksella, mutta voit jatkaa olemassa olevien sovellusten kehittämistä ikuisesti.", - "WhatHappensWhenLicenseEndsExplanation3": "Voit saada päivityksiä moduuleista ja teemoista MAJOR-versiostasi. Esimerkiksi; jos käytät moduulin v3.2.0-versiota, voit silti saada päivityksiä kyseisen moduulin versiolle v3.x.x (v3.3.0, v3.5.2... jne.). Mutta et voi saada päivityksiä seuraavaan pääversioon (kuten v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "Voit saada päivityksiä moduuleille ja teemoille MAJOR-versiosi sisällä (paitsi RC- tai esikatseluversiot). Esimerkiksi; jos käytät moduulin v3.2.0:aa, voit silti saada päivityksiä kyseisen moduulin v3.x.x:lle (v3.3.0, v3.5.2... jne.). Mutta et voi saada päivityksiä seuraavaan pääversioon (kuten v4.x, v5.x). Esimerkiksi kun lisenssisi vanheni, viimeisin versio oli v4.4.3 ja uusin esikatseluversio v5.0.0-rc.2, voit käyttää v4.X.X, mutta et voi käyttää v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Et voi asentaa uusia moduuleja ja teemoja, jotka on lisätty ABP Commercial -alustalle käyttöoikeuden päättymisen jälkeen.", "WhatHappensWhenLicenseEndsExplanation5": "Et voi käyttää ABP Suitea.", "WhatHappensWhenLicenseEndsExplanation6": "Et voi enää saada premium-tukea .", - "WhatHappensWhenLicenseEndsExplanation7": "Voit uusia tilauksesi, jos haluat jatkaa näiden etujen saamista. Tilauksen uusimisessa on 20% alennus.", + "WhatHappensWhenLicenseEndsExplanation7": "Voit jatkaa (uusia) lisenssiäsi, jos haluat jatkaa näiden etujen saamista. Jos jatkat lisenssiäsi 1 kuukauden kuluessa lisenssin vanhenemisesta, seuraavat alennukset sovelletaan: Team License {0}% alennus, Business License {1}% alennus, Enterprise License {2}% alennus .", "WhenShouldIRenewMyLicense": "Milloin minun pitäisi uusia lisenssini?", - "WhenShouldIRenewMyLicenseExplanation1": "Jos uusit lisenssin kuukauden kuluessa lisenssin voimassaolon päättymisestä, lisenssin kokonaishintaan sovelletaan 20% alennusta.", - "WhenShouldIRenewMyLicenseExplanation2": "Jos uusit lisenssin kuukauden kuluttua lisenssin voimassaolon päättymispäivästä, uusimishinta on sama kuin lisenssin ostohinta eikä uusimiselle ole alennusta.", + "WhenShouldIRenewMyLicenseExplanation": "Jos uusit lisenssisi 1 kuukauden kuluessa lisenssin vanhenemisesta, seuraavat alennukset sovelletaan: Team License {0} % alennus, Business License {1} % alennus, Enterprise License {2} % alennus . Jos uusit lisenssisi 1 kuukauden lisenssin päättymispäivän jälkeen, uusimishinta on sama kuin lisenssin ostohinta, eikä uusimisesta saa alennusta.", "TrialPlan": "Onko sinulla kokeilusuunnitelma?", "TrialPlanExplanation": "Toistaiseksi ABP Commercialilla ei ole kokeilusuunnitelmaa. Joukkueen lisensseille tarjoamme 30 päivän rahanpalautustakuun. Voit vain pyytää hyvitystä 30 ensimmäisen päivän aikana. Yritys- ja yrityslisensseille hyvitämme 60% 30 päivässä. Tämä johtuu siitä, että yritys- ja yrityslisenssit sisältävät kaikkien moduulien ja teemojen täydellisen lähdekoodin.", "DoYouAcceptBankWireTransfer": "Hyväksytkö pankkisiirron?", - "DoYouAcceptBankWireTransferExplanation": "Kyllä, hyväksymme pankkisiirron.
Kun olet lähettänyt lisenssimäärän pankkisiirrolla, lähetä meille kuitti ja pyydetty lisenssityyppi sähköpostitse.
Kansainväliset pankkitilitiedot:", + "DoYouAcceptBankWireTransferExplanation": "Kyllä, hyväksymme pankkisiirron.
Kun olet lähettänyt lisenssimaksun pankkisiirrolla, lähetä meille sähköposti osoitteeseen accounting@abp.io kuittisi ja pyydetty lisenssityyppi. Kansainväliset pankkitilitietomme:", "HowToUpgrade": "Kuinka päivittää olemassa olevia sovelluksia, kun uusi versio on saatavilla?", "HowToUpgradeExplanation1": "Kun luot uuden sovelluksen ABP Commercial -sovelluksella, kaikkia moduuleja ja teemaa käytetään NuGet- ja NPM-paketteina. Joten voit päivittää paketit helposti, kun uusi versio on saatavilla.", "HowToUpgradeExplanation2": "Normaalien NuGet/NPM-päivitysten lisäksi ABP CLI tarjoaa päivityskomennon, joka etsii ja päivittää kaikki ratkaisusi ABP-paketit.", @@ -390,6 +389,9 @@ "TrialLicenseExpireMessage": "Käytät kokeiluversiota, ja kokeilukäyttölupasi vanhenee {0}.", "TryForFree": "Kokeile ilmaiseksi", "TrialLicenseExpiredInfo": "Kokeilulisenssijaksosi on umpeutunut!", - "CommercialNewsletterConfirmationMessage": "Hyväksyn käyttöehdot ja tietosuojakäytännön ." + "CommercialNewsletterConfirmationMessage": "Hyväksyn käyttöehdot ja tietosuojakäytännön .", + "ContinueWithNewOrganization": "Jatka uudessa organisaatiossa", + "RenewLicenseEarly": "Jos uusin ajokorttini etuajassa, saanko koko vuoden?", + "RenewLicenseEarylExplanation": "Kun uusit lisenssisi ennen lisenssin vanhenemispäivää, lisenssin voimassaolopäivään lisätään 1 vuosi. Jos lisenssisi vanhenee esimerkiksi {0}-06-06 ja uusit sen {0}-01-01, uusi lisenssisi päättymispäivä on {1}-06-06." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json index 33629b2dcf..392c9b06ab 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Quelle est la différence entre l'extension de licence et la mise à niveau?", "LicenseExtendUpgradeDiffExplanation": " Extension: en prolongeant/renouvelant votre licence, vous continuerez à bénéficier d'une assistance premium et à des mises à jour majeures pour les modules et les thèmes. De plus, vous pourrez continuer à créer de nouveaux projets. Et vous pourrez toujours utiliser ABP Suite, ce qui accélère votre développement.
Mise à niveau: En mettant à jour votre licence, vous passerez à un plan de licence supérieur qui vous permettra d’obtenir des avantages supplémentaires . Consultez le tableau de comparaison des licences pour vérifier les différences entre les plans de licence. En revanche, lors de la mise à niveau, la date d'expiration de votre licence ne changera pas! Pour prolonger la date de fin de votre licence, vous devez prolonger votre licence.", "LicenseRenewalCost": "Quel est le coût du renouvellement de la licence après 1 an?", - "LicenseRenewalCostExplanation": "Le taux de renouvellement (extension) de toutes les licences perpétuelles ABP Commercial correspond à {0} du prix catalogue de la licence. Le prix de renouvellement de la licence d'équipe standard est de {1} $, la licence professionnelle standard de {2} $ et la licence d'entreprise standard de {3} $. Si vous êtes déjà client, connectez-vous à votre compte pour consulter les tarifs de renouvellement disponibles.", + "LicenseRenewalCostExplanation": "Le prix de renouvellement (extension) de la licence d'équipe standard est de {0} $, la licence d'entreprise standard est de {1} $ et la licence d'entreprise standard est de {2} $. Si vous êtes déjà client, connectez-vous à votre compte pour consulter les tarifs de renouvellement disponibles.", "HowDoIRenewMyLicense": "Comment renouveler ma licence?", "HowDoIRenewMyLicenseExplanation": "Vous pouvez renouveler votre licence en accédant à la page de gestion de l'organisation . Afin de profiter de nos tarifs réduits de renouvellement anticipé, assurez-vous de renouveler avant l'expiration de votre licence. Cependant, ne vous inquiétez pas de ne pas savoir quand votre opportunité de renouvellement anticipé se termine. Vous recevrez 2 e-mails de rappel avant l'expiration de votre abonnement. Nous les enverrons dans les 30 jours, 7 jours avant l'expiration.", "IsSourceCodeIncluded": "Ma licence inclut-elle le code source des modules et thèmes commerciaux?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Puis-je changer les développeurs enregistrés de mon organisation à l'avenir?", "ChangingDevelopersExplanation": "En plus d'ajouter de nouveaux développeurs à votre licence, vous pouvez également modifier les développeurs existants (vous pouvez supprimer un développeur et en ajouter un nouveau sur le même siège) sans aucun coût supplémentaire.", "WhatHappensWhenLicenseEnds": "Que se passe-t-il à la fin de ma période de licence?", - "WhatHappensWhenLicenseEndsExplanation1": "Le type de licence commerciale ABP est licence perpétuelle . Une fois votre licence expirée, vous pouvez continuer à développer votre projet. Et vous n'êtes pas obligé de renouveler votre licence. Votre licence est livrée avec un plan de mise à jour et de support d'un an prêt à l'emploi. Pour continuer à recevoir de nouvelles fonctionnalités, des améliorations de performances, des corrections de bogues, une assistance et continuer à utiliser ABP Suite, assurez-vous de renouveler votre plan chaque année. Lorsque votre licence expirera, vous ne pourrez plus bénéficier des avantages suivants;", + "WhatHappensWhenLicenseEndsExplanation1": "Le type de licence commerciale ABP est licence perpétuelle. Une fois votre licence expirée, vous pouvez continuer à développer votre projet. Et vous n'êtes pas obligé de renouveler votre licence. Votre licence est livrée avec une mise à jour d'un an et un plan d'assistance prêt à l'emploi. Pour continuer à obtenir de nouvelles fonctionnalités, des améliorations de performances, des corrections de bogues, une assistance et continuer à utiliser ABP Suite, vous devez renouveler votre licence. À l'expiration de votre licence, vous ne bénéficierez pas des avantages suivants :", "WhatHappensWhenLicenseEndsExplanation2": "Vous ne pouvez pas créer de nouvelles solutions en utilisant ABP Commercial, mais vous pouvez continuer à développer vos applications existantes pour toujours.", - "WhatHappensWhenLicenseEndsExplanation3": "Vous pourrez obtenir des mises à jour pour les modules et les thèmes de votre version MAJEURE. Par example; si vous utilisez la v3.2.0 d'un module, vous pouvez toujours obtenir des mises à jour pour la v3.x.x (v3.3.0, v3.5.2... etc.) de ce module. Mais vous ne pouvez pas obtenir de mises à jour pour la prochaine version majeure (comme v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "Vous pourrez obtenir des mises à jour pour les modules et les thèmes de votre version MAJEURE (à l'exception des versions RC ou Preview). Par example; si vous utilisez la v3.2.0 d'un module, vous pouvez toujours obtenir des mises à jour pour la v3.x.x (v3.3.0, v3.5.2... etc.) de ce module. Mais vous ne pouvez pas obtenir de mises à jour pour la prochaine version majeure (comme v4.x, v5.x). Par exemple, lorsque votre licence a expiré, la dernière version était la v4.4.3 et la dernière version d'aperçu était la v5.0.0-rc.2, vous pouvez accéder à la v4.X.X mais vous ne pouvez pas accéder à la v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Vous ne pouvez pas installer de nouveaux modules et thèmes ajoutés à la plate-forme ABP Commercial après la fin de votre licence.", "WhatHappensWhenLicenseEndsExplanation5": "Vous ne pouvez pas utiliser ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "Vous ne pouvez plus bénéficier de l ' assistance premium .", - "WhatHappensWhenLicenseEndsExplanation7": "Vous pouvez renouveler votre abonnement si vous souhaitez continuer à bénéficier de ces avantages. Il y a une réduction de 20% lorsque vous renouvelez votre abonnement.", + "WhatHappensWhenLicenseEndsExplanation7": "Vous pouvez prolonger (renouveler) votre licence si vous souhaitez continuer à bénéficier de ces avantages. Si vous prolongez votre licence dans un délai de 1 mois après l'expiration de votre licence, les remises suivantes seront appliquées : licence d'équipe {0} % de remise, licence d'entreprise {1} % de remise, licence d'entreprise {2} % de remise .", "WhenShouldIRenewMyLicense": "Quand dois-je renouveler ma licence?", - "WhenShouldIRenewMyLicenseExplanation1": "Si vous renouvelez votre licence dans un délai de 1 mois après l'expiration de votre licence, une réduction de 20% sera appliquée au prix total de la licence.", - "WhenShouldIRenewMyLicenseExplanation2": "Si vous renouvelez votre licence 1 mois après la date d'expiration de votre licence, le prix de renouvellement sera le même que le prix d'achat de la licence et il n'y aura pas de réduction pour votre renouvellement.", + "WhenShouldIRenewMyLicenseExplanation": "Si vous renouvelez votre licence dans un délai de 1 mois après l'expiration de votre licence, les remises suivantes seront appliquées : licence d'équipe {0} % de remise, licence d'entreprise {1} % de remise, licence d'entreprise {2} % de remise . Si vous renouvelez votre licence 1 mois après la date d'expiration de votre licence, le prix de renouvellement sera le même que le prix d'achat de la licence et il n'y aura aucune remise sur votre renouvellement.", "TrialPlan": "Avez-vous un plan d'essai?", "TrialPlanExplanation": "Pour l'instant, ABP Commercial n'a pas de plan d'essai. Pour les licences Team, nous offrons une garantie de remboursement de 30 jours. Vous pouvez simplement demander un remboursement dans les 30 premiers jours. Pour les licences Business et Enterprise, nous fournissons un remboursement de 60% en 30 jours. En effet, les licences Business et Enterprise incluent le code source complet de tous les modules et thèmes.", "DoYouAcceptBankWireTransfer": "Acceptez-vous les virements bancaires?", - "DoYouAcceptBankWireTransferExplanation": "Oui, nous acceptons les virements bancaires.
Après avoir envoyé le montant de la licence par virement bancaire, envoyez-nous votre reçu et le type de licence demandé par e-mail.
Nos coordonnées bancaires internationales:", + "DoYouAcceptBankWireTransferExplanation": "Oui, nous acceptons les virements bancaires.
Après avoir envoyé les frais de licence par virement bancaire, envoyez-nous par e-mail à accounting@abp.io votre reçu et le type de licence demandé. Nos coordonnées bancaires internationales :", "HowToUpgrade": "Comment mettre à niveau des applications existantes lorsqu'une nouvelle version est disponible?", "HowToUpgradeExplanation1": "Lorsque vous créez une nouvelle application à l'aide d'ABP Commercial, tous les modules et le thème sont utilisés en tant que packages NuGet et NPM. Ainsi, vous pouvez facilement mettre à jour les packages lorsqu'une nouvelle version est disponible.", "HowToUpgradeExplanation2": "Outre les mises à niveau standard de NuGet/NPM, ABP CLI fournit une commande de mise à jour qui recherche et met automatiquement à niveau tous les packages associés à ABP dans votre solution.", @@ -390,6 +389,9 @@ "TrialLicenseExpireMessage": "Vous utilisez la licence d'essai et votre licence d'essai expirera le {0}.", "TryForFree": "Essayer gratuitement", "TrialLicenseExpiredInfo": "Votre période de licence d'essai a expiré !", - "CommercialNewsletterConfirmationMessage": "J'accepte les Conditions générales et la Politique de confidentialité ." + "CommercialNewsletterConfirmationMessage": "J'accepte les Conditions générales et la Politique de confidentialité .", + "ContinueWithNewOrganization": "Continuer avec une nouvelle organisation", + "RenewLicenseEarly": "Si je renouvelle ma licence plus tôt, obtiendrai-je l'année complète ?", + "RenewLicenseEarylExplanation": "Lorsque vous renouvelez votre licence avant la date d'expiration de votre licence, 1 an sera ajouté à la date d'expiration de votre licence. Par exemple, si votre licence expire le {0}-06-06 et que vous la renouvelez le {0}-01-01, la nouvelle date d'expiration de votre licence sera le {1}-06-06." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json index bbaa413d5e..a7c3c2aeeb 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "लाइसेंस विस्तार और उन्नयन के बीच क्या अंतर है?", "LicenseExtendUpgradeDiffExplanation": "Extending: By extending/renewing your license, you will continue to get premium support and get major updates for the modules and themes. Besides, you will be able to continue creating new projects. And you will still be able to use ABP Suite which speeds up your development.
Upgrading: By upgrading your license, you will promote to a higher license plan which will allow you to get additional benefits. See the license comparison table to check the differences between the license plans.On the other hand, when you upgrade, your license expiry date will not change!To extend your license end date, you need to extend your license.", "LicenseRenewalCost": "1 वर्ष के बाद लाइसेंस नवीनीकरण लागत क्या है?", - "LicenseRenewalCostExplanation": "सभी एबीपी वाणिज्यिक स्थायी लाइसेंसों की नवीनीकरण (विस्तार) दर लाइसेंस सूची मूल्य का {0} है। मानक टीम लाइसेंस का नवीनीकरण मूल्य ${1} है, मानक व्यवसाय लाइसेंस ${2} है और मानक एंटरप्राइज़ लाइसेंस ${3} है। यदि आप पहले से ही ग्राहक हैं, तो उपलब्ध नवीनीकरण मूल्य-निर्धारण की समीक्षा के लिए अपने खाते में लॉग इन करें।", + "LicenseRenewalCostExplanation": "मानक टीम लाइसेंस का नवीनीकरण (विस्तार) मूल्य ${0} है, मानक व्यवसाय लाइसेंस ${1} है और मानक एंटरप्राइज़ लाइसेंस ${2} है। यदि आप पहले से ही एक ग्राहक हैं, तो उपलब्ध नवीनीकरण मूल्य निर्धारण की समीक्षा के लिए अपने खाते में लॉग इन करें।", "HowDoIRenewMyLicense": "मैं अपने लाइसेंस का नवीनीकरण कैसे करूँ?", "HowDoIRenewMyLicenseExplanation": "आप संगठन प्रबंधन पृष्ठ पर नेविगेट करके अपने लाइसेंस का नवीनीकरण कर सकते हैं। हमारी रियायती प्रारंभिक नवीनीकरण दरों का लाभ उठाने के लिए, अपने लाइसेंस की समय सीमा समाप्त होने से पहले नवीनीकरण करना सुनिश्चित करें। हालाँकि, यह जानने की चिंता न करें कि आपका प्रारंभिक नवीनीकरण अवसर कब बंद होगा। आपकी सदस्यता समाप्त होने से पहले आपको 2 अनुस्मारक ई-मेल प्राप्त होंगे। हम उन्हें समाप्ति से ७ दिन पहले, ३० दिनों में भेजेंगे।", "IsSourceCodeIncluded": "क्या मेरे लाइसेंस में वाणिज्यिक मॉड्यूल और थीम का स्रोत कोड शामिल है?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "क्या मैं भविष्य में अपने संगठन के पंजीकृत डेवलपर्स को बदल सकता हूं?", "ChangingDevelopersExplanation": "अपने लाइसेंस में नए डेवलपर्स को जोड़ने के अलावा, आप मौजूदा डेवलपर्स को भी बदल सकते हैं (आप एक डेवलपर को हटा सकते हैं और एक ही सीट पर एक नया जोड़ सकते हैं) बिना किसी अतिरिक्त लागत के।", "WhatHappensWhenLicenseEnds": "मेरे लाइसेंस की अवधि समाप्त होने पर क्या होता है?", - "WhatHappensWhenLicenseEndsExplanation1": "ABP वाणिज्यिक लाइसेंस प्रकार स्थायी लाइसेंस है। आपके लाइसेंस की समय सीमा समाप्त होने के बाद, आप अपने प्रोजेक्ट को विकसित करना जारी रख सकते हैं। और आप अपने लाइसेंस को नवीनीकृत करने के लिए बाध्य नहीं हैं। आपका लाइसेंस बॉक्स से बाहर एक साल के अपडेट और समर्थन योजना के साथ आता है। नई सुविधाओं, प्रदर्शन में सुधार, बग फिक्स, समर्थन प्राप्त करना जारी रखने और एबीपी सूट का उपयोग जारी रखने के लिए, हर साल अपनी योजना को नवीनीकृत करना सुनिश्चित करें। जब आपका लाइसेंस समाप्त हो जाता है, तो आप निम्नलिखित में से अधिक लाभ प्राप्त नहीं कर पाएंगे;", + "WhatHappensWhenLicenseEndsExplanation1": "ABP वाणिज्यिक लाइसेंस प्रकार सतत लाइसेंस है। आपके लाइसेंस की समय सीमा समाप्त होने के बाद, आप अपने प्रोजेक्ट को विकसित करना जारी रख सकते हैं। और आप अपने लाइसेंस को नवीनीकृत करने के लिए बाध्य नहीं हैं। आपका लाइसेंस एक साल के अपडेट और आउट ऑफ द बॉक्स के साथ आता है। नई सुविधाओं, प्रदर्शन में सुधार, बग फिक्स, समर्थन प्राप्त करने और एबीपी सूट का उपयोग जारी रखने के लिए, आपको अपने लाइसेंस को नवीनीकृत करने की आवश्यकता है। जब आपका लाइसेंस समाप्त हो जाता है, तो आपको निम्नलिखित लाभ नहीं मिलेंगे;", "WhatHappensWhenLicenseEndsExplanation2": "आप एबीपी कमर्शियल का उपयोग करके नए समाधान नहीं बना सकते हैं, लेकिन आप अपने मौजूदा अनुप्रयोगों को हमेशा के लिए विकसित कर सकते हैं।", - "WhatHappensWhenLicenseEndsExplanation3": "आप अपने मुख्य संस्करण के भीतर मॉड्यूल और विषयों के लिए अद्यतन प्राप्त करने में सक्षम होंगे। उदाहरण के लिए; यदि आप किसी मॉड्यूल के v3.2.0 का उपयोग कर रहे हैं, तो आप उस मॉड्यूल के v3.x.x (v3.3.0, v3.5.2... आदि) के लिए अपडेट प्राप्त कर सकते हैं। लेकिन आपको अगले प्रमुख संस्करण के लिए अपडेट नहीं मिल सकता है (जैसे v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "आप अपने MAJOR संस्करण (RC या पूर्वावलोकन संस्करणों को छोड़कर) के भीतर मॉड्यूल और थीम के लिए अपडेट प्राप्त करने में सक्षम होंगे। उदाहरण के लिए; यदि आप किसी मॉड्यूल के v3.2.0 का उपयोग कर रहे हैं, तो भी आप उस मॉड्यूल के v3.x.x (v3.3.0, v3.5.2... आदि) के लिए अपडेट प्राप्त कर सकते हैं। लेकिन आप अगले प्रमुख संस्करण (जैसे v4.x, v5.x) के लिए अपडेट प्राप्त नहीं कर सकते। उदाहरण के लिए, जब आपका लाइसेंस समाप्त हो गया था, तो नवीनतम रिलीज़ v4.4.3 था और नवीनतम पूर्वावलोकन संस्करण v5.0.0-rc.2 था, आप v4.X.X तक पहुंच सकते हैं लेकिन आप v5.X.X तक नहीं पहुंच सकते।", "WhatHappensWhenLicenseEndsExplanation4": "आप अपने लाइसेंस समाप्त होने के बाद एबीपी वाणिज्यिक मंच में जोड़े गए नए मॉड्यूल और थीम स्थापित नहीं कर सकते।", "WhatHappensWhenLicenseEndsExplanation5": "आप एबीपी सूट का उपयोग नहीं कर सकते।", "WhatHappensWhenLicenseEndsExplanation6": "अब आपको प्रीमियम सहायता नहीं मिल सकती है।", - "WhatHappensWhenLicenseEndsExplanation7": "यदि आप इन लाभों को प्राप्त करना जारी रखना चाहते हैं तो आप अपनी सदस्यता को नवीनीकृत कर सकते हैं। जब आप अपनी सदस्यता को नवीनीकृत करते हैं तो 20% छूट मिलती है।", + "WhatHappensWhenLicenseEndsExplanation7": "यदि आप इन लाभों को प्राप्त करना जारी रखना चाहते हैं तो आप अपने लाइसेंस का विस्तार (नवीनीकरण) कर सकते हैं। यदि आप अपने लाइसेंस की समय सीमा समाप्त होने के बाद 1 महीने के भीतर अपना लाइसेंस बढ़ाते हैं, तो निम्नलिखित छूट लागू होंगी: टीम लाइसेंस {0}% छूट, व्यवसाय लाइसेंस {1}% छूट, एंटरप्राइज़ लाइसेंस {2}% छूट .", "WhenShouldIRenewMyLicense": "मुझे अपना लाइसेंस कब नवीनीकृत करना चाहिए?", - "WhenShouldIRenewMyLicenseExplanation1": "यदि आप अपना लाइसेंस समाप्त होने के बाद अपने लाइसेंस का नवीनीकरण 1 महीने के भीतर करते हैं, तो कुल लाइसेंस मूल्य पर% 20 छूट लागू होगी।", - "WhenShouldIRenewMyLicenseExplanation2": "यदि आप अपनी लाइसेंस की समाप्ति तिथि से 1 महीने के बाद अपना लाइसेंस नवीनीकृत करते हैं, तो नवीनीकरण मूल्य लाइसेंस खरीद मूल्य के समान होगा और आपके नवीकरण के लिए कोई छूट नहीं होगी।", + "WhenShouldIRenewMyLicenseExplanation": "यदि आप अपने लाइसेंस की समय सीमा समाप्त होने के बाद 1 महीने के भीतर अपना लाइसेंस नवीनीकृत करते हैं, तो निम्नलिखित छूटें लागू होंगी: टीम लाइसेंस {0}% छूट, व्यवसाय लाइसेंस {1}% छूट, एंटरप्राइज़ लाइसेंस {2}% छूट . यदि आप अपने लाइसेंस की समाप्ति तिथि के बाद 1 महीने अपने लाइसेंस का नवीनीकरण करते हैं, तो नवीनीकरण मूल्य लाइसेंस खरीद मूल्य के समान होगा और आपके नवीनीकरण पर कोई छूट नहीं होगी।", "TrialPlan": "क्या आपके पास एक परीक्षण योजना है?", "TrialPlanExplanation": "अभी के लिए, एबीपी कमर्शियल में ट्रायल प्लान नहीं है। टीम लाइसेंस के लिए हम 30 दिन की मनी बैक गारंटी प्रदान करते हैं। आप पहले 30 दिनों में धनवापसी का अनुरोध कर सकते हैं। बिजनेस और एंटरप्राइज लाइसेंस के लिए, हम 30 दिनों में 60% रिफंड प्रदान करते हैं। ऐसा इसलिए है क्योंकि व्यवसाय और एंटरप्राइज़ लाइसेंस में सभी मॉड्यूल और थीम का पूर्ण स्रोत कोड शामिल है।", "DoYouAcceptBankWireTransfer": "क्या आप बैंक वायर ट्रांसफर स्वीकार करते हैं?", - "DoYouAcceptBankWireTransferExplanation": "हां, हम बैंक वायर ट्रांसफर स्वीकार करते हैं।
बैंक वायर ट्रांसफर के माध्यम से लाइसेंस राशि भेजने के बाद, हमें अपनी रसीद और अनुरोधित लाइसेंस प्रकार ई-मेल के माध्यम से भेजें।
हमारे अंतरराष्ट्रीय बैंक खाते की जानकारी:", + "DoYouAcceptBankWireTransferExplanation": "हां, हम बैंक वायर ट्रांसफर स्वीकार करते हैं।
बैंक हस्तांतरण के माध्यम से लाइसेंस शुल्क भेजने के बाद, हमें अपनी रसीद और अनुरोधित लाइसेंस के प्रकार accounting@abp.io पर ईमेल करें। हमारे अंतरराष्ट्रीय बैंक खाते की जानकारी:", "HowToUpgrade": "नया संस्करण उपलब्ध होने पर मौजूदा एप्लिकेशन को कैसे अपग्रेड किया जाए?", "HowToUpgradeExplanation1": "जब आप ABP कमर्शियल का उपयोग करके एक नया एप्लिकेशन बनाते हैं, तो सभी मॉड्यूल और थीम को NuGet और NPM पैकेज के रूप में उपयोग किया जाता है। इसलिए, नया संस्करण उपलब्ध होने पर आप आसानी से संकुल को अपग्रेड कर सकते हैं।", "HowToUpgradeExplanation2": "मानक NuGet/NPM अपग्रेड के अलावा, ABP CLI एक अपडेट कमांड प्रदान करता है जो आपके समाधान में सभी ABP संबंधित पैकेजों को स्वचालित रूप से ढूंढता और अपग्रेड करता है।", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json index 7690296f9d..2dab77c1a9 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Mi a különbség a licenc kiterjesztése és a frissítés között?", "LicenseExtendUpgradeDiffExplanation": "Meghosszabbítás: A licenc meghosszabbításával/megújításával továbbra is prémium támogatást kap, valamint jelentős frissítéseket kap a modulokhoz és témákhoz. Emellett folytathatja új projektek létrehozását. És továbbra is használhatja az ABP Suite-ot, amely felgyorsítja a fejlesztést.
Frissítés: Licencének frissítésével magasabb licenccsomagra lép fel, amely további előnyöket biztosít. . Tekintse meg a licenc-összehasonlító táblázatot, hogy ellenőrizze a licenccsomagok közötti különbségeket.Másrészt, ha frissít, a licenc lejárati dátuma nem változik! >A licenc lejárati dátumának meghosszabbításához meg kell hosszabbítania az engedélyt.", "LicenseRenewalCost": "Mennyibe kerül a licenc megújítása 1 év után?", - "LicenseRenewalCostExplanation": "Az összes ABP Commercial örökös licenc megújítási (meghosszabbítási) aránya a licenc listaárának {0}-a. A normál csapatlicenc megújítási ára {1} USD, a normál üzleti licencé {2} USD, a normál vállalati licencé pedig {3} USD. Ha már ügyfele, jelentkezzen be fiókjába, hogy áttekintse az elérhető megújítási árakat.", + "LicenseRenewalCostExplanation": "A normál csapatlicenc megújítási (meghosszabbítási) ára {0} USD, a normál üzleti licencé {1} USD, a normál vállalati licencé pedig {2} USD. Ha Ön már ügyfél, jelentkezzen be fiókjába, hogy áttekintse az elérhető megújítási árakat.", "HowDoIRenewMyLicense": "Hogyan újíthatom meg a jogosítványomat?", "HowDoIRenewMyLicenseExplanation": "Licencét megújíthatja, ha felkeresi a szervezetkezelési oldalt. Ha szeretné kihasználni a kedvezményes korai megújítási díjainkat, ne felejtse el megújítani a licence lejárta előtt. Ne aggódjon amiatt, hogy nem tudja, mikor zárul be a korai megújítási lehetősége. Előfizetése lejárta előtt 3 emlékeztető e-mailt fog kapni. 30 nappal, 7 nappal és 1 nappal a lejárat előtt küldjük el.", "IsSourceCodeIncluded": "Tartalmazza a licencem a kereskedelmi modulok és témák forráskódját?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Módosíthatom a szervezetem regisztrált fejlesztőit a jövőben?", "ChangingDevelopersExplanation": "Amellett, hogy új fejlesztőket ad hozzá a licenchez, további költségek nélkül módosíthatja a meglévő fejlesztőket is (eltávolíthat egy fejlesztőt, és hozzáadhat egy újat ugyanarra a helyre).", "WhatHappensWhenLicenseEnds": "Mi történik, ha lejár a jogosítványom?", - "WhatHappensWhenLicenseEndsExplanation1": "Az ABP Kereskedelmi licenc típusa örök licenc. A licenc lejárta után folytathatja a projekt fejlesztését. És nem köteles megújítani az engedélyt. Licencéhez egyéves frissítési és támogatási csomag tartozik. Ha továbbra is új funkciókat, teljesítményfejlesztéseket, hibajavításokat, támogatást szeretne kapni, és továbbra is használni szeretné az ABP Suite szolgáltatást, minden évben újítsa meg tervét. Ha az engedélye lejár, az alábbi előnyök közül többet nem fog tudni igénybe venni;", + "WhatHappensWhenLicenseEndsExplanation1": "Az ABP Kereskedelmi licenc típusa örök licenc. A licenc lejárta után folytathatja a projekt fejlesztését. És nem köteles megújítani az engedélyt. Licencéhez egyéves frissítés és támogatási terv tartozik. Ha továbbra is új funkciókat, teljesítményjavításokat, hibajavításokat, támogatást szeretne kapni, és továbbra is használni szeretné az ABP Suite szolgáltatást, meg kell újítania licencét. Ha az engedélye lejár, nem részesül a következő előnyökben;", "WhatHappensWhenLicenseEndsExplanation2": "Az ABP Commercial használatával nem hozhat létre új megoldásokat, de örökké folytathatja meglévő alkalmazásai fejlesztését.", - "WhatHappensWhenLicenseEndsExplanation3": "Frissítéseket kaphat a MAJOR verziójában található modulokhoz és témákhoz. Például; ha egy modul v3.2.0-s verzióját használja, továbbra is kaphat frissítéseket a modul v3.x.x-hez (v3.3.0, v3.5.2... stb.). De nem kaphat frissítéseket a következő nagyobb verzióhoz (például v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "You will be able to get updates for the modules and themes within your MAJOR version (except RC or Preview versions). For example; if you are using v3.2.0 of a module, you can still get updates for v3.x.x (v3.3.0, v3.5.2... etc.) of that module. But you cannot get updates for the next major version (like v4.x, v5.x). For example, when your license expired, the latest release was v4.4.3 and the latest preview version was v5.0.0-rc.2, you can access the v4.X.X but you cannot access the v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "A licenc lejárta után nem telepíthet új modulokat és témákat az ABP Commercial platformhoz.", "WhatHappensWhenLicenseEndsExplanation5": "Az ABP Suite nem használható.", "WhatHappensWhenLicenseEndsExplanation6": "Többé nem veheti igénybe a prémium támogatást.", - "WhatHappensWhenLicenseEndsExplanation7": "Megújíthatja előfizetését, ha továbbra is igénybe kívánja venni ezeket az előnyöket. Ha a licence lejártát követő 1 hónapon belül megújítja előfizetését, 20% kedvezmény jár.", + "WhatHappensWhenLicenseEndsExplanation7": "Meghosszabbíthatja (megújíthatja) jogosítványát, ha továbbra is igénybe kívánja venni ezeket az előnyöket. Ha a licenc lejártát követő 1 hónapon belül meghosszabbítja licencét, a következő kedvezmények érvényesülnek: Csapatengedély: {0}% kedvezmény, Üzleti licenc {1}% kedvezmény, Vállalati licenc {2}% kedvezmény .", "WhenShouldIRenewMyLicense": "Mikor kell megújítanom a jogosítványomat?", - "WhenShouldIRenewMyLicenseExplanation1": "Ha a licenc lejártát követő 1 hónapon belül megújítja a licencet, 20 % kedvezményt adunk.", - "WhenShouldIRenewMyLicenseExplanation2": "Ha a licencet a licenc lejárati dátumától számított 1 hónap elteltével újítja meg, a megújítási ár megegyezik a licenc vételárával, és nem jár kedvezmény a megújításra.", + "WhenShouldIRenewMyLicenseExplanation": "Ha a licenc lejártát követő 1 hónapon belül megújítja a licencet, a következő kedvezmények érvényesek: Csapatlicenc {0}% kedvezmény, Üzleti licenc {1}% kedvezmény, Vállalati licenc {2}% kedvezmény . Ha megújítja a licencet 1 hónappal a licenc lejárati dátuma után, a megújítási ár megegyezik a licenc vásárlási árával, és nem jár kedvezmény a megújításra.", "TrialPlan": "Van próbaterv?", "TrialPlanExplanation": "Az ABP Commercial egyelőre nem rendelkezik próbatervvel. A Team licencekre 30 napos pénzvisszafizetési garanciát vállalunk. Az első 30 napban csak visszatérítést kérhet. A Business és Enterprise licencek esetén 60% visszatérítést biztosítunk 30 napon belül. Ennek az az oka, hogy a Business és Enterprise licencek tartalmazzák az összes modul és téma teljes forráskódját.", "DoYouAcceptBankWireTransfer": "Elfogadja a banki átutalást?", - "DoYouAcceptBankWireTransferExplanation": "Igen, elfogadunk banki átutalást.
Miután banki átutalással elküldte a licencdíjat, küldje el nekünk e-mailben az info@abp.io címre a nyugtát és a kért engedély típusát. Nemzetközi bankszámlánk információi:", + "DoYouAcceptBankWireTransferExplanation": "Igen, elfogadunk banki átutalást.
Miután banki átutalással elküldte a licencdíjat, küldje el nekünk e-mailben az accounting@abp.io címre nyugtát és a kért engedély típusát. Nemzetközi bankszámlánk információi:", "HowToUpgrade": "Hogyan lehet frissíteni a meglévő alkalmazásokat, ha új verzió érhető el?", "HowToUpgradeExplanation1": "Amikor új alkalmazást hoz létre az ABP Commercial használatával, az összes modul és a téma NuGet és NPM csomagként kerül felhasználásra. Így könnyen frissítheti a csomagokat, ha új verzió érhető el.", "HowToUpgradeExplanation2": "A szabványos NuGet/NPM frissítéseken kívül az ABP CLI frissítési parancsot biztosít, amely automatikusan megkeresi és frissíti az összes ABP-vel kapcsolatos csomagot a megoldásban.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json index cfe0771825..dcf91ee003 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Hver er munurinn á lengingu leyfis og uppfærslu?", "LicenseExtendUpgradeDiffExplanation": " Framlenging: Með því að framlengja/endurnýja leyfið þitt muntu halda áfram að fá aukagjaldstuðning og fá stórar uppfærslur fyrir einingarnar og þemu. Að auki muntu geta haldið áfram að búa til ný verkefni. Og þú munt samt geta notað ABP Suite sem flýtir fyrir þróun þinni.
Uppfærsla: Með því að uppfæra leyfið þitt muntu kynna þér hærri leyfisáætlun sem gerir þér kleift að fá frekari ávinning . Skoðaðu tafla til samanburðar leyfa til að athuga muninn á leyfisáætlunum. Á hinn bóginn, þegar þú uppfærir mun gildistími leyfis þíns ekki breytast! < /strong> Til að framlengja lokadagsetningu leyfis þíns þarftu að framlengja leyfið.", "LicenseRenewalCost": "Hver er kostnaður við endurnýjun leyfis eftir 1 ár?", - "LicenseRenewalCostExplanation": "Endurnýjun (framlenging) hlutfall allra ABP Commercial leyfa er {0} af leyfislistaverði. Endurnýjunarverð staðlaða teymisleyfis er $ {1}, venjulegt viðskiptaleyfi er $ {2} og venjulegt fyrirtækjaleyfi er $ {3}. Ef þú ert þegar viðskiptavinur, skráðu þig inn á reikninginn þinn til að fara yfir tiltæk endurnýjunarverð.", + "LicenseRenewalCostExplanation": "Endurnýjunarverð (framlengja) venjulegs liðsleyfis er ${0}, venjulegt viðskiptaleyfi er ${1} og staðlað fyrirtækisleyfi er ${2}. Ef þú ert nú þegar viðskiptavinur skaltu skrá þig inn á reikninginn þinn til að skoða tiltæk endurnýjunarverð.", "HowDoIRenewMyLicense": "Hvernig endurnýi ég leyfi mitt?", "HowDoIRenewMyLicenseExplanation": "Þú getur endurnýjað leyfið með því að fara á stjórnunarsíðu fyrirtækisins . Til að nýta afsláttarverð okkar fyrir snemma endurnýjun, vertu viss um að endurnýja áður en leyfið þitt rennur út. Ekki hafa áhyggjur af því að vita ekki hvenær tækifæri þínu til endurnýjunar lokast. Þú færð 2 áminningarpósta áður en áskriftin rennur út. Við sendum þau 30 dögum, 7 dögum fyrir lok.", "IsSourceCodeIncluded": "Inniheldur leyfi mitt frumkóða viðskiptareininganna og þemanna?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Get ég breytt skráðum forriturum fyrirtækisins míns í framtíðinni?", "ChangingDevelopersExplanation": "Til viðbótar við að bæta nýjum verktaka við leyfi þitt geturðu einnig breytt núverandi forriturum (þú getur fjarlægt verktaka og bætt nýjum við) án aukakostnaðar.", "WhatHappensWhenLicenseEnds": "Hvað gerist þegar leyfistímabilinu lýkur?", - "WhatHappensWhenLicenseEndsExplanation1": "ABP Commercial er eilíft leyfi . Eftir að leyfi þitt rennur út geturðu haldið áfram að þróa verkefnið þitt. Og þér er ekki skylt að endurnýja leyfið. Leyfi þínu fylgir eins árs uppfærslur og stuðningsáætlun úr kassanum. Til að halda áfram að fá nýja eiginleika, endurbætur á afköstum, villuleiðréttingar, stuðning og halda áfram að nota ABP Suite, vertu viss um að endurnýja áætlun þína á hverju ári. Þegar leyfi þitt rennur út muntu ekki geta fengið fleiri af eftirfarandi ávinningi;", + "WhatHappensWhenLicenseEndsExplanation1": "ABP Commercial leyfistegund er ælíft leyfi. Eftir að leyfið þitt rennur út geturðu haldið áfram að þróa verkefnið þitt. Og þú ert ekki skyldugur til að endurnýja leyfið þitt. Leyfinu þínu fylgir eins árs uppfærsla og stuðningsáætlun beint úr kassanum. Til að halda áfram að fá nýja eiginleika, auka afköst, villuleiðréttingar, stuðning og halda áfram að nota ABP Suite þarftu að endurnýja leyfið þitt. Þegar leyfið þitt rennur út færðu ekki eftirfarandi fríðindi;", "WhatHappensWhenLicenseEndsExplanation2": "Þú getur ekki búið til nýjar lausnir með því að nota ABP Commercial, en þú getur haldið áfram að þróa núverandi forrit þín að eilífu.", - "WhatHappensWhenLicenseEndsExplanation3": "Þú munt geta fengið uppfærslur fyrir einingar og þemu í MAJOR útgáfunni þinni. Til dæmis; ef þú ert að nota v3.2.0 af einingu geturðu samt fengið uppfærslur fyrir v3.x.x (v3.3.0, v3.5.2 ... osfrv.) þeirrar einingar. En þú getur ekki fengið uppfærslur fyrir næstu stóru útgáfuna (eins og v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "Þú munt geta fengið uppfærslur fyrir einingarnar og þemu í MAJOR útgáfunni þinni (nema RC eða Preview útgáfur). Til dæmis; ef þú ert að nota v3.2.0 af einingu geturðu samt fengið uppfærslur fyrir v3.x.x (v3.3.0, v3.5.2... osfrv.) af þeirri einingu. En þú getur ekki fengið uppfærslur fyrir næstu helstu útgáfu (eins og v4.x, v5.x). Til dæmis, þegar leyfið þitt rann út, nýjasta útgáfan var v4.4.3 og nýjasta forskoðunarútgáfan var v5.0.0-rc.2, þú getur fengið aðgang að v4.X.X en þú getur ekki fengið aðgang að v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Þú getur ekki sett upp nýjar einingar og þemu sem bætt er við ABP Commercial eftir að leyfi þínu lýkur.", "WhatHappensWhenLicenseEndsExplanation5": "Þú getur ekki notað ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "Þú getur ekki fengið úrvalsstuðninginn lengur.", - "WhatHappensWhenLicenseEndsExplanation7": "Þú getur endurnýjað áskriftina þína ef þú vilt halda áfram að fá þessa kosti. Ef þú endurnýjar áskriftina innan 1 mánaðar eftir að leyfi þitt rennur út, er 20% afsláttur veittur.", + "WhatHappensWhenLicenseEndsExplanation7": "Þú getur framlengt (endurnýjað) leyfið þitt ef þú vilt halda áfram að njóta þessara fríðinda. Ef þú framlengir leyfið þitt innan eins mánaðar eftir að leyfið þitt rennur út verða eftirfarandi afslættir notaðir: Team Leyfi {0}% afsláttur, Business License {1}% afsláttur, Enterprise License {2}% afsláttur .", "WhenShouldIRenewMyLicense": "Hvenær ætti ég að endurnýja leyfið mitt?", - "WhenShouldIRenewMyLicenseExplanation1": "Ef þú endurnýjar leyfið þitt innan 1 mánaðar eftir að leyfið þitt rennur út, er notaður 20 %afsláttur.", - "WhenShouldIRenewMyLicenseExplanation2": "Ef þú endurnýjar leyfið þitt eftir 1 mánuð frá því að leyfi þitt rennur út, þá verður endurnýjunarverð það sama og kaupverð leyfis og enginn afsláttur verður af endurnýjun þinni.", + "WhenShouldIRenewMyLicenseExplanation": "Ef þú endurnýjar leyfið þitt innan eins mánaðar eftir að leyfið þitt rennur út verða eftirfarandi afslættir notaðir: Team Leyfi {0}% afsláttur, Business License {1}% afsláttur, Enterprise License {2}% afsláttur . Ef þú endurnýjar leyfið þitt 1 mánuði eftir að leyfið rennur út, verður endurnýjunarverðið það sama og kaupverð leyfisins og enginn afsláttur af endurnýjun þinni.", "TrialPlan": "Ertu með prufuáætlun?", "TrialPlanExplanation": "Í bili hefur ABP Commercial ekki prufuáætlun. Fyrir teymis leyfi veitum við 30 daga endurgreiðsluábyrgð. Þú getur bara óskað eftir endurgreiðslu fyrstu 30 dagana. Viðskipta- og fyrirtækjalyfið veitir 60% endurgreiðslu á 30 dögum. Þetta er vegna þess að viðskipta- og fyrirtækjaleyfi innihalda fullan kóða allra eininga og þemanna.", "DoYouAcceptBankWireTransfer": "Samþykki þið bankamillifærslu?", - "DoYouAcceptBankWireTransferExplanation": "Já, við samþykkjum bankamillifærslu.
Eftir að þú hefur sent leyfisgjaldið með millifærslu, sendu okkur kvittunina þína í tölvupósti á info@abp.io með tegund leyfis sem óskað er eftir. Alþjóðlegar bankareikningsupplýsingar okkar:", + "DoYouAcceptBankWireTransferExplanation": "Já, við tökum við bankamillifærslu.
Eftir að hafa sent leyfisgjaldið með millifærslu skaltu senda okkur tölvupóst á accounting@abp.io kvittun þína og tegund leyfis sem óskað er eftir. Upplýsingar um alþjóðlega bankareikninginn okkar:", "HowToUpgrade": "Hvernig á að uppfæra núverandi forrit þegar ný útgáfa er í boði?", "HowToUpgradeExplanation1": "Þegar þú býrð til nýtt forrit með ABP Commercial eru allar einingarnar og þemað notað sem NuGet og NPM pakkar. Svo þú getur auðveldlega uppfært pakkana þegar ný útgáfa er fáanleg.", "HowToUpgradeExplanation2": "Til viðbótar við hefðbundna NuGet/NPM uppfærslu veitir ABP CLI uppfærsluskipun sem finnur og uppfærir sjálfkrafa alla ABP tengda pakka í lausninni þinni.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json index 06f54e48ec..66f59455b1 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Qual è la differenza tra estensione della licenza e aggiornamento?", "LicenseExtendUpgradeDiffExplanation": " Estensione: estendendo/rinnovando la tua licenza, continuerai a ricevere supporto premium e a ricevere aggiornamenti importanti per moduli e temi. Inoltre, potrai continuare a creare nuovi progetti. E sarai ancora in grado di utilizzare ABP Suite che accelera il tuo sviluppo.
Upgrade: aggiornando la tua licenza, promuoverai un piano di licenza superiore che ti consentirà di ottenere ulteriori vantaggi . Consulta la tabella di confronto delle licenze per verificare le differenze tra i piani di licenza. D'altra parte, quando esegui l'upgrade, la data di scadenza della licenza non cambierà! Per estendere la data di fine della licenza, è necessario estendere la licenza.", "LicenseRenewalCost": "Qual è il costo del rinnovo della licenza dopo 1 anno?", - "LicenseRenewalCostExplanation": "La tariffa di rinnovo (estensione) di tutte le licenze perpetue ABP Commercial è {0} del prezzo di listino della licenza. Il prezzo di rinnovo della licenza Team standard è $ {1}, la licenza Business standard è $ {2} e la licenza Enterprise standard è $ {3}. Se sei già un cliente, accedi al tuo account per esaminare i prezzi di rinnovo disponibili.", + "LicenseRenewalCostExplanation": "Il prezzo di rinnovo (estensione) della Licenza Team standard è ${0}, la Licenza Business standard è ${1} e la Licenza Enterprise standard è ${2}. Se sei già un cliente, accedi al tuo account per rivedere i prezzi di rinnovo disponibili.", "HowDoIRenewMyLicense": "Come rinnovo la mia licenza?", "HowDoIRenewMyLicenseExplanation": "Puoi rinnovare la licenza accedendo alla pagina di gestione dell'organizzazione . Per poter usufruire delle nostre tariffe scontate di rinnovo anticipato, assicurati di rinnovare prima della scadenza della licenza. Tuttavia, non preoccuparti di non sapere quando si chiude la tua opportunità di rinnovo anticipato. Riceverai 2 e-mail di promemoria prima della scadenza dell'abbonamento. Li invieremo a 30 giorni, 7 giorni prima della scadenza.", "IsSourceCodeIncluded": "La mia licenza include il codice sorgente dei moduli e dei temi commerciali?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Posso cambiare gli sviluppatori registrati della mia organizzazione in futuro?", "ChangingDevelopersExplanation": "Oltre ad aggiungere nuovi sviluppatori alla tua licenza, puoi anche Modifica gli sviluppatori esistenti (puoi rimuovere uno sviluppatore e aggiungerne uno nuovo alla stessa postazione) senza alcun costo aggiuntivo.", "WhatHappensWhenLicenseEnds": "Cosa succede al termine del periodo di licenza?", - "WhatHappensWhenLicenseEndsExplanation1": "Il tipo di licenza ABP Commercial è licenza perpetua . Dopo la scadenza della licenza, puoi continuare a sviluppare il tuo progetto. E non sei obbligato a rinnovare la tua licenza. La tua licenza viene fornita con un anno di aggiornamenti e piano di supporto pronto all'uso. Per continuare a ricevere nuove funzionalità, miglioramenti delle prestazioni, correzioni di bug, supporto e continuare a utilizzare ABP Suite, assicurati di rinnovare il tuo piano ogni anno. Quando la tua licenza scade, non sarai in grado di ottenere più dei seguenti vantaggi;", + "WhatHappensWhenLicenseEndsExplanation1": "Il tipo di licenza commerciale ABP è licenza perpetua. Dopo la scadenza della licenza, puoi continuare a sviluppare il tuo progetto. E non sei obbligato a rinnovare la patente. La tua licenza viene fornita con un aggiornamento di un anno e un piano di supporto pronti all'uso. Per continuare a ottenere nuove funzionalità, miglioramenti delle prestazioni, correzioni di bug, supporto e continuare a utilizzare ABP Suite, è necessario rinnovare la licenza. Alla scadenza della licenza, non otterrai i seguenti vantaggi;", "WhatHappensWhenLicenseEndsExplanation2": "Non è possibile creare nuove soluzioni utilizzando ABP Commercial, ma è possibile continuare a sviluppare le applicazioni esistenti per sempre.", - "WhatHappensWhenLicenseEndsExplanation3": "Sarai in grado di ottenere aggiornamenti per i moduli e i temi nella tua versione MAJOR. Per esempio; se stai usando la v3.2.0 di un modulo, puoi comunque ottenere gli aggiornamenti per la v3.x.x (v3.3.0, v3.5.2... ecc.) di quel modulo. Ma non puoi ottenere aggiornamenti per la prossima versione principale (come v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "Sarai in grado di ottenere aggiornamenti per i moduli e i temi all'interno della tua versione MAJOR (eccetto le versioni RC o Preview). Per esempio; se stai usando la v3.2.0 di un modulo, puoi comunque ottenere gli aggiornamenti per la v3.x.x (v3.3.0, v3.5.2... ecc.) di quel modulo. Ma non puoi ottenere aggiornamenti per la prossima versione principale (come v4.x, v5.x). Ad esempio, quando la tua licenza è scaduta, l'ultima versione era v4.4.3 e l'ultima versione di anteprima era v5.0.0-rc.2, puoi accedere alla v4.X.X ma non alla v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Non è possibile installare nuovi moduli e temi aggiunti alla piattaforma ABP Commercial al termine della licenza.", "WhatHappensWhenLicenseEndsExplanation5": "Non è possibile utilizzare ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "Non puoi più ottenere l ' assistenza premium .", - "WhatHappensWhenLicenseEndsExplanation7": "Puoi rinnovare l'abbonamento se desideri continuare a usufruire di questi vantaggi. C'è uno sconto del 20% quando rinnovi l'abbonamento.", + "WhatHappensWhenLicenseEndsExplanation7": "Puoi estendere (rinnovare) la tua licenza se vuoi continuare a ottenere questi vantaggi. Se estendi la tua licenza entro 1 mese dopo la scadenza della licenza, verranno applicati i seguenti sconti: Licenza Team {0}% di sconto, Licenza Business {1}% di sconto, Licenza Enterprise {2}% di sconto .", "WhenShouldIRenewMyLicense": "Quando devo rinnovare la mia licenza?", - "WhenShouldIRenewMyLicenseExplanation1": "Se rinnovi la licenza entro 1 mese dalla scadenza della licenza, verrà applicato uno sconto del 20% sul prezzo totale della licenza.", - "WhenShouldIRenewMyLicenseExplanation2": "Se rinnovi la licenza dopo 1 mese dalla data di scadenza della licenza, il prezzo di rinnovo sarà lo stesso del prezzo di acquisto della licenza e non ci saranno sconti per il rinnovo.", + "WhenShouldIRenewMyLicenseExplanation": "Se rinnovi la licenza entro 1 mese dopo la scadenza della licenza, verranno applicati i seguenti sconti: Licenza Team {0}% di sconto, Licenza Business {1}% di sconto, Licenza Enterprise {2}% di sconto . Se rinnovi la licenza 1 mese dopo la data di scadenza della licenza, il prezzo di rinnovo sarà lo stesso del prezzo di acquisto della licenza e non ci saranno sconti sul rinnovo.", "TrialPlan": "Hai un piano di prova?", "TrialPlanExplanation": "Per ora, ABP Commercial non ha un piano di prova. Per le licenze Team forniamo una garanzia di rimborso di 30 giorni. Puoi richiedere un rimborso solo nei primi 30 giorni. Per le licenze Business ed Enterprise, forniamo un rimborso del 60% in 30 giorni. Questo perché le licenze Business ed Enterprise includono il codice sorgente completo di tutti i moduli e i temi.", "DoYouAcceptBankWireTransfer": "Accettate bonifici bancari?", - "DoYouAcceptBankWireTransferExplanation": "Sì, accettiamo bonifico bancario.
Dopo aver inviato l'importo della licenza tramite bonifico bancario, inviaci la ricevuta e il tipo di licenza richiesto tramite e-mail.
Informazioni sul nostro conto bancario internazionale:", + "DoYouAcceptBankWireTransferExplanation": "Sì, accettiamo bonifico bancario.
Dopo aver inviato il canone tramite bonifico bancario, inviaci un'e-mail a accounting@abp.io con la ricevuta e il tipo di licenza richiesta. Le nostre informazioni sul conto bancario internazionale:", "HowToUpgrade": "Come aggiornare le applicazioni esistenti quando è disponibile una nuova versione?", "HowToUpgradeExplanation1": "Quando crei una nuova applicazione utilizzando ABP Commercial, tutti i moduli e il tema vengono utilizzati come pacchetti NuGet e NPM. Quindi, puoi facilmente aggiornare i pacchetti quando è disponibile una nuova versione.", "HowToUpgradeExplanation2": "Oltre agli aggiornamenti NuGet/NPM standard, ABP CLI fornisce un comando di aggiornamento che trova e aggiorna automaticamente tutti i pacchetti relativi a ABP nella soluzione.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json index a1ffca1de0..cf37db5c8e 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Wat is het verschil tussen licentieverlenging en upgrade?", "LicenseExtendUpgradeDiffExplanation": "Verlengen: door uw licentie te verlengen/vernieuwen, blijft u premium ondersteuning krijgen en krijgt u belangrijke updates voor de modules en thema's. Bovendien kunt u doorgaan met het maken van nieuwe projecten. En u kunt nog steeds ABP Suite gebruiken, wat uw ontwikkeling versnelt.
Upgraden: door uw licentie te upgraden, promoveert u naar een hoger licentieplan waarmee u extra voordelen kunt krijgen . Bekijk de licentievergelijkingstabel om de verschillen tussen de licentieplannen te controleren.Aan de andere kant, wanneer u een upgrade uitvoert, verandert de vervaldatum van uw licentie niet!Om de einddatum van uw licentie te verlengen, moet u uw licentie verlengen.", "LicenseRenewalCost": "Wat zijn de kosten voor het verlengen van de licentie na 1 jaar?", - "LicenseRenewalCostExplanation": "Het tarief voor verlenging (verlenging) van alle eeuwigdurende licenties van ABP Commercial is {0} van de catalogusprijs van de licentie. De verlengingsprijs van de standaard Teamlicentie is ${1}, de standaard Business-licentie is ${2} en de standaard Enterprise-licentie is ${3}. Als u al klant bent, log in op uw account om de beschikbare verlengingsprijzen te bekijken.", + "LicenseRenewalCostExplanation": "Het tarief voor catalogusverlenging van alle eeuwigdurende licenties van ABP Commercial is {0} van de licentieprijs van de licentie. De verlengingsprijs van de standaard Teamlicentie is ${1}, de standaard Business-licentie is ${2} en de standaard Enterprise-licentie is ${3}. Als u al klant bent, log in op uw account om de beschikbare verlengingsprijzen te bekijken.", "HowDoIRenewMyLicense": "Hoe verleng ik mijn licentie?", "HowDoIRenewMyLicenseExplanation": "U kunt uw licentie verlengen door naar de organisatiebeheerpagina te gaan. Om te profiteren van onze gereduceerde tarieven voor vroege verlenging, moet u ervoor zorgen dat u verlengt voordat uw licentie verloopt. U hoeft zich echter geen zorgen te maken dat u niet weet wanneer uw mogelijkheid tot vervroegde verlenging afloopt. Je ontvangt 3 herinneringsmails voordat je abonnement afloopt. We sturen ze 30 dagen, 7 dagen en 1 dag voor de vervaldatum.", "IsSourceCodeIncluded": "Bevat mijn licentie de broncode van de commerciële modules en thema's?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Kan ik de geregistreerde ontwikkelaars van mijn organisatie in de toekomst wijzigen?", "ChangingDevelopersExplanation": "Naast het toevoegen van nieuwe ontwikkelaars aan uw licentie, kunt u ook de bestaande ontwikkelaars wijzigen (u kunt een ontwikkelaar verwijderen en een nieuwe toevoegen aan dezelfde stoel) zonder extra kosten.", "WhatHappensWhenLicenseEnds": "Wat gebeurt er als mijn licentieperiode afloopt?", - "WhatHappensWhenLicenseEndsExplanation1": "Het commerciële ABP-licentietype is eeuwigdurende licentie. Nadat uw licentie is verlopen, kunt u doorgaan met het ontwikkelen van uw project. En je bent niet verplicht om je rijbewijs te verlengen. Uw licentie wordt standaard geleverd met een jaarabonnement voor updates en ondersteuning. Om nieuwe functies, prestatieverbeteringen, bugfixes en ondersteuning te blijven ontvangen en ABP Suite te blijven gebruiken, moet u uw abonnement elk jaar verlengen. Wanneer uw licentie verloopt, kunt u niet meer van de volgende voordelen krijgen;", + "WhatHappensWhenLicenseEndsExplanation1": "Het commerciële ABP-licentietype is permanente licentie. Nadat uw licentie is verlopen, kunt u doorgaan met het ontwikkelen van uw project. En je bent niet verplicht om je rijbewijs te verlengen. Uw licentie wordt standaard geleverd met een jaarupdate en ondersteuningsplan. Om nieuwe functies, prestatieverbeteringen, bugfixes, ondersteuning te blijven krijgen en ABP Suite te blijven gebruiken, moet u uw licentie verlengen. Wanneer uw licentie verloopt, krijgt u niet de volgende voordelen;", "WhatHappensWhenLicenseEndsExplanation2": "Met de ABP Commercial kunt u geen nieuwe oplossingen creëren, maar u kunt wel eeuwig doorgaan met het doorontwikkelen van uw bestaande applicaties.", - "WhatHappensWhenLicenseEndsExplanation3": "U kunt updates krijgen voor de modules en thema's binnen uw MAJOR-versie. Bijvoorbeeld; als je v3.2.0 van een module gebruikt, kun je nog steeds updates krijgen voor v3.x.x (v3.3.0, v3.5.2... etc.) van die module. Maar u kunt geen updates krijgen voor de volgende hoofdversie (zoals v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "U kunt updates krijgen voor de modules en thema's binnen uw MAJOR-versie (behalve RC- of Preview-versies). Bijvoorbeeld; als je v3.2.0 van een module gebruikt, kun je nog steeds updates krijgen voor v3.x.x (v3.3.0, v3.5.2... etc.) van die module. Maar u kunt geen updates krijgen voor de volgende hoofdversie (zoals v4.x, v5.x). Toen uw licentie bijvoorbeeld verliep, was de nieuwste release v4.4.3 en de nieuwste preview-versie v5.0.0-rc.2, hebt u toegang tot v4.X.X, maar hebt u geen toegang tot v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "U kunt geen nieuwe modules en thema's installeren die zijn toegevoegd aan het ABP Commercieel platform nadat uw licentie is afgelopen.", "WhatHappensWhenLicenseEndsExplanation5": "U kunt geen gebruik maken van de ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "U kunt geen premium ondersteuning meer krijgen.", - "WhatHappensWhenLicenseEndsExplanation7": "U kunt uw abonnement verlengen als u van deze voordelen wilt blijven genieten. Als u uw abonnement verlengt binnen 1 maand nadat uw licentie is verlopen, wordt 20% korting toegepast.", + "WhatHappensWhenLicenseEndsExplanation7": "U kunt uw licentie verlengen (vernieuwen) als u van deze voordelen wilt blijven genieten. Als u uw licentie verlengt binnen 1 maand nadat uw licentie is verlopen, worden de volgende kortingen toegepast: Teamlicentie {0}% korting, Zakelijke licentie {1}% korting, Enterprise-licentie {2}% korting .", "WhenShouldIRenewMyLicense": "Wanneer moet ik mijn licentie verlengen?", - "WhenShouldIRenewMyLicenseExplanation1": "Als u uw licentie verlengt binnen 1 maand nadat uw licentie is verlopen, wordt een korting van %20 toegepast.", - "WhenShouldIRenewMyLicenseExplanation2": "Als u uw licentie verlengt na 1 maand vanaf de vervaldatum van uw licentie, is de verlengingsprijs hetzelfde als de aankoopprijs van de licentie en is er geen korting voor uw verlenging.", + "WhenShouldIRenewMyLicenseExplanation": "Als u uw licentie verlengt binnen 1 maand nadat uw licentie is verlopen, worden de volgende kortingen toegepast: Teamlicentie {0}% korting, Zakelijke licentie {1}% korting, Enterprise-licentie {2}% korting . Als u uw licentie 1 maand na de vervaldatum van uw licentie verlengt, is de verlengingsprijs gelijk aan de aankoopprijs van de licentie en wordt er geen korting op uw verlenging gegeven.", "TrialPlan": "Heb je een proefplan?", "TrialPlanExplanation": "Voorlopig heeft ABP Commercial geen proefabonnement. Voor de Team-licenties bieden we 30 dagen geld-terug-garantie. U kunt de eerste 30 dagen gewoon een terugbetaling aanvragen. Voor de Business- en Enterprise-licenties bieden we 60% restitutie in 30 dagen. Dit komt doordat Business en Enterprise licenties de volledige broncode van alle modules en thema's bevatten.", "DoYouAcceptBankWireTransfer": "Accepteert u bankoverschrijvingen?", - "DoYouAcceptBankWireTransferExplanation": "Ja, we accepteren bankoverschrijvingen.
Nadat u de licentievergoeding via bankoverschrijving heeft verzonden, stuurt u een e-mail naar info@abp.io met uw betalingsbewijs en het type licentie dat u heeft aangevraagd. Onze internationale bankrekeninggegevens:", + "DoYouAcceptBankWireTransferExplanation": "Ja, we accepteren bankoverschrijvingen.
Nadat u de licentievergoeding via bankoverschrijving heeft verzonden, stuurt u een e-mail naar accounting@abp.io met uw kwitantie en het type licentie dat u heeft aangevraagd. Onze internationale bankrekeninggegevens:", "HowToUpgrade": "Hoe kan ik bestaande applicaties upgraden wanneer er een nieuwe versie beschikbaar is?", "HowToUpgradeExplanation1": "Wanneer je een nieuwe applicatie maakt met ABP Commercial, worden alle modules en het thema gebruikt als NuGet- en NPM-pakketten. U kunt de pakketten dus eenvoudig upgraden wanneer er een nieuwe versie beschikbaar is.", "HowToUpgradeExplanation2": "Naast de standaard NuGet/NPM-upgrades, biedt ABP CLI een update-opdracht die automatisch alle ABP-gerelateerde pakketten in uw oplossing vindt en opwaardeert.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json index e13dcd1e30..b4751c02c0 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Jaka jest różnica między rozszerzeniem licencji a aktualizacją?", "LicenseExtendUpgradeDiffExplanation": "Przedłużanie: przedłużając/odnawiając licencję, będziesz nadal otrzymywać wsparcie premium i ważne aktualizacje modułów i motywów. Poza tym będziesz mógł kontynuować tworzenie nowych projektów. I nadal będziesz mógł korzystać z pakietu ABP Suite, który przyspieszy Twój rozwój.
Aktualizacja: Uaktualniając swoją licencję, awansujesz do wyższego planu licencyjnego, który pozwoli Ci uzyskać dodatkowe korzyści . Zobacz tablicę porównawczą licencji, aby sprawdzić różnice między planami licencyjnymi.Z drugiej strony po uaktualnieniu data wygaśnięcia licencji nie ulegnie zmianie!Aby przedłużyć datę zakończenia licencji, musisz przedłużyć swoją licencję.", "LicenseRenewalCost": "Jaki jest koszt odnowienia licencji po 1 roku?", - "LicenseRenewalCostExplanation": "Stawka odnawiania (przedłużania) wszystkich komercyjnych licencji wieczystych ABP wynosi {0} ceny katalogowej licencji. Cena odnowienia standardowej licencji zespołowej wynosi {1} USD, standardowej licencji biznesowej {2} USD, a standardowej Licencji Enterprise {3} USD. Jeśli jesteś już klientem, zaloguj się na swoje konto, aby sprawdzić dostępne ceny odnowienia.", + "LicenseRenewalCostExplanation": "Cena odnowienia (przedłużenia) standardowej licencji zespołowej to {0}$, standardowej licencji biznesowej to {1}$, a standardowej licencji Enterprise to {2}$. Jeśli jesteś już klientem, zaloguj się na swoje konto, aby sprawdzić dostępne ceny odnowienia.", "HowDoIRenewMyLicense": "Jak odnowić licencję?", "HowDoIRenewMyLicenseExplanation": "Możesz odnowić licencję, przechodząc na stronę zarządzania organizacją. Aby skorzystać z naszych obniżonych stawek za wcześniejsze odnowienie, pamiętaj o odnowieniu przed wygaśnięciem licencji. Nie martw się jednak, że nie wiesz, kiedy kończy się Twoja szansa na wczesne odnowienie. Otrzymasz 3 e-maile z przypomnieniem przed wygaśnięciem subskrypcji. Wyślemy je na 30 dni, 7 dni i 1 dzień przed wygaśnięciem.", "IsSourceCodeIncluded": "Czy moja licencja zawiera kod źródłowy modułów i motywów komercyjnych?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Czy mogę w przyszłości zmienić zarejestrowanych programistów mojej organizacji?", "ChangingDevelopersExplanation": "Oprócz dodawania nowych programistów do swojej licencji możesz również zmieniać istniejących programistów (możesz usunąć programistę i dodać nowego do tego samego stanowiska) bez żadnych dodatkowych kosztów.", "WhatHappensWhenLicenseEnds": "Co się stanie, gdy skończy się mój okres licencji?", - "WhatHappensWhenLicenseEndsExplanation1": "Typ licencji komercyjnej ABP to licencja wieczysta. Po wygaśnięciu licencji możesz kontynuować rozwijanie swojego projektu. I nie musisz odnawiać swojej licencji. Twoja licencja jest dostarczana z rocznym planem aktualizacji i pomocy technicznej zaraz po wyjęciu z pudełka. Aby nadal otrzymywać nowe funkcje, ulepszenia wydajności, poprawki błędów, wsparcie i nadal korzystać z pakietu ABP Suite, pamiętaj o odnawianiu swojego planu co roku. Po wygaśnięciu licencji nie będziesz mógł uzyskać więcej z następujących korzyści;", + "WhatHappensWhenLicenseEndsExplanation1": "Typ licencji komercyjnej ABP to licencja wieczysta. Po wygaśnięciu licencji możesz kontynuować rozwijanie swojego projektu. I nie musisz odnawiać swojej licencji. Twoja licencja jest dostarczana z roczną aktualizacją i planem wsparcia po wyjęciu z pudełka. Aby nadal otrzymywać nowe funkcje, ulepszenia wydajności, poprawki błędów, wsparcie i nadal korzystać z pakietu ABP Suite, musisz odnowić licencję. Po wygaśnięciu licencji nie uzyskasz następujących korzyści;", "WhatHappensWhenLicenseEndsExplanation2": "Nie możesz tworzyć nowych rozwiązań za pomocą ABP Commercial, ale możesz dalej rozwijać swoje istniejące aplikacje w nieskończoność.", - "WhatHappensWhenLicenseEndsExplanation3": "Będziesz mógł otrzymywać aktualizacje modułów i motywów w swojej GŁÓWNEJ wersji. Na przykład; jeśli używasz v3.2.0 modułu, nadal możesz otrzymywać aktualizacje dla v3.x.x (v3.3.0, v3.5.2... itd.) tego modułu. Ale nie możesz pobrać aktualizacji dla następnej głównej wersji (takiej jak v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "Będziesz mógł otrzymywać aktualizacje modułów i motywów w swojej GŁÓWNEJ wersji (z wyjątkiem wersji RC i Preview). Na przykład; jeśli używasz v3.2.0 modułu, nadal możesz otrzymywać aktualizacje dla v3.x.x (v3.3.0, v3.5.2... itd.) tego modułu. Ale nie możesz pobrać aktualizacji dla następnej głównej wersji (takiej jak v4.x, v5.x). Na przykład, gdy Twoja licencja wygasła, najnowsza wersja to 4.4.3, a najnowsza wersja zapoznawcza to 5.0.0-rc.2, możesz uzyskać dostęp do wersji 4.X.X, ale nie możesz uzyskać dostępu do wersji 5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Nie możesz instalować nowych modułów i motywów dodanych do platformy ABP Commercial po wygaśnięciu licencji.", "WhatHappensWhenLicenseEndsExplanation5": "Nie możesz korzystać z pakietu ABP.", "WhatHappensWhenLicenseEndsExplanation6": "Nie możesz już uzyskać pomocy premium.", - "WhatHappensWhenLicenseEndsExplanation7": "Możesz odnowić swoją subskrypcję, jeśli chcesz nadal korzystać z tych korzyści. Jeśli odnowisz subskrypcję w ciągu 1 miesiąca po wygaśnięciu licencji, zostanie zastosowana zniżka 20%.", + "WhatHappensWhenLicenseEndsExplanation7": "Możesz przedłużyć (odnowić) swoją licencję, jeśli chcesz nadal korzystać z tych korzyści. Jeśli przedłużysz licencję w ciągu 1 miesiąca po wygaśnięciu licencji, zostaną zastosowane następujące rabaty: Licencja zespołowa {0}% rabatu, Licencja biznesowa {1}% rabatu, Licencja Enterprise {2}% rabatu .", "WhenShouldIRenewMyLicense": "Kiedy powinienem odnowić licencję?", - "WhenShouldIRenewMyLicenseExplanation1": "Jeśli odnowisz licencję w ciągu 1 miesiąca po wygaśnięciu licencji, zostanie zastosowana zniżka w wysokości 20%.", - "WhenShouldIRenewMyLicenseExplanation2": "Jeśli odnowisz licencję po upływie 1 miesiąca od daty wygaśnięcia licencji, cena odnowienia będzie taka sama jak cena zakupu licencji i nie będzie rabatu za odnowienie.", + "WhenShouldIRenewMyLicenseExplanation": "Jeśli odnowisz licencję w ciągu 1 miesiąca po wygaśnięciu licencji, zostaną zastosowane następujące rabaty: licencja zespołowa {0}% rabatu, licencja biznesowa {1}% rabatu, licencja Enterprise {2}% rabatu . Jeśli odnowisz licencję 1 miesiąc po dacie wygaśnięcia licencji, cena odnowienia będzie taka sama jak cena zakupu licencji i nie będzie rabatu na odnowienie.", "TrialPlan": "Czy masz plan próbny?", "TrialPlanExplanation": "Na razie ABP Commercial nie ma planu próbnego. W przypadku licencji Team zapewniamy 30-dniową gwarancję zwrotu pieniędzy. Możesz po prostu poprosić o zwrot pieniędzy w ciągu pierwszych 30 dni. W przypadku licencji Business i Enterprise zapewniamy zwrot 60% w ciągu 30 dni. Dzieje się tak, ponieważ licencje Business i Enterprise zawierają pełny kod źródłowy wszystkich modułów i motywów.", "DoYouAcceptBankWireTransfer": "Czy akceptujesz przelew bankowy?", - "DoYouAcceptBankWireTransferExplanation": "Tak, akceptujemy przelew bankowy.
Po wysłaniu opłaty licencyjnej przelewem bankowym wyślij nam e-mail na adres info@abp.io z potwierdzeniem odbioru i rodzajem żądanej licencji. Informacje o naszych międzynarodowych kontach bankowych:", + "DoYouAcceptBankWireTransferExplanation": "Tak, akceptujemy przelew bankowy.
Po wysłaniu opłaty licencyjnej przelewem bankowym wyślij nam e-mail na adres accounting@abp.io z potwierdzeniem odbioru i rodzajem żądanej licencji. Informacje o naszych międzynarodowych kontach bankowych:", "HowToUpgrade": "Jak zaktualizować istniejące aplikacje, gdy dostępna jest nowa wersja?", "HowToUpgradeExplanation1": "Podczas tworzenia nowej aplikacji przy użyciu ABP Commercial wszystkie moduły i motyw są używane jako pakiety NuGet i NPM. Możesz więc łatwo zaktualizować pakiety, gdy dostępna będzie nowa wersja.", "HowToUpgradeExplanation2": "Oprócz standardowych uaktualnień NuGet/NPM ABP CLI udostępnia polecenie aktualizacji, które automatycznie wyszukuje i uaktualnia wszystkie pakiety powiązane z ABP w Twoim rozwiązaniu.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json index 2e1468cf4c..2ad5bd5a84 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Qual é a diferença entre extensão de licença e atualização?", "LicenseExtendUpgradeDiffExplanation": "Extending: By extending/renewing your license, you will continue to get premium support and get major updates for the modules and themes. Besides, you will be able to continue creating new projects. And you will still be able to use ABP Suite which speeds up your development.
Upgrading: By upgrading your license, you will promote to a higher license plan which will allow you to get additional benefits. See the license comparison table to check the differences between the license plans.On the other hand, when you upgrade, your license expiry date will not change!To extend your license end date, you need to extend your license.", "LicenseRenewalCost": "Qual é o custo de renovação da licença após 1 ano?", - "LicenseRenewalCostExplanation": "A taxa de renovação (extensão) de todas as licenças perpétuas da ABP Commercial é {0} do preço de lista da licença. O preço de renovação da Licença de equipe padrão é $ {1}, a Licença comercial padrão é $ {2} e a Licença corporativa padrão é $ {3}. Se você já é um cliente, faça login em sua conta para verificar os preços de renovação disponíveis.", + "LicenseRenewalCostExplanation": "O preço de renovação (extensão) da Licença de equipe padrão é $ {0}, a Licença comercial padrão é $ {1} e a Licença corporativa padrão é $ {2}. Se você já é um cliente, faça login em sua conta para verificar os preços de renovação disponíveis.", "HowDoIRenewMyLicense": "Como eu renovo minha licença?", "HowDoIRenewMyLicenseExplanation": "Você pode renovar sua licença navegando até a página de gerenciamento da organização . Para aproveitar nossas taxas de desconto de renovação antecipada, certifique-se de renovar antes que sua licença expire. Não se preocupe em não saber quando sua oportunidade de Renovação Antecipada terminará. Você receberá 3 e-mails de lembrete antes que sua assinatura expire. Iremos enviá-los 30 dias, 7 dias e 1 dia antes do vencimento.", "IsSourceCodeIncluded": "Minha licença inclui o código-fonte dos módulos e temas comerciais?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Posso mudar os desenvolvedores registrados da minha organização no futuro?", "ChangingDevelopersExplanation": "Além de adicionar novos desenvolvedores à sua licença, você também pode alterar os desenvolvedores existentes (você pode remover um desenvolvedor e adicionar um novo ao mesmo assento) sem nenhum custo adicional.", "WhatHappensWhenLicenseEnds": "O que acontece quando meu período de licença termina?", - "WhatHappensWhenLicenseEndsExplanation1": "O tipo de licença comercial ABP é licença perpétua . Depois que sua licença expirar, você pode continuar desenvolvendo seu projeto. E você não é obrigado a renovar sua licença. Sua licença vem com um plano de suporte e atualizações de um ano pronto para uso. Para continuar a receber novos recursos, melhorias de desempenho, correções de bugs, suporte e continuar a usar o ABP Suite, certifique-se de renovar seu plano a cada ano. Quando sua licença expirar, você não poderá obter mais dos seguintes benefícios;", + "WhatHappensWhenLicenseEndsExplanation1": "O tipo de licença comercial ABP é licença perpétua . Depois que sua licença expirar, você pode continuar desenvolvendo seu projeto. E você não é obrigado a renovar sua licença. Sua licença vem com um plano de atualização e suporte de um ano pronto para uso. Para continuar a obter novos recursos, melhorias de desempenho, correções de bugs, suporte e continuar a usar o ABP Suite, você precisa renovar sua licença. Quando sua licença expirar, você não obterá os seguintes benefícios;", "WhatHappensWhenLicenseEndsExplanation2": "Você não pode criar novas soluções usando o ABP Commercial, mas pode continuar a desenvolver seus aplicativos existentes para sempre.", - "WhatHappensWhenLicenseEndsExplanation3": "Você poderá obter atualizações para os módulos e temas em sua versão PRINCIPAL. Por exemplo; se estiver usando a v3.2.0 de um módulo, você ainda pode obter atualizações para v3.x.x (v3.3.0, v3.5.2 ... etc.) desse módulo. Mas você não pode obter atualizações para a próxima versão principal (como v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "Você poderá obter atualizações para os módulos e temas em sua versão MAJOR (exceto versões RC ou Preview). Por exemplo; se estiver usando a v3.2.0 de um módulo, você ainda pode obter atualizações para v3.x.x (v3.3.0, v3.5.2 ... etc.) desse módulo. Mas você não pode obter atualizações para a próxima versão principal (como v4.x, v5.x). Por exemplo, quando sua licença expirou, a versão mais recente foi a v4.4.3 e a versão de visualização mais recente foi a v5.0.0-rc.2, você pode acessar a v4.X.X, mas não pode acessar a v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Você não pode instalar novos módulos e temas adicionados à plataforma comercial da ABP após o término de sua licença.", "WhatHappensWhenLicenseEndsExplanation5": "Você não pode usar o ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "Você não pode mais obter o suporte premium .", - "WhatHappensWhenLicenseEndsExplanation7": "Você pode renovar sua assinatura se quiser continuar a obter esses benefícios. Se você renovar sua assinatura dentro de 1 mês após a expiração da licença, um desconto de 20% será aplicado.", + "WhatHappensWhenLicenseEndsExplanation7": "Você pode estender (renovar) sua licença se quiser continuar a obter esses benefícios. Se você estender sua licença em 1 mês após a expiração da licença, os seguintes descontos serão aplicados: Licença de equipe {0}% de desconto, Licença de negócios {1}% de desconto, Licença empresarial {2}% de desconto .", "WhenShouldIRenewMyLicense": "Quando devo renovar minha licença?", - "WhenShouldIRenewMyLicenseExplanation1": "Se você renovar sua licença em 1 mês após a expiração da licença, um desconto de 20% será aplicado.", - "WhenShouldIRenewMyLicenseExplanation2": "Se você renovar sua licença após 1 mês da data de expiração da licença, o preço de renovação será igual ao preço de compra da licença e não haverá desconto para sua renovação.", + "WhenShouldIRenewMyLicenseExplanation": "Se você renovar sua licença em 1 mês após a expiração da licença, os seguintes descontos serão aplicados: Licença de equipe {0}% de desconto, Licença de negócios {1}% de desconto, Licença empresarial {2}% de desconto . Se você renovar sua licença 1 mês após a data de expiração de sua licença, o preço de renovação será o mesmo que o preço de compra da licença e não haverá desconto em sua renovação.", "TrialPlan": "Você tem um plano experimental?", "TrialPlanExplanation": "Por enquanto, a ABP Commercial não tem um plano de teste. Para as licenças Team, oferecemos 30 dias de garantia de devolução do dinheiro. Você pode apenas solicitar um reembolso nos primeiros 30 dias. Para as licenças Business e Enterprise, oferecemos reembolso de 60% em 30 dias. Isso ocorre porque as licenças Business e Enterprise incluem o código-fonte completo de todos os módulos e temas.", "DoYouAcceptBankWireTransfer": "Você aceita transferência bancária?", - "DoYouAcceptBankWireTransferExplanation": "Sim, aceitamos transferência bancária.
Após o envio da licença por transferência bancária, envie-nos um e-mail para info@abp.io com o seu recibo e o tipo de licença solicitada. Nossas informações de conta bancária internacional:", + "DoYouAcceptBankWireTransferExplanation": "Sim, aceitamos transferência bancária.
Após enviar a taxa de licença por transferência bancária, envie-nos um e-mail para accounting@abp.io com seu recibo e o tipo de licença solicitada. Nossas informações de conta bancária internacional:", "HowToUpgrade": "Como atualizar os aplicativos existentes quando uma nova versão estiver disponível?", "HowToUpgradeExplanation1": "Quando você cria um novo aplicativo usando ABP Comercial, todos os módulos e o tema são usados como pacotes NuGet e NPM. Assim, você pode atualizar facilmente os pacotes quando uma nova versão estiver disponível.", "HowToUpgradeExplanation2": "Além das atualizações NuGet/NPM padrão, ABP CLI fornece um comando de atualização que automaticamente encontra e atualiza todos os pacotes relacionados a ABP em sua solução.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json index 02b42236e9..273b9790d6 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Care este diferenţa dintre prelungirea licenţei şi îmbunătăţirea acesteia?", "LicenseExtendUpgradeDiffExplanation": "Prelungirea: Prin prelungirea/reînnoirea licenţei dumneavoastră, veţi continua să aveţi acces la asistenţă premium, şi veţi avea parte de actualizările majore ale modulelor şi temelor. Pe lângă asta, veţi putea în continuare să creaţi proiecte noi. Veţi putea folosi în continuare suita ABP, ceea ce va accelera dezvoltarea.
Îmbunătăţirea: Prin îmbunătăţirea licenţei dumneavoastră, veţi promova la un plan de licenţă superior, care vă va permite să beneficiaţi de anumite avantaje. Consultaţi tabelul de comparare a licenţelor pentru a verifica diferenţele dintre planurile de licenţă.Pe de altă parte, când îmbunătăţiţi licenţa, data de expirare a acesteia nu se va schimba!Pentru a vă extinde data de încheiere a licenţei, va trebui să vă extindeţi licenţa.", "LicenseRenewalCost": "Care este costul de reînnoire a licenţei dupa 1 an?", - "LicenseRenewalCostExplanation": "Rata de reînnoire (extindere) a tuturor licenţelor ABP Comercial reprezintă {0} din preţul listei licenţelor. Preţul de reînnoire a licenţei standard de echipă este de ${1}, cel pentru licenţa standard de afaceri este de ${2} iar cel pentru licenţa standard enterprise este de ${3}. Dacă deja sunteţi clientul nostru, autentificaţi-vă pentru a examina preţurile disponibile pentru reînnoire.", + "LicenseRenewalCostExplanation": "Prețul de reînnoire (extindere) al licenței standard de echipă este de {0} USD, al licenței standard de afaceri este de {1} USD și al licenței standard Enterprise este de {2} USD. Dacă sunteți deja client, conectați-vă la contul dvs. pentru a examina prețurile de reînnoire disponibile.", "HowDoIRenewMyLicense": "Cum îmi reînnoiesc licenţa?", "HowDoIRenewMyLicenseExplanation": "Puteţi să vă reînnoiţi licenţa prin accesarea paginii pagina de gestionare a organizaţiei. Pentru a profita de tarifele noastre reduse de reînnoire anticipată, asiguraţi-vă că reînnoiţi înainte de expirarea licenţei. Totuşi, nu vă faceţi griji dacă nu ştiţi când expiră oportunitatea dumneavoastră de reînnoire anticipată. Veţi primi 2 e-mailuri de reamintire înainte de expirarea abonamentului. Vi le vom trimite cu 30, respectiv 7 zile înainte de expirare.", "IsSourceCodeIncluded": "Licenţa mea include şi codul sursă al modulelor comerciale şi a temelor?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Pe viitor, pot schimba dezvoltatorii ataşaţi organizaţiei mele?", "ChangingDevelopersExplanation": "În plus faţă de adăugarea de noi dezvoltatori la licenţa dumneavoastră, puteţi schimba şi dezvoltatorii existenţi(puteţi elimina un dezvoltator şi adăuga unul nou în locul lui) fără costuri suplimentare.", "WhatHappensWhenLicenseEnds": "Ce se întâmplă când se încheie perioada licenţei mele?", - "WhatHappensWhenLicenseEndsExplanation1": "Tipul de licenţă ABP Comercial este o licenţă perpetuă. După ce vă expiră licenţa, puteţi continua dezvoltarea proiectului dumneavoastră. Şi nu sunteţi obligat să vă reînnoiţi licenţa. Licenţa dumneavoastră vine la pachet cu actualizări şi plan de asistenţă valabile timp de un an. Pentru a continua să beneficiaţi de noi caracteristici, îmbunătăţiri de performanţe, corectarea erorilor, asistenţă şi să continuaţi să utilizaţi suita ABP, asiguraţi-vă că vă reînnoiţi licenţa anual. Când vă expiră licenţa, nu veţi mai beneficia de niciunul din beneficiile următoare;", + "WhatHappensWhenLicenseEndsExplanation1": "Tipul de licență comercială ABP este licență perpetuă. După expirarea licenței, puteți continua să vă dezvoltați proiectul. Și nu sunteți obligat să vă reînnoiți permisul. Licența dvs. vine cu o actualizare de un an și un plan de asistență. Pentru a beneficia în continuare de noi funcții, îmbunătățiri de performanță, remedieri de erori, asistență și pentru a continua să utilizați ABP Suite, trebuie să vă reînnoiți licența. La expirarea licenței, nu veți beneficia de următoarele beneficii;", "WhatHappensWhenLicenseEndsExplanation2": "Nu puteţi crea noi soluţii folosind ABP Commercial, dar puteţi continua dezvoltarea aplicaţiilor existente oricând.", - "WhatHappensWhenLicenseEndsExplanation3": "You will be able to get updates for the modules and themes within your MAJOR version. For example; if you are using v3.2.0 of a module, you can still get updates for v3.x.x (v3.3.0, v3.5.2... etc.) of that module. But you cannot get updates for the next major version (like v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "Veți putea obține actualizări pentru modulele și temele din versiunea dvs. MAJOR (cu excepția versiunilor RC sau Preview). De exemplu; dacă utilizați v3.2.0 a unui modul, puteți obține în continuare actualizări pentru v3.x.x (v3.3.0, v3.5.2... etc.) ale acelui modul. Dar nu puteți obține actualizări pentru următoarea versiune majoră (cum ar fi v4.x, v5.x). De exemplu, când licența dvs. a expirat, cea mai recentă versiune a fost v4.4.3 și cea mai recentă versiune de previzualizare a fost v5.0.0-rc.2, puteți accesa v4.X.X, dar nu puteți accesa v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Nu puteţi instala noi module şi teme adăugate pe platforma ABP Comercial după ce se încheie licenţa dumneavoastră.", "WhatHappensWhenLicenseEndsExplanation5": "Nu puteţi folosi suita ABP.", "WhatHappensWhenLicenseEndsExplanation6": "Nu veţi mai avea parte de asistenţă premium.", - "WhatHappensWhenLicenseEndsExplanation7": "Puteţi să vă reînnoiţi abonamentul dacă vreţi să continuaţi să aveţi parte de aceste beneficii. Dacă vă reînnoiţi abonamentul în termen de o lună după ce vă expiră licenţa, vi se aplică un discount de 20%.", + "WhatHappensWhenLicenseEndsExplanation7": "Vă puteți prelungi (reînnoi) licența dacă doriți să continuați să beneficiați de aceste beneficii. Dacă vă prelungiți licența în termen de 1 lună după expirarea licenței, se vor aplica următoarele reduceri: Licență de echipă {0}% reducere, Licență de afaceri {1}% reducere, Licență Enterprise {2}% reducere .", "WhenShouldIRenewMyLicense": "Când ar trebui să-mi reînnoiesc licenţa?", - "WhenShouldIRenewMyLicenseExplanation1": "Dacă vă reînnoiţi licenţa în termen de 1 lună după ce vă expiră licenţa, se va aplica un discount de 20%.", - "WhenShouldIRenewMyLicenseExplanation2": "Dacă vă reînnoiţi licenţa la o lună după ce aceasta a expirat, preţul de reînnoire va fi acelaşi cu cel iniţial, şi nu va fi aplicat niciun discount pentru reînnoirea dumneavoastră.", + "WhenShouldIRenewMyLicenseExplanation": "Dacă vă reînnoiți licența în termen de o lună de la expirarea licenței, se vor aplica următoarele reduceri: Licență de echipă {0}% reducere, Licență de afaceri {1}% reducere, Licență Enterprise {2}% reducere . Dacă vă reînnoiți licența la 1 lună după data de expirare a licenței, prețul de reînnoire va fi același cu prețul de achiziție a licenței și nu va exista nicio reducere la reînnoirea dvs.", "TrialPlan": "Aveţi un plan de încercare?", "TrialPlanExplanation": "Pentru moment, ABP Comercial nu include un plan de încercare. Pentru licenţa de Team oferim înapoirea banilor în termen de 30 de zile. Puteţi să solicitaţi o restituire în primele 30 de zile. Pentru licenţele de tip Business şi Enterprise, noi asigurăm restituirea a 60% din sumă în 30 de zile. Acest lucru se datorează faptului că licenţele de tip Business şi Enterprise includ codul sursă complet al tuturor modulelor şi temelor.", "DoYouAcceptBankWireTransfer": "Acceptaţi transfer bancar?", - "DoYouAcceptBankWireTransferExplanation": "Da, acceptăm transfer bancar.
După trimiterea taxei de licenţă prin transfer bancar, trimiteţi-ne pe e-mail la info@abp.io chitanţa dumneavoastră şi tipul de licenţă solicitat. Informaţiile despre contul bancar internaţional al nostru:", + "DoYouAcceptBankWireTransferExplanation": "Da, acceptăm transfer bancar.
După ce ați trimis taxa de licență prin transfer bancar, trimiteți-ne un e-mail la accounting@abp.io chitanța dvs. și tipul de licență solicitat. Informațiile noastre internaționale despre contul nostru bancar:", "HowToUpgrade": "Cum se actualizează aplicaţiile existente atunci când este disponibilă o nouă versiune?", "HowToUpgradeExplanation1": "Cand creaţi o nouă aplicaţie folosind ABP Commercial, toate modulele şi tema sunt folosite sub formă de pachete NuGet şi NPM. Astfel, puteţi cu uşurinţă să actualizaţi pachetele atunci când o versiune nouă este disponibilă.", "HowToUpgradeExplanation2": "Pe lângă actualizările standard NuGet-NPM, ABP CLI furnizează o comandă de actualizare automată care identifică şi actualizează toate pachetele legate de ABP din soluţia dumneavoastră.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ru.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ru.json index 3ebfca458a..dbac54eaf9 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ru.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ru.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "В чем разница между продлением лицензии и обновлением?", "LicenseExtendUpgradeDiffExplanation": "Extending: By extending/renewing your license, you will continue to get premium support and get major updates for the modules and themes. Besides, you will be able to continue creating new projects. And you will still be able to use ABP Suite which speeds up your development.
Upgrading: By upgrading your license, you will promote to a higher license plan which will allow you to get additional benefits. See the license comparison table to check the differences between the license plans.On the other hand, when you upgrade, your license expiry date will not change!To extend your license end date, you need to extend your license.", "LicenseRenewalCost": "Какова стоимость продления лицензии через 1 год?", - "LicenseRenewalCostExplanation": "Скорость продления (продления) всех бессрочных коммерческих лицензий ABP составляет {0} от стоимости лицензии. Стоимость продления стандартной групповой лицензии составляет {1} долл. США, стандартной бизнес-лицензии составляет {2} долл. США, а стандартной корпоративной лицензии составляет {3} долл. США. Если вы уже являетесь клиентом, войдите в свою учетную запись , чтобы просмотреть доступные цены на продление.", + "LicenseRenewalCostExplanation": "Стоимость продления (продления) стандартной лицензии для группы составляет {0} долл. США, стандартной бизнес-лицензии составляет {1} долл. США, а стандартной лицензии Enterprise составляет {2} долл. США. Если вы уже являетесь клиентом, войдите в свою учетную запись , чтобы просмотреть доступные цены на продление.", "HowDoIRenewMyLicense": "Как мне продлить лицензию?", "HowDoIRenewMyLicenseExplanation": "Вы можете продлить лицензию, перейдя на страницу управления организацией . Чтобы воспользоваться нашими скидками на раннее продление, убедитесь, что вы продлили подписку до истечения срока действия лицензии. Однако не беспокойтесь о том, что не знаете, когда закроется ваша возможность раннего продления. Вы получите 3 напоминания по электронной почте до истечения срока подписки. Мы отправим их за 30 дней, 7 дней и 1 день до истечения срока.", "IsSourceCodeIncluded": "Включает ли моя лицензия исходный код коммерческих модулей и тем?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Могу ли я сменить зарегистрированных разработчиков моей организации в будущем?", "ChangingDevelopersExplanation": "Помимо добавления новых разработчиков к вашей лицензии, вы также можете изменить существующих разработчиков (вы можете удалить разработчика и добавить нового на то же место) без каких-либо дополнительных затрат.", "WhatHappensWhenLicenseEnds": "Что произойдет, когда закончится срок моей лицензии?", - "WhatHappensWhenLicenseEndsExplanation1": "Тип коммерческой лицензии ABP - бессрочная лицензия . По истечении срока действия лицензии вы можете продолжить разработку своего проекта. И вы не обязаны продлевать лицензию. Ваша лицензия поставляется с годовым планом обновлений и поддержки. Чтобы продолжать получать новые функции, улучшения производительности, исправления ошибок, поддержку и продолжать использовать ABP Suite, не забудьте ежегодно продлевать свой план. Когда срок действия вашей лицензии истечет, вы не сможете получить больше следующих преимуществ;", + "WhatHappensWhenLicenseEndsExplanation1": "Тип коммерческой лицензии ABP - бессрочная лицензия . По истечении срока действия лицензии вы можете продолжить разработку своего проекта. И вы не обязаны продлевать лицензию. Ваша лицензия поставляется с годовым обновлением и планом поддержки прямо из коробки. Чтобы продолжать получать новые функции, улучшения производительности, исправления ошибок, поддержку и продолжать использовать ABP Suite, вам необходимо продлить лицензию. По истечении срока действия лицензии вы не получите следующих преимуществ;", "WhatHappensWhenLicenseEndsExplanation2": "Вы не можете создавать новые решения с помощью ABP Commercial, но вы можете продолжать разрабатывать существующие приложения бесконечно.", - "WhatHappensWhenLicenseEndsExplanation3": "Вы сможете получать обновления для модулей и тем в вашей ОСНОВНОЙ версии. Например; если вы используете модуль v3.2.0, вы все равно можете получать обновления для v3.x.x (v3.3.0, v3.5.2 ... и т. д.) этого модуля. Но вы не можете получать обновления для следующей основной версии (например, v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "You will be able to get updates for the modules and themes within your MAJOR version (except RC or Preview versions). For example; if you are using v3.2.0 of a module, you can still get updates for v3.x.x (v3.3.0, v3.5.2... etc.) of that module. But you cannot get updates for the next major version (like v4.x, v5.x). For example, when your license expired, the latest release was v4.4.3 and the latest preview version was v5.0.0-rc.2, you can access the v4.X.X but you cannot access the v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Вы не можете устанавливать новые модули и темы, добавленные на коммерческую платформу ABP, после истечения срока действия лицензии.", "WhatHappensWhenLicenseEndsExplanation5": "Вы не можете использовать ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "Вы больше не можете получать премиум-поддержку .", - "WhatHappensWhenLicenseEndsExplanation7": "Вы можете продлить подписку, если хотите и дальше пользоваться этими преимуществами. Если вы продлеваете подписку в течение 1 месяца после истечения срока действия лицензии, применяется скидка 20%.", + "WhatHappensWhenLicenseEndsExplanation7": "Вы можете продлить (обновить) вашу лицензию, если хотите и дальше пользоваться этими преимуществами. Если вы продлите лицензию в течение 1 месяца после истечения срока действия лицензии, будут применяться следующие скидки: групповая лицензия {0}% скидка, бизнес-лицензия {1}% скидка, корпоративная лицензия {2}% скидка .", "WhenShouldIRenewMyLicense": "Когда мне следует продлить лицензию?", - "WhenShouldIRenewMyLicenseExplanation1": "Если вы продлеваете лицензию в течение 1 месяца после истечения срока действия лицензии, применяется скидка 20%.", - "WhenShouldIRenewMyLicenseExplanation2": "Если вы продлите свою лицензию через 1 месяц с даты истечения срока действия лицензии, цена продления будет такой же, как цена покупки лицензии, и не будет скидки на продление.", + "WhenShouldIRenewMyLicenseExplanation": "Если вы продлите лицензию в течение 1 месяца после истечения срока действия лицензии, будут применяться следующие скидки: групповая лицензия {0}% скидка, бизнес-лицензия {1}% скидка, корпоративная лицензия {2}% скидка . Если вы продлеваете лицензию через 1 месяц после даты истечения срока действия лицензии, цена продления будет такой же, как цена покупки лицензии, и при продлении скидки не будет.", "TrialPlan": "У вас есть пробный план?", "TrialPlanExplanation": "На данный момент у ABP Commercial нет пробного плана. Для командных лицензий мы предоставляем 30-дневную гарантию возврата денег. Вы можете просто запросить возврат в течение первых 30 дней. Для лицензий Business и Enterprise мы возвращаем 60% в течение 30 дней. Это связано с тем, что лицензии Business и Enterprise включают полный исходный код всех модулей и тем.", "DoYouAcceptBankWireTransfer": "Вы принимаете банковский перевод?", - "DoYouAcceptBankWireTransferExplanation": "Да, мы принимаем банковский перевод.
После отправки платы за лицензию банковским переводом отправьте нам по электронной почте info@abp.io квитанцию и тип запрошенной лицензии. Информация о нашем международном банковском счете:", + "DoYouAcceptBankWireTransferExplanation": "Да, мы принимаем банковский перевод.
После отправки платы за лицензию банковским переводом отправьте нам электронное письмо по адресу accounting@abp.io, квитанцию и тип запрошенной лицензии. Информация о нашем международном банковском счете:", "HowToUpgrade": "Как обновить существующие приложения, когда доступна новая версия?", "HowToUpgradeExplanation1": "Когда вы создаете новое приложение с помощью ABP Commercial, все модули и тема используются как пакеты NuGet и NPM. Таким образом, вы можете легко обновить пакеты, когда станет доступна новая версия.", "HowToUpgradeExplanation2": "Помимо стандартных обновлений NuGet/NPM, ABP CLI предоставляет команду обновления, которая автоматически находит и обновляет все связанные с ABP пакеты в вашем решении.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json index e9193febc3..a6e95cd794 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Aký je rozdiel medzi rozšírením a aktualizáciou licencie?", "LicenseExtendUpgradeDiffExplanation": "Predĺženie: Predĺžením/obnovením licencie budete naďalej dostávať prémiovú podporu a hlavné aktualizácie modulov a tém. Okrem toho budete môcť pokračovať vo vytváraní nových projektov, naďalej používať balík ABP Suite, ktorý urýchľuje váš vývoj.
Podpísanie licencie: Rozšírením licencie postúpite na vyšší licenčný plán, ktorý vám umožní získať ďalšie výhody. Pozrite si tabuľku porovnania licencií a skontrolujte rozdiely medzi licenčnými plánmi.Na druhej strane, pri upgrade sa dátum skončenia platnosti vašej licencie nezmení! Ak chcete predĺžiť dátum skončenia platnosti vašej licencie, musíte si licenciu predĺžiť.", "LicenseRenewalCost": "Aké sú náklady na obnovenie licencie po 1 roku?", - "LicenseRenewalCostExplanation": "Sadzba za obnovenie (predĺženie) všetkých trvalých licencií ABP Commercial je {0} z cenníkovej ceny licencie. Cena za obnovenie štandardnej tímovej licencie je {1} USD, štandardnej obchodnej licencie je {2} USD a štandardnej podnikovej licencie je {3} USD. Ak už ste zákazníkom, prihláste sa do svojho konta a prezrite si dostupné ceny za obnovenie.", + "LicenseRenewalCostExplanation": "Cena obnovenia (predĺženia) štandardnej tímovej licencie je ${0}, štandardnej obchodnej licencie je ${1} a štandardnej podnikovej licencie je ${2}. Ak už ste zákazníkom, prihláste sa do svojho účtu a pozrite si dostupné ceny za obnovenie.", "HowDoIRenewMyLicense": "Ako si môžem obnoviť licenciu?", "HowDoIRenewMyLicenseExplanation": "Svoju licenciu môžete obnoviť tak, že prejdete na stránku správa organizície. Ak chcete využiť naše zľavnené ceny za predčasné obnovenie, uistite sa, že ste licenciu obnovili pred uplynutím jej platnosti. Netrápte sa však tým, že neviete, kedy sa vaša možnosť predčasného obnovenia skončí. Pred vypršaním platnosti predplatného dostanete 2 e-maily s pripomienkami. Budeme ich posielať o 30 dní, teda 7 dní pred vypršaním platnosti.", "IsSourceCodeIncluded": "Zahŕňa moja licencia zdrojový kód komerčných modulov a tém?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Môžem v budúcnosti zmeniť registrovaných vývojárov svojej organizácie?", "ChangingDevelopersExplanation": "Okrem pridávania nových vývojárov do licencie môžete tiež meniť existujúcich vývojárov (môžete odstrániť vývojára a pridať nového na to isté miesto) bez akýchkoľvek ďalších nákladov.", "WhatHappensWhenLicenseEnds": "Čo sa stane po skončení platnosti licencie?", - "WhatHappensWhenLicenseEndsExplanation1": "Typ komerčnej licencie ABP je trvalá licencia. Po uplynutí platnosti licencie môžete pokračovať vo vývoji svojho projektu. A nie ste povinní obnoviť svoju licenciu. Vaša licencia sa dodáva s ročným plánom aktualizácií a podpory už v balení. Ak chcete aj naďalej dostávať nové funkcie, vylepšenia výkonu, opravy chýb, podporu a pokračovať v používaní balíka ABP Suite, nezabudnite si plán každý rok obnoviť. Po skončení platnosti vašej licencie nebudete môcť získať ďalšie z nasledujúcich výhod;", + "WhatHappensWhenLicenseEndsExplanation1": "Typ licencie ABP Commercial je trvalá licencia. Po vypršaní platnosti licencie môžete pokračovať vo vývoji svojho projektu. A nie ste povinní obnoviť si licenciu. Vaša licencia sa dodáva s jednoročnou aktualizáciou a plánom podpory hneď po vybalení. Ak chcete naďalej získavať nové funkcie, vylepšenia výkonu, opravy chýb, podporu a naďalej používať ABP Suite, musíte si obnoviť licenciu. Po vypršaní platnosti licencie nezískate nasledujúce výhody;", "WhatHappensWhenLicenseEndsExplanation2": "Pomocou ABP Commercial nemôžete vytvárať nové riešenia, ale navždy môžete pokračovať vo vývoji existujúcich aplikácií.", - "WhatHappensWhenLicenseEndsExplanation3": "Budete môcť získať aktualizácie modulov a tém v rámci vašej hlavnej verzie. Ak napríklad používate modul vo verzii 3.2.0, stále môžete získať aktualizácie pre verziu 3.x.x (v3.3.0, v3.5.2... atď.) tohto modulu. Nemôžete však získať aktualizácie pre ďalšiu hlavnú verziu (napr. v4.x, v5.x).", + "WhatHappensWhenLicenseEndsExplanation3": "Budete môcť získať aktualizácie pre moduly a témy v rámci vašej HLAVNEJ verzie (okrem verzií RC alebo Preview). Napríklad; ak používate verziu 3.2.0 modulu, stále môžete získať aktualizácie pre verziu 3.x.x (v3.3.0, v3.5.2... atď.) tohto modulu. Nemôžete však získať aktualizácie pre ďalšiu hlavnú verziu (napríklad v4.x, v5.x). Napríklad, keď platnosť vašej licencie vypršala, najnovšie vydanie bolo v4.4.3 a najnovšia ukážková verzia bola v5.0.0-rc.2, máte prístup k verzii 4.X.X, ale nemáte prístup k verzii 5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Po skončení platnosti vašej licencie nemôžete inštalovať nové moduly a témy pridané do platformy ABP Commercial.", "WhatHappensWhenLicenseEndsExplanation5": "Balík ABP Suite nemôžete používať.", "WhatHappensWhenLicenseEndsExplanation6": "Už nemôžete získať prémiovú podporu.", - "WhatHappensWhenLicenseEndsExplanation7": "Ak chcete tieto výhody využívať aj naďalej, môžete si predplatné obnoviť. Pri obnovení predplatného sa poskytuje 20 % zľava.", + "WhatHappensWhenLicenseEndsExplanation7": "You can extend (renew) your license if you want to continue to get these benefits. If you extend your license within 1 month after your license expires, the following discounts will be applied: Team License {0}% discount, Business License {1}% discount, Enterprise License {2}% discount.", "WhenShouldIRenewMyLicense": "Kedy si mám obnoviť licenciu?", - "WhenShouldIRenewMyLicenseExplanation1": "Ak si licenciu obnovíte do 1 mesiaca po vypršaní platnosti licencie, na celkovú cenu licencie bude uplatnená zľava 20%.", - "WhenShouldIRenewMyLicenseExplanation2": "Ak obnovíte licenciu po uplynutí 1 mesiaca od dátumu vypršania platnosti licencie, cena za obnovenie bude rovnaká ako cena za nákup licencie a na obnovenie nebude poskytnutá žiadna zľava.", + "WhenShouldIRenewMyLicenseExplanation": "Ak si licenciu obnovíte do 1 mesiaca po vypršaní platnosti licencie, budú sa uplatňovať tieto zľavy: Tímová licencia {0}% zľava, Obchodná licencia {1}% zľava, Enterprise licencia {2}% zľava . Ak si licenciu obnovíte 1 mesiac po dátume skončenia platnosti licencie, cena za obnovenie bude rovnaká ako cena za nákup licencie a na obnovenie nebude poskytnutá žiadna zľava.", "TrialPlan": "Máte skúšobný plán?", "TrialPlanExplanation": "Spoločnosť ABP Commercial zatiaľ nemá skúšobný plán. Pre tímové licencie poskytujeme 30-dňovú záruku vrátenia peňazí. Stačí požiadať o vrátenie peňazí počas prvých 30 dní. V prípade licencií Business a Enterprise poskytujeme 60% vrátenie peňazí do 30 dní. Je to preto, že licencie Business a Enterprise obsahujú úplný zdrojový kód všetkých modulov a tém.", "DoYouAcceptBankWireTransfer": "Prijímate bankový prevod?", - "DoYouAcceptBankWireTransferExplanation": "Áno, akceptujeme bankový prevod.
Po zaslaní sumy za licenciu bankovým prevodom nám pošlite potvrdenie a požadovaný typ licencie e-mailom.
Naše informácie o medzinárodnom bankovom účte:", + "DoYouAcceptBankWireTransferExplanation": "Áno, akceptujeme bankový prevod.
Po odoslaní licenčného poplatku bankovým prevodom nám pošlite e-mail na adresu accounting@abp.io svoje potvrdenie a požadovaný typ licencie. Informácie o našom medzinárodnom bankovom účte:", "HowToUpgrade": "Ako aktualizovať existujúce aplikácie, keď je k dispozícii nová verzia?", "HowToUpgradeExplanation1": "Pri vytváraní novej aplikácie pomocou ABP Commercial sa všetky moduly a téma použijú ako balíky NuGet a NPM. Balíky teda môžete ľahko aktualizovať, keď je k dispozícii nová verzia.", "HowToUpgradeExplanation2": "Okrem štandardných aktualizácií NuGet/NPM poskytuje ABP CLI príkaz update, ktorý automaticky nájde a aktualizuje všetky balíky súvisiace s ABP vo vašom riešení.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json index 23b6e130f1..3c9254349e 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Kakšna je razlika med podaljšanjem licence in nadgradnjo?", "LicenseExtendUpgradeDiffExplanation": "Razširitev: S podaljšanjem/obnovitvijo licence boste še naprej prejemali vrhunsko podporo in večje posodobitve za module in teme. Poleg tega boste lahko nadaljevali z ustvarjanjem novih projektov. Še vedno boste lahko uporabljali zbirko ABP Suite, ki pospeši vaš razvoj.
Nadgradnja: Z nadgradnjo vaše licence boste napredovali v višji licenčni načrt, ki vam bo omogočil dodatne ugodnosti . Če želite preveriti razlike med licenčnimi načrti, glejte tabelo za primerjavo licenc.Po drugi strani pa se datum poteka licence ne bo spremenil, ko nadgradite! >Če želite podaljšati končni datum licence, morate podaljšati licenco.", "LicenseRenewalCost": "Kakšni so stroški podaljšanja licence po 1 letu?", - "LicenseRenewalCostExplanation": "Stopnja podaljšanja (podaljšanja) vseh trajnih licenc ABP Commercial je {0} maloprodajne cene licence. Cena podaljšanja standardne licence za ekipo je {1} $, standardne poslovne licence {2} $ in standardne licence za podjetje je {3} $. Če ste že stranka, se prijavite v svoj račun in si oglejte razpoložljive cene za podaljšanje.", + "LicenseRenewalCostExplanation": "Cena podaljšanja (podaljšanja) standardne licence ekipe je {0} $, standardne poslovne licence {1} $ in standardne licence za podjetje je {2} $. Če ste že stranka, se prijavite v svoj račun in si oglejte razpoložljive cene za podaljšanje.", "HowDoIRenewMyLicense": "Kako podaljšam licenco?", "HowDoIRenewMyLicenseExplanation": "Licenco lahko podaljšate tako, da se pomaknete na stran za upravljanje organizacije. Če želite izkoristiti naše znižane cene zgodnjega podaljšanja, se prepričajte, da ste jo podaljšali, preden vam poteče licenca. Ne skrbite, da ne boste vedeli, kdaj se izteče vaša priložnost za zgodnjo obnovo. Pred potekom naročnine boste prejeli 3 e-poštna sporočila z opomnikom. Poslali jih bomo 30 dni, 7 dni in 1 dan pred iztekom.", "IsSourceCodeIncluded": "Ali moja licenca vključuje izvorno kodo komercialnih modulov in tem?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Ali lahko v prihodnosti spremenim registrirane razvijalce svoje organizacije?", "ChangingDevelopersExplanation": "Poleg dodajanja novih razvijalcev vaši licenci lahko spremenite tudi obstoječe razvijalce (lahko odstranite razvijalca in dodate novega na isto mesto) brez dodatnih stroškov.", "WhatHappensWhenLicenseEnds": "Kaj se zgodi, ko se moje licenčno obdobje konča?", - "WhatHappensWhenLicenseEndsExplanation1": "Vrsta licence ABP Commercial je trajna licenca. Po poteku licence lahko nadaljujete z razvojem svojega projekta. In licence vam ni treba podaljšati. Vaša licenca je priložena enoletnemu načrtu za posodobitve in podporo. Če želite še naprej prejemati nove funkcije, izboljšave zmogljivosti, popravke napak, podporo in še naprej uporabljati ABP Suite, vsako leto obnovite svoj načrt. Ko vam licenca poteče, ne boste mogli več prejemati naslednjih ugodnosti;", + "WhatHappensWhenLicenseEndsExplanation1": "Vrsta licence ABP Commercial je trajna licenca. Po poteku licence lahko nadaljujete z razvojem svojega projekta. In licence vam ni treba podaljšati. Vaša licenca je priložena enoletnemu načrtu za posodobitev in podporo. Če želite še naprej prejemati nove funkcije, izboljšave zmogljivosti, popravke napak, podporo in še naprej uporabljati ABP Suite, morate obnoviti licenco. Ko vaša licenca poteče, ne boste prejeli naslednjih ugodnosti;", "WhatHappensWhenLicenseEndsExplanation2": "Z uporabo ABP Commercial ne morete ustvarjati novih rešitev, lahko pa za vedno razvijate svoje obstoječe aplikacije.", - "WhatHappensWhenLicenseEndsExplanation3": "V svoji MAJOR različici boste lahko prejemali posodobitve za module in teme. Na primer; če uporabljate v3.2.0 modula, lahko še vedno dobite posodobitve za v3.x.x (v3.3.0, v3.5.2 ... itd.) tega modula. Vendar ne morete dobiti posodobitev za naslednjo večjo različico (na primer v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "Prejeli boste lahko posodobitve za module in teme v vaši MAJOR različici (razen različic RC ali Preview). Na primer; če uporabljate v3.2.0 modula, lahko še vedno dobite posodobitve za v3.x.x (v3.3.0, v3.5.2 ... itd.) tega modula. Vendar ne morete dobiti posodobitev za naslednjo večjo različico (na primer v4.x, v5.x). Na primer, ko je vaša licenca potekla, je bila najnovejša izdaja v4.4.3 in najnovejša različica za predogled v5.0.0-rc.2, lahko dostopate do v4.X.X, vendar ne morete dostopati do v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Po prenehanju licence ne morete namestiti novih modulov in tem, ki so bili dodani platformi ABP Commercial.", "WhatHappensWhenLicenseEndsExplanation5": "Ne morete uporabljati ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "Ne morete več dobiti premium podpore.", - "WhatHappensWhenLicenseEndsExplanation7": "Če želite še naprej prejemati te ugodnosti, lahko podaljšate naročnino. Če naročnino podaljšate v roku 1 meseca po poteku licence, velja 20 % popust.", + "WhatHappensWhenLicenseEndsExplanation7": "Licenco lahko podaljšate (obnovite), če želite še naprej uživati te ugodnosti. Če podaljšate licenco v 1 mesecu po poteku licence, bodo uveljavljeni naslednji popusti: licenca ekipe {0} % popusta, poslovna licenca {1} % popusta, licenca podjetja {2} % popusta .", "WhenShouldIRenewMyLicense": "Kdaj naj podaljšam licenco?", - "WhenShouldIRenewMyLicenseExplanation1": "Če licenco podaljšate v 1 mesecu po poteku licence, velja 20 % popust.", - "WhenShouldIRenewMyLicenseExplanation2": "Če licenco podaljšate po 1 mesecu od datuma poteka veljavnosti licence, bo cena podaljšanja enaka nakupni ceni licence in za podaljšanje ne bo popusta.", + "WhenShouldIRenewMyLicenseExplanation": "Če licenco podaljšate v 1 mesecu po poteku licence, bodo uporabljeni naslednji popusti: licenca za ekipo {0} % popusta, poslovna licenca {1} % popusta, licenca za podjetje {2} % popusta . Če podaljšate licenco 1 mesec po datumu poteka veljavnosti licence, bo cena podaljšanja enaka nakupni ceni licence in pri podaljšanju ne bo popusta.", "TrialPlan": "Ali imate poskusni načrt?", "TrialPlanExplanation": "Za zdaj ABP Commercial nima načrta za preskus. Za licence ekipe nudimo 30-dnevno garancijo vračila denarja. Zahtevate lahko samo vračilo kupnine v prvih 30 dneh. Za licence Business in Enterprise nudimo 60 % vračilo v 30 dneh. To je zato, ker licence Business in Enterprise vključujejo celotno izvorno kodo vseh modulov in tem.", "DoYouAcceptBankWireTransfer": "Ali sprejemate bančno nakazilo?", - "DoYouAcceptBankWireTransferExplanation": "Da, sprejemamo bančno nakazilo.
Ko pošljete licenčnino prek bančnega nakazila, nam pošljite e-pošto na info@abp.io vaše potrdilo o prejemu in vrsto zahtevane licence. Naši podatki o mednarodnem bančnem računu:", + "DoYouAcceptBankWireTransferExplanation": "Da, sprejemamo bančno nakazilo.
Ko pošljete licenčnino prek bančnega nakazila, nam pošljite e-pošto na accounting@abp.io vaše potrdilo o prejemu in vrsto zahtevane licence. Naši podatki o mednarodnem bančnem računu:", "HowToUpgrade": "Kako nadgraditi obstoječe aplikacije, ko je na voljo nova različica?", "HowToUpgradeExplanation1": "Ko ustvarite novo aplikacijo z uporabo ABP Commercial, se vsi moduli in tema uporabljajo kot paketa NuGet in NPM. Tako lahko preprosto nadgradite pakete, ko je na voljo nova različica.", "HowToUpgradeExplanation2": "Poleg standardnih nadgradenj NuGet/NPM ponuja ABP CLI ukaz za posodobitev, ki samodejno poišče in nadgradi vse pakete, povezane z ABP, v vaši rešitvi.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json index ae472a27f0..c653314255 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Lisans uzatma ve yükseltme arasındaki fark nedir?", "LicenseExtendUpgradeDiffExplanation": "Uzatma: Lisansınızı uzatarak/yenileyerek, premium destek almaya ve modüller ve temalar için önemli güncellemeler almaya devam edeceksiniz. Ayrıca, yeni projeler oluşturmaya devam edebileceksiniz. Ve geliştirmenizi hızlandıran ABP Suite'i kullanmaya devam edebileceksiniz.
Yükseltme: Lisansınızı yükselterek, ek avantajlar elde etmenizi sağlayacak daha yüksek bir lisans planına terfi edeceksiniz. . Lisans planları arasındaki farkları kontrol etmek için lisans karşılaştırma tablosuna bakın.Öte yandan, yükseltme yaptığınızda lisans geçerlilik bitiş tarihiniz değişmez!Lisans bitiş tarihinizi uzatmak için lisansınızı uzatmanız gerekir.", "LicenseRenewalCost": "1 yıl sonra ehliyet yenileme ücreti ne kadardır?", - "LicenseRenewalCostExplanation": "Tüm ABP Ticari kalıcı lisanslarının yenileme (uzatma) oranı, lisans liste fiyatının {0} kadarıdır. Standart Takım Lisansının yenileme fiyatı {1}$, standart İşletme Lisansı {2}$ ve standart Enterprise Lisansı {3}$'dır. Zaten bir müşteriyseniz, mevcut yenileme fiyatlarını incelemek için hesabınıza giriş yapın.", + "LicenseRenewalCostExplanation": "Standart Takım Lisansının yenileme (uzatma) fiyatı ${0}, standart İşletme Lisansı ${1} ve standart Enterprise Lisansı {2} $'dır. Zaten bir müşteriyseniz, mevcut yenileme fiyatlarını incelemek için hesabınıza giriş yapın.", "HowDoIRenewMyLicense": "Lisansımı nasıl yenilerim?", "HowDoIRenewMyLicenseExplanation": "Kuruluş yönetimi sayfasına giderek lisansınızı yenileyebilirsiniz. İndirimli Erken Yenileme fiyatlarımızdan yararlanmak için lisansınızın süresi dolmadan yenilemeyi unutmayınız. Bununla birlikte, Erken Yenileme fırsatınızın ne zaman kapanacağını bilmemek konusunda endişelenmeyin. Aboneliğiniz sona ermeden önce 3 hatırlatma e-postası alacaksınız. Son kullanma tarihinden 30 gün, 7 gün ve 1 gün önce göndereceğiz.", "IsSourceCodeIncluded": "Lisansım ticari modüllerin ve temaların kaynak kodunu içeriyor mu?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Gelecekte kuruluşumun kayıtlı geliştiricilerini değiştirebilir miyim?", "ChangingDevelopersExplanation": "Lisansınıza yeni geliştiriciler eklemenin yanı sıra mevcut geliştiricileri de herhangi bir ek ücret ödemeden değiştirebilirsiniz (bir geliştiriciyi kaldırıp aynı koltuğa yeni bir tane ekleyebilirsiniz).", "WhatHappensWhenLicenseEnds": "Lisans sürem sona erdiğinde ne olur?", - "WhatHappensWhenLicenseEndsExplanation1": "ABP Ticari lisans türü kalıcı lisanstır. Lisansınızın süresi dolduktan sonra projenizi geliştirmeye devam edebilirsiniz. Ve ehliyetinizi yenilemek zorunda değilsiniz. Lisansınız, kutudan çıktığı gibi bir yıllık Güncellemeler ve Destek planıyla birlikte gelir. Yeni özellikler, performans geliştirmeleri, hata düzeltmeleri, destek almaya devam etmek ve ABP Suite'i kullanmaya devam etmek için planınızı her yıl yenilemeyi unutmayın. Lisansınızın süresi dolduğunda, aşağıdaki avantajlardan daha fazlasını elde edemezsiniz;", + "WhatHappensWhenLicenseEndsExplanation1": "ABP Ticari lisans türü kalıcı lisanstır. Lisansınızın süresi dolduktan sonra projenizi geliştirmeye devam edebilirsiniz. Ve lisansınızı yenilemek zorunda değilsiniz. Lisansınız, kutudan çıktığı gibi bir yıllık güncelleme ve destek planıyla birlikte gelir. Yeni özellikler, performans geliştirmeleri, hata düzeltmeleri, destek almaya devam etmek ve ABP Suite'i kullanmaya devam etmek için lisansınızı yenilemeniz gerekir. Lisansınızın süresi dolduğunda aşağıdaki avantajlardan yararlanamazsınız;", "WhatHappensWhenLicenseEndsExplanation2": "ABP Ticari kullanarak yeni çözümler üretemezsiniz, ancak mevcut uygulamalarınızı sonsuza kadar geliştirmeye devam edebilirsiniz.", - "WhatHappensWhenLicenseEndsExplanation3": "MAJOR sürümünüzdeki modüller ve temalar için güncellemeler alabileceksiniz. Örneğin; Bir modülün v3.2.0'ını kullanıyorsanız, yine de o modülün v3.x.x (v3.3.0, v3.5.2... vb.) güncellemelerini alabilirsiniz. Ancak bir sonraki ana sürüm için güncellemeler alamazsınız (v4.x, v5.x gibi)", + "WhatHappensWhenLicenseEndsExplanation3": "MAJOR sürümünüzdeki modüller ve temalar için güncellemeler alabileceksiniz (RC veya Önizleme sürümleri hariç). Örneğin; Bir modülün v3.2.0'ını kullanıyorsanız, yine de o modülün v3.x.x (v3.3.0, v3.5.2... vb.) güncellemelerini alabilirsiniz. Ancak bir sonraki ana sürüm için güncellemeler alamazsınız (v4.x, v5.x gibi). Örneğin, lisansınızın süresi dolduğunda, en son sürüm v4.4.3 ve en son önizleme sürümü v5.0.0-rc.2 olduğunda, v4.X.X'e erişebilirsiniz ancak v5.X.X'e erişemezsiniz.", "WhatHappensWhenLicenseEndsExplanation4": "ABP Ticari platformuna eklenen yeni modülleri ve temaları lisansınız sona erdikten sonra kuramazsınız.", "WhatHappensWhenLicenseEndsExplanation5": "ABP Suite'i kullanamazsınız.", "WhatHappensWhenLicenseEndsExplanation6": "Artık premium desteği alamazsınız.", - "WhatHappensWhenLicenseEndsExplanation7": "Bu avantajlardan yararlanmaya devam etmek istiyorsanız aboneliğinizi yenileyebilirsiniz. Lisansınızın süresi dolduktan sonra 1 ay içerisinde aboneliğinizi yenilerseniz %20 indirim uygulanır.", + "WhatHappensWhenLicenseEndsExplanation7": "Bu avantajlardan yararlanmaya devam etmek istiyorsanız lisansınızı uzatabilirsiniz (yenileyebilirsiniz). Lisansınızın süresi dolduktan sonra 1 ay içinde lisansınızı uzatırsanız, aşağıdaki indirimler uygulanacaktır: Takım Lisansı %{0} indirim, İşletme Lisansı %{1} indirim, Kurumsal Lisans %{2} indirim .", "WhenShouldIRenewMyLicense": "Ehliyetimi ne zaman yenilemeliyim?", - "WhenShouldIRenewMyLicenseExplanation1": "Lisansınızın süresi dolduktan sonra 1 ay içinde lisansınızı yenilerseniz %20 indirim uygulanır.", - "WhenShouldIRenewMyLicenseExplanation2": "Lisansınızın bitiş tarihinden 1 ay sonra lisansınızı yenilerseniz, yenileme fiyatı lisans satın alma fiyatı ile aynı olacak ve yenilemenizde indirim yapılmayacaktır.", + "WhenShouldIRenewMyLicenseExplanation": "Lisansınızın süresi dolduktan sonra 1 ay içinde lisansınızı yenilerseniz, aşağıdaki indirimler uygulanacaktır: Takım Lisansı %{0} indirim, İşletme Lisansı %{1} indirim, Kurumsal Lisans %{2} indirim . Lisansınızın sona erme tarihinden 1 ay sonra lisansınızı yenilerseniz, yenileme fiyatı lisans satın alma fiyatı ile aynı olacak ve yenilemenizde indirim yapılmayacaktır.", "TrialPlan": "Deneme planınız var mı?", "TrialPlanExplanation": "ABP Commercial'ın şimdilik bir deneme planı yok. Ekip lisansları için 30 gün para iade garantisi veriyoruz. Sadece ilk 30 gün içinde geri ödeme talebinde bulunabilirsiniz. Business ve Enterprise lisansları için 30 gün içinde %60 geri ödeme sağlıyoruz. Bunun nedeni, Business ve Enterprise lisanslarının tüm modüllerin ve temaların tam kaynak kodunu içermesidir.", "DoYouAcceptBankWireTransfer": "Banka havalesini kabul ediyor musunuz?", - "DoYouAcceptBankWireTransferExplanation": "Evet, banka havalesini kabul ediyoruz.
Lisans ücretini banka havalesi yoluyla gönderdikten sonra, makbuzunuzu ve istenen lisans türünü info@abp.io adresine e-posta ile gönderin. Uluslararası banka hesap bilgilerimiz:", + "DoYouAcceptBankWireTransferExplanation": "Evet, banka havalesini kabul ediyoruz.
Lisans ücretini banka havalesi yoluyla gönderdikten sonra, dekontunuzu ve talep edilen lisans türünü accounting@abp.io adresinden bize e-posta ile gönderin. Uluslararası banka hesap bilgilerimiz:", "HowToUpgrade": "Yeni bir sürüm mevcut olduğunda mevcut uygulamalar nasıl yükseltilir?", "HowToUpgradeExplanation1": "ABP Commercial kullanarak yeni bir uygulama oluşturduğunuzda, tüm modüller ve tema NuGet ve NPM paketleri olarak kullanılır. Böylece yeni bir sürüm çıktığında paketleri kolayca yükseltebilirsiniz.", "HowToUpgradeExplanation2": "Standart NuGet/NPM yükseltmelerine ek olarak, ABP CLI, çözümünüzdeki ABP ile ilgili tüm paketleri otomatik olarak bulan ve yükselten bir güncelleme komutu sağlar.", @@ -390,6 +389,10 @@ "TrialLicenseExpireMessage": "Deneme lisansını kullanıyorsunuz ve deneme lisansınızın süresi {0} tarihinde sona erecek.", "TryForFree": "Ücretsiz deneyin", "TrialLicenseExpiredInfo": "Deneme lisansınızın süresi doldu!", - "CommercialNewsletterConfirmationMessage": "Şartlar, Koşullar ve Gizlilik politikasını kabul ediyorum." + "CommercialNewsletterConfirmationMessage": "Şartlar, Koşullar ve Gizlilik politikasını kabul ediyorum.", + "RemoveCurrentUserFromOrganizationWarningMessage": "Organizasyondan kendinizi kaldırıyorsunuz. Artık bu organizasyonu yönetemeyeceksiniz, onaylıyor musunuz? ", + "RenewExistingOrganizationOrCreateNewOneMessage": "Aşağıdaki \"Şimdi Uzat\" buton(lar)una tıklayarak organizasyon(lar)ınızın lisansını yenileyebilir ve böylece lisans geçerlilik süresini 1 yıl uzatabilirsiniz. Ödemeye devam ederseniz, yeni bir organizasyonunuz olacak. Yeni bir organizasyonla devam etmek istiyor musunuz? ", + "ExtendNow": "Şimdi Uzat", + "ContinueWithNewOrganization": "Yeni bir organizasyon ile devam et" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json index e2b5bc6f4c..03284209fb 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "Sự khác biệt giữa gia hạn giấy phép và nâng cấp là gì?", "LicenseExtendUpgradeDiffExplanation": "Extending: By extending/renewing your license, you will continue to get premium support and get major updates for the modules and themes. Besides, you will be able to continue creating new projects. And you will still be able to use ABP Suite which speeds up your development.
Upgrading: By upgrading your license, you will promote to a higher license plan which will allow you to get additional benefits. See the license comparison table to check the differences between the license plans.On the other hand, when you upgrade, your license expiry date will not change!To extend your license end date, you need to extend your license.", "LicenseRenewalCost": "Chi phí gia hạn giấy phép sau 1 năm là bao nhiêu?", - "LicenseRenewalCostExplanation": "Tỷ lệ gia hạn (gia hạn) của tất cả các giấy phép vĩnh viễn của ABP Thương mại bằng {0} giá niêm yết của giấy phép. Giá gia hạn của Giấy phép nhóm tiêu chuẩn là $ {1}, Giấy phép Kinh doanh tiêu chuẩn là $ {2} và Giấy phép Doanh nghiệp tiêu chuẩn là $ {3}. Nếu bạn đã là khách hàng, hãy đăng nhập vào tài khoản của bạn để xem lại giá gia hạn hiện có.", + "LicenseRenewalCostExplanation": "Giá gia hạn (gia hạn) của Giấy phép nhóm tiêu chuẩn là $ {0}, Giấy phép kinh doanh tiêu chuẩn là $ {1} và Giấy phép doanh nghiệp tiêu chuẩn là $ {2}. Nếu bạn đã là khách hàng, hãy đăng nhập vào tài khoản của bạn để xem lại giá gia hạn hiện có.", "HowDoIRenewMyLicense": "Làm cách nào để gia hạn giấy phép của tôi?", "HowDoIRenewMyLicenseExplanation": "Bạn có thể gia hạn giấy phép của mình bằng cách điều hướng đến trang quản lý tổ chức . Để tận dụng mức giá Gia hạn sớm được chiết khấu của chúng tôi, hãy đảm bảo bạn gia hạn trước khi giấy phép hết hạn. Tuy nhiên, đừng lo lắng về việc không biết khi nào cơ hội Gia hạn sớm của bạn đóng lại. Bạn sẽ nhận được 3 e-mail nhắc nhở trước khi đăng ký của bạn hết hạn. Chúng tôi sẽ gửi chúng vào 30 ngày, 7 ngày và 1 ngày trước khi hết hạn.", "IsSourceCodeIncluded": "Giấy phép của tôi có bao gồm mã nguồn của các mô-đun và chủ đề thương mại không?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "Tôi có thể thay đổi các nhà phát triển đã đăng ký của tổ chức của mình trong tương lai không?", "ChangingDevelopersExplanation": "Ngoài việc thêm các nhà phát triển mới vào giấy phép của mình, bạn cũng có thể thay đổi các nhà phát triển hiện có (bạn có thể xóa một nhà phát triển và thêm một nhà phát triển mới vào cùng một chỗ ngồi) mà không phải trả thêm bất kỳ chi phí nào.", "WhatHappensWhenLicenseEnds": "Điều gì xảy ra khi thời hạn cấp phép của tôi kết thúc?", - "WhatHappensWhenLicenseEndsExplanation1": "Loại giấy phép thương mại của ABP là giấy phép vĩnh viễn . Sau khi giấy phép của bạn hết hạn, bạn có thể tiếp tục phát triển dự án của mình. Và bạn không có nghĩa vụ phải gia hạn giấy phép của mình. Giấy phép của bạn đi kèm với gói Cập nhật và Hỗ trợ kéo dài một năm. Để tiếp tục nhận được các tính năng mới, cải tiến hiệu suất, sửa lỗi, hỗ trợ và tiếp tục sử dụng ABP Suite, hãy đảm bảo gia hạn gói của bạn mỗi năm. Khi giấy phép của bạn hết hạn, bạn sẽ không thể nhận được nhiều lợi ích hơn sau đây;", + "WhatHappensWhenLicenseEndsExplanation1": "ABP Loại giấy phép thương mại là giấy phép vĩnh viễn . Sau khi giấy phép của bạn hết hạn, bạn có thể tiếp tục phát triển dự án của mình. Và bạn không có nghĩa vụ phải gia hạn giấy phép của mình. Giấy phép của bạn đi kèm với một bản cập nhật và kế hoạch hỗ trợ trong một năm. Để tiếp tục nhận các tính năng mới, cải tiến hiệu suất, sửa lỗi, hỗ trợ và tiếp tục sử dụng ABP Suite, bạn cần gia hạn giấy phép của mình. Khi giấy phép của bạn hết hạn, bạn sẽ không nhận được những lợi ích sau đây;", "WhatHappensWhenLicenseEndsExplanation2": "Bạn không thể tạo các giải pháp mới bằng ABP Commercial, nhưng bạn có thể tiếp tục phát triển các ứng dụng hiện có của mình mãi mãi.", - "WhatHappensWhenLicenseEndsExplanation3": "Bạn sẽ có thể nhận được các bản cập nhật cho các mô-đun và chủ đề trong phiên bản CHÍNH của bạn. Ví dụ; nếu bạn đang sử dụng v3.2.0 của một mô-đun, bạn vẫn có thể nhận được các bản cập nhật cho v3.x.x (v3.3.0, v3.5.2 ... v.v.) của mô-đun đó. Nhưng bạn không thể nhận các bản cập nhật cho phiên bản chính tiếp theo (như v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation3": "Bạn sẽ có thể nhận được bản cập nhật cho các mô-đun và chủ đề trong phiên bản CHÍNH của mình (ngoại trừ phiên bản RC hoặc Preview). Ví dụ; nếu bạn đang sử dụng v3.2.0 của một mô-đun, bạn vẫn có thể nhận được các bản cập nhật cho v3.x.x (v3.3.0, v3.5.2 ... v.v.) của mô-đun đó. Nhưng bạn không thể nhận các bản cập nhật cho phiên bản chính tiếp theo (như v4.x, v5.x). Ví dụ: khi giấy phép của bạn hết hạn, bản phát hành mới nhất là v4.4.3 và phiên bản xem trước mới nhất là v5.0.0-rc.2, bạn có thể truy cập v4.X.X nhưng không thể truy cập v5.X.X.", "WhatHappensWhenLicenseEndsExplanation4": "Bạn không thể cài đặt các mô-đun và chủ đề mới được thêm vào nền tảng ABP Commercial sau khi giấy phép của bạn hết hạn.", "WhatHappensWhenLicenseEndsExplanation5": "Bạn không thể sử dụng ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "Bạn không thể nhận được hỗ trợ cao cấp nữa.", - "WhatHappensWhenLicenseEndsExplanation7": "Bạn có thể gia hạn đăng ký của mình nếu bạn muốn tiếp tục nhận được những lợi ích này. Nếu bạn gia hạn đăng ký của mình trong vòng 1 tháng sau khi giấy phép hết hạn, bạn sẽ được giảm giá 20%.", + "WhatHappensWhenLicenseEndsExplanation7": "Bạn có thể gia hạn (gia hạn) giấy phép của mình nếu bạn muốn tiếp tục nhận được những lợi ích này. Nếu bạn gia hạn giấy phép của mình trong vòng 1 tháng sau khi giấy phép của bạn hết hạn, các chiết khấu sau sẽ được áp dụng: Giảm giá {0}% cho Giấy phép Nhóm, Giảm giá {1}% Giấy phép Kinh doanh, Giảm giá {2}% Giấy phép Doanh nghiệp .", "WhenShouldIRenewMyLicense": "Khi nào tôi nên gia hạn giấy phép của mình?", - "WhenShouldIRenewMyLicenseExplanation1": "Nếu bạn gia hạn giấy phép trong vòng 1 tháng sau khi giấy phép của bạn hết hạn, chiết khấu% 20 sẽ được áp dụng.", - "WhenShouldIRenewMyLicenseExplanation2": "Nếu bạn gia hạn giấy phép sau 1 tháng kể từ ngày giấy phép hết hạn, giá gia hạn sẽ giống như giá mua giấy phép và sẽ không có chiết khấu cho việc gia hạn của bạn.", + "WhenShouldIRenewMyLicenseExplanation": "Nếu bạn gia hạn giấy phép của mình trong vòng 1 tháng sau khi giấy phép của bạn hết hạn, các chiết khấu sau sẽ được áp dụng: Giảm giá {0}% cho Giấy phép Nhóm, Giảm giá {1}% Giấy phép Kinh doanh, Giảm giá {2}% Giấy phép Doanh nghiệp . Nếu bạn gia hạn giấy phép 1 tháng sau ngày giấy phép hết hạn, giá gia hạn sẽ giống như giá mua giấy phép và sẽ không có chiết khấu khi gia hạn của bạn.", "TrialPlan": "Bạn có kế hoạch dùng thử không?", "TrialPlanExplanation": "Hiện tại, ABP Commercial không có kế hoạch dùng thử. Đối với giấy phép Nhóm, chúng tôi cung cấp bảo đảm hoàn tiền trong 30 ngày. Bạn chỉ có thể yêu cầu hoàn lại tiền trong 30 ngày đầu tiên. Đối với giấy phép Doanh nghiệp và Doanh nghiệp, chúng tôi hoàn lại 60% trong 30 ngày. Điều này là do giấy phép Doanh nghiệp và Doanh nghiệp bao gồm mã nguồn đầy đủ của tất cả các mô-đun và chủ đề.", "DoYouAcceptBankWireTransfer": "Bạn có chấp nhận chuyển khoản ngân hàng không?", - "DoYouAcceptBankWireTransferExplanation": "Có, chúng tôi chấp nhận chuyển khoản ngân hàng.
Sau khi gửi phí cấp phép qua chuyển khoản ngân hàng, hãy gửi email cho chúng tôi theo địa chỉ info@abp.io biên lai của bạn và loại giấy phép được yêu cầu. Thông tin tài khoản ngân hàng quốc tế của chúng tôi:", + "DoYouAcceptBankWireTransferExplanation": "Có, chúng tôi chấp nhận chuyển khoản ngân hàng.
Sau khi gửi phí cấp phép qua chuyển khoản ngân hàng, hãy gửi email cho chúng tôi theo địa chỉ accounting@abp.io biên lai của bạn và loại giấy phép được yêu cầu. Thông tin tài khoản ngân hàng quốc tế của chúng tôi:", "HowToUpgrade": "Làm thế nào để nâng cấp các ứng dụng hiện có khi có phiên bản mới?", "HowToUpgradeExplanation1": "Khi bạn tạo một ứng dụng mới bằng ABP Commercial, tất cả các mô-đun và chủ đề được sử dụng dưới dạng gói NuGet và NPM. Vì vậy, bạn có thể dễ dàng nâng cấp các gói khi có phiên bản mới.", "HowToUpgradeExplanation2": "Ngoài các nâng cấp NuGet/NPM tiêu chuẩn, ABP CLI cung cấp lệnh cập nhật tự động tìm và nâng cấp tất cả các gói liên quan đến ABP trong giải pháp của bạn.", 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 2ceda42341..8b2818debc 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "许可扩展和升级有什么区别?", "LicenseExtendUpgradeDiffExplanation": "扩展: 通过扩展/更新许可,你将继续获得高级支持,并获得有关模块和主题的重大更新. 此外你将能够继续创建新项目. 而且你仍然可以使用ABP Suite来加快开发速度.
升级: 通过升级许可,你将升级到更高的许可计划,这将使你获得更多好处. 查看 许可比较表来检查许可计划之间的差异. 另一方面,当你升级时你的许可到期日期不会改变!要延长你的许可终止日期,你需要延长你的许可.", "LicenseRenewalCost": "一年后的许可续期费用是多少?", - "LicenseRenewalCostExplanation": "ABP商业版的续费价格均为原价的{0}. Team版本的续费价格是${1}, Business版本的续订价格是${2}, Enterprise版本的续订价格是${3}. 如果你已经购买了, 请登录帐户以查看续订价格.", + "LicenseRenewalCostExplanation": "标准 Team 许可证的续订(扩展)价格为 ${0},标准 Business License 为 ${1},标准 Enterprise License 为 ${2}。 如果您已经是客户,请登录您的帐户查看可用的续订价格。", "HowDoIRenewMyLicense": "如何续费我的许可证?", "HowDoIRenewMyLicenseExplanation": "你可以登录以进行续费. 为了享受我们的续费折扣, 请确保在许可证到期之前进行续费, 我们会在到期前7天和30天发送2封续费提醒邮件.", "IsSourceCodeIncluded": "我的许可是否包括商业模块和主题的源代码?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "我将来可以更改我组织的注册开发人员吗?", "ChangingDevelopersExplanation": "除了将新的开发人员添加到你的许可中之外,你还可以更改现有的开发人员(可以删除一个开发人员并将新的开发人员添加到同一位置),而无需任何额外费用.", "WhatHappensWhenLicenseEnds": "我的许可期限结束后会怎样?", - "WhatHappensWhenLicenseEndsExplanation1": "ABP商业版具有永久许可类型.许可到期后, 你可以继续开发你的项目.并且你没有义务续订许可.许可到期后;", + "WhatHappensWhenLicenseEndsExplanation1": "ABP 商业许可类型为永久许可。 许可证到期后,您可以继续开发您的项目。 而且您没有义务更新您的许可证。 您的许可证随附开箱即用的一年更新和支持计划。 要继续获得新功能、性能增强、错误修复、支持和继续使用 ABP Suite,您需要更新您的许可证。 当您的许可证到期时,您将无法获得以下好处;", "WhatHappensWhenLicenseEndsExplanation2": "你不能使用ABP商业版创建新的解决方案,但可以永远继续开发现有的应用程序.", - "WhatHappensWhenLicenseEndsExplanation3": "你能够在你的主版本中获得模块和主题的更新.例如;如果你使用的是模块的v3.2.0版本,你仍然可以获得v3.x.x版本的更新.(v3.3.0 v3.5.2…等等)的.但是你无法获得下一个主要版本(如v4.x,v5.x)的更新", + "WhatHappensWhenLicenseEndsExplanation3": "您将能够获得主要版本(RC 或预览版除外)中的模块和主题的更新。 例如; 如果您使用的是模块的 v3.2.0,您仍然可以获得该模块的 v3.x.x(v3.3.0、v3.5.2...等)的更新。 但是您无法获得下一个主要版本(如 v4.x、v5.x)的更新。 比如你的license过期了,最新版本是v4.4.3,最新预览版本是v5.0.0-rc.2,你可以访问v4.X.X,但是不能访问v5.X.X。", "WhatHappensWhenLicenseEndsExplanation4": "许可到期后,你无法安装添加到ABP商业平台的新模块和主题.", "WhatHappensWhenLicenseEndsExplanation5": "你不能使用ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "你不再获得高级支持.", - "WhatHappensWhenLicenseEndsExplanation7": "如果你想继续获得这些好处,可以继续续订. 续订时有20%的折扣.", + "WhatHappensWhenLicenseEndsExplanation7": "如果您想继续获得这些好处,您可以延长(续订)您的许可证。 如果您在许可证到期后 1 个月内延长许可证,将享受以下折扣:团队许可证 {0}% 折扣、商业许可证 {1}% 折扣、企业许可证 {2}% 折扣 .", "WhenShouldIRenewMyLicense": "我什么时候应该续订我的许可?", - "WhenShouldIRenewMyLicenseExplanation1": "如果你在许可到期后的 1个月内续订许可,则许可总价将享受%20的折扣.", - "WhenShouldIRenewMyLicenseExplanation2": "如果你在你的许可过期日期1个月后更新你的许可,更新价格将与许可购买价格相同,你的更新将没有折扣.", + "WhenShouldIRenewMyLicenseExplanation": "如果您在许可证到期后 1 个月内续订许可证,将享受以下折扣:团队许可证 {0}% 折扣、商业许可证 {1}% 折扣、企业许可证 {2}% 折扣 . 如果您在许可证到期后 1 个月续订许可证,续订价格将与许可证购买价格相同,并且续订不会有折扣。", "TrialPlan": "你们有试用计划吗?", "TrialPlanExplanation": "目前,ABP商业版还没有试用计划.对于团队许可,我们提供30天的退款保证.你可以在30天内要求退款.对于企业营业执照,我们提供30天内60%的退款.这是因为商业和企业许可包含所有模块和主题的完整源代码.", "DoYouAcceptBankWireTransfer": "你们接受银行电汇吗?", - "DoYouAcceptBankWireTransferExplanation": "是的,我们接受银行电汇.
通过银行电汇转账,通过电子邮件将收据和所要求的许可类型发送给我们.
我们的国际银行帐户信息:", + "DoYouAcceptBankWireTransferExplanation": "是的,我们接受银行电汇。
在通过银行转账发送许可费后,将您的收据和所需的许可类型通过电子邮件发送至accounting@abp.io。 我们的国际银行账户信息:", "HowToUpgrade": "可用新版本时如何升级现有应用程序?", "HowToUpgradeExplanation1": "使用ABP商业版创建新应用程序时,所有模块和主题均用作NuGet和NPM软件包. 因此,当有新版本可用时,你可以轻松升级软件包.", "HowToUpgradeExplanation2": "除了标准的NuGet/NPM升级之外, ABP CLI 还提供了一条更新命令,该命令可自动查找和升级解决方案中的所有与ABP相关的软件包.", 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 3979809a50..2cd3e79b5c 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json @@ -180,7 +180,7 @@ "LicenseExtendUpgradeDiff": "許可擴展和升級有什麽區別?", "LicenseExtendUpgradeDiffExplanation": "擴展: 通過擴展/更新許可,你將繼續獲得高級支持,並獲得有關模塊和主題的重大更新. 此外你將能夠繼續創建新項目. 而且你仍然可以使用ABP Suite來加快開發速度.
升級: 通過升級許可,你將升級到更高的許可計劃,這將使你獲得更多好處. 查看 許可比較表來檢查許可計劃之間的差異. 另一方面,當你升級時你的許可到期日期不會改變!要延長你的許可終止日期,你需要延長你的許可.", "LicenseRenewalCost": "一年後的許可續期費用是多少?", - "LicenseRenewalCostExplanation": "ABP商業版的續費價格均為原價的{0}. Team版本的續費價格是${1}, Business版本的續訂價格是${2}, Enterprise版本的續訂價格是${3}. 如果你已經購買了, 請登錄帳戶以查看續訂價格.", + "LicenseRenewalCostExplanation": "標準 Team 許可證的續訂(擴展)價格為 ${0},標準 Business License 為 ${1},標準 Enterprise License 為 ${2}。 如果您已經是客戶,請登錄您的帳戶查看可用的續訂價格。", "HowDoIRenewMyLicense": "如何續費我的許可證?", "HowDoIRenewMyLicenseExplanation": "你可以登錄以進行續費. 為了享受我們的續費折扣, 請確保在許可證到期之前進行續費, 我們會在到期前7天和30天發送2封續費提醒郵件.", "IsSourceCodeIncluded": "我的許可是否包括商業模塊和主題的源代碼?", @@ -191,20 +191,19 @@ "ChangingDevelopers": "我將來可以更改我組織的註冊開發人員嗎?", "ChangingDevelopersExplanation": "除了將新的開發人員添加到你的許可中之外,你還可以更改現有的開發人員(可以刪除一個開發人員並將新的開發人員添加到同一位置),而無需任何額外費用.", "WhatHappensWhenLicenseEnds": "我的許可期限結束後會怎樣?", - "WhatHappensWhenLicenseEndsExplanation1": "ABP商業版具有永久許可類型.許可到期後, 你可以繼續開發你的項目.並且你沒有義務續訂許可.許可到期後;", + "WhatHappensWhenLicenseEndsExplanation1": "ABP 商業許可類型為永久許可。 許可證到期後,您可以繼續開發您的項目。 而且您沒有義務更新您的許可證。 您的許可證隨附開箱即用的一年更新和支持計劃。 要繼續獲得新功能、性能增強、錯誤修復、支持和繼續使用 ABP Suite,您需要更新您的許可證。 當您的許可證到期時,您將無法獲得以下好處;", "WhatHappensWhenLicenseEndsExplanation2": "你不能使用ABP商業版創建新的解決方案,但可以永遠繼續開發現有的應用程序.", - "WhatHappensWhenLicenseEndsExplanation3": "你能夠在你的主版本中獲得模塊和主題的更新.例如;如果你使用的是模塊的v3.2.0版本,你仍然可以獲得v3.x.x版本的更新.(v3.3.0 v3.5.2…等等)的.但是你無法獲得下一個主要版本(如v4.x,v5.x)的更新", + "WhatHappensWhenLicenseEndsExplanation3": "您將能夠獲得主要版本(RC 或預覽版除外)中的模塊和主題的更新。 例如; 如果您使用的是模塊的 v3.2.0,您仍然可以獲得該模塊的 v3.x.x(v3.3.0、v3.5.2...等)的更新。 但是您無法獲得下一個主要版本(如 v4.x、v5.x)的更新。 比如你的license過期了,最新版本是v4.4.3,最新預覽版本是v5.0.0-rc.2,你可以訪問v4.X.X,但是不能訪問v5.X.X。", "WhatHappensWhenLicenseEndsExplanation4": "許可到期後,你無法安裝添加到ABP商業平臺的新模塊和主題.", "WhatHappensWhenLicenseEndsExplanation5": "你不能使用ABP Suite.", "WhatHappensWhenLicenseEndsExplanation6": "你不再獲得高級支持.", - "WhatHappensWhenLicenseEndsExplanation7": "如果你想繼續獲得這些好處,可以繼續續訂. 續訂時有20%的折扣.", + "WhatHappensWhenLicenseEndsExplanation7": "如果您想繼續獲得這些好處,您可以延長(續訂)您的許可證。 如果您在許可證到期後 1 個月內延長許可證,將適用以下折扣:團隊許可證 {0}% 折扣、商業許可證 {1}% 折扣、企業許可證 {2}% 折扣 .", "WhenShouldIRenewMyLicense": "我什麽時候應該續訂我的許可?", - "WhenShouldIRenewMyLicenseExplanation1": "如果你在许可到期后的 1个月内续订许可,则许可总价将享受%20的折扣.", - "WhenShouldIRenewMyLicenseExplanation2": "如果你在你的許可過期日期1個月後更新你的許可,更新價格將與許可購買價格相同,你的更新將沒有折扣.", + "WhenShouldIRenewMyLicenseExplanation": "如果您在許可證到期後 1 個月內續訂許可證,將享受以下折扣:團隊許可證 {0}% 折扣、商業許可證 {1}% 折扣、企業許可證 {2}% 折扣 . 如果您在許可證到期後 1 個月續訂許可證,續訂價格將與許可證購買價格相同,並且續訂不會有折扣。", "TrialPlan": "你們有試用計劃嗎?", "TrialPlanExplanation": "目前,ABP商業版還沒有試用計劃.對於團隊許可,我們提供30天的退款保證.你可以在30天內要求退款.對於企業營業執照,我們提供30天內60%的退款.這是因為商業和企業許可包含所有模塊和主題的完整源代碼.", "DoYouAcceptBankWireTransfer": "你們接受銀行電匯嗎?", - "DoYouAcceptBankWireTransferExplanation": "是的,我們接受銀行電匯.
通過銀行電匯轉賬,通過電子郵件將收據和所要求的許可類型發送給我們.
我們的國際銀行帳戶信息:", + "DoYouAcceptBankWireTransferExplanation": "是的,我們接受銀行電匯。
在通過銀行轉賬發送許可費後,將您的收據和所需的許可類型通過電子郵件發送至accounting@abp.io。 我們的國際銀行賬戶信息:", "HowToUpgrade": "可用新版本時如何升級現有應用程序?", "HowToUpgradeExplanation1": "使用ABP商業版創建新應用程序時,所有模塊和主題均用作NuGet和NPM軟件包. 因此,當有新版本可用時,你可以輕松升級軟件包.", "HowToUpgradeExplanation2": "除了標準的NuGet/NPM升級之外, ABP CLI 還提供了一條更新命令,該命令可自動查找和升級解決方案中的所有與ABP相關的軟件包.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json index 44d5d79aac..751b33613e 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "يجب عليك إدخال 3 أحرف على الأقل!", "Volo.AbpIo.Domain:060001": "عنوان URL المصدر (\"{ArticleUrl}\") ليس عنوان URL لـ Github", "Volo.AbpIo.Domain:060002": "محتوى المقالة غير متوفر من مورد Github (\"{ArticleUrl}\").", - "Volo.AbpIo.Domain:060003": "لم يتم العثور على محتوى مقال!" + "Volo.AbpIo.Domain:060003": "لم يتم العثور على محتوى مقال!", + "SeeMore": "شاهد المزيد" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de-DE.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de-DE.json index 9dc7da3144..f3da536e7e 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de-DE.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de-DE.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "Sie müssen mindestens 3 Zeichen eingeben!", "Volo.AbpIo.Domain:060001": "Quell-URL(\"{ArticleUrl}\") ist keine Github-URL", "Volo.AbpIo.Domain:060002": "Artikelinhalt ist über die Github(\"{ArticleUrl}\")-Ressource nicht verfügbar.", - "Volo.AbpIo.Domain:060003": "Kein Artikelinhalt gefunden!" + "Volo.AbpIo.Domain:060003": "Kein Artikelinhalt gefunden!", + "SeeMore": "Mehr Sehen" } } \ 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 652090d864..5925ffb172 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en-GB.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en-GB.json @@ -101,6 +101,7 @@ "ArticleRequestMessageTitle": "Open an issue on the GitHub to request an article/tutorial you want to see on this web site.", "ArticleRequestMessageBody": "Here is the list of the requested articles by the Community. Do you want to write a requested article? Please click on the request and join the discussion.", "Language": "Language", - "CreateArticleLanguageInfo": "The language in which the article is written" + "CreateArticleLanguageInfo": "Language of the article", + "SeeMore": "See More" } -} \ No newline at end of file +} diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json index 2da4a62481..16a86cac37 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "You must enter at least 3 characters!", "Volo.AbpIo.Domain:060001": "Source URL(\"{ArticleUrl}\") is not Github URL", "Volo.AbpIo.Domain:060002": "Article Content is not available from Github(\"{ArticleUrl}\") resource.", - "Volo.AbpIo.Domain:060003": "No article content found!" + "Volo.AbpIo.Domain:060003": "No article content found!", + "SeeMore": "See More" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json index 7b34e043a7..3066263e8b 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "¡Debes ingresar al menos 3 caracteres!", "Volo.AbpIo.Domain:060001": "La URL de origen (\"{ArticleUrl}\") no es la URL de Github", "Volo.AbpIo.Domain:060002": "El contenido del artículo no está disponible en el recurso de Github (\"{ArticleUrl}\").", - "Volo.AbpIo.Domain:060003": "¡No se encontró contenido del artículo!" + "Volo.AbpIo.Domain:060003": "¡No se encontró contenido del artículo!", + "SeeMore": "Ver Más" } } \ 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 2c09984774..435ac1ef92 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "Sinun on annettava vähintään 3 merkkiä!", "Volo.AbpIo.Domain:060001": "Lähteen URL-osoite (\"{ArticleUrl}\") ei ole Githubin URL-osoite", "Volo.AbpIo.Domain:060002": "Artikkelin sisältö ei ole saatavilla Githubin (\"{ArticleUrl}\") -resurssista.", - "Volo.AbpIo.Domain:060003": "Artikkelin sisältöä ei löytynyt!" + "Volo.AbpIo.Domain:060003": "Artikkelin sisältöä ei löytynyt!", + "SeeMore": "Katso Lisää" } } \ 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 42f239f987..cbeb8ae084 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "Vous devez saisir au moins 3 caractères!", "Volo.AbpIo.Domain:060001": "L'URL source (\"{ArticleUrl}\") n'est pas une URL Github", "Volo.AbpIo.Domain:060002": "Le contenu de l'article n'est pas disponible à partir de la ressource Github(\"{ArticleUrl}\").", - "Volo.AbpIo.Domain:060003": "Aucun contenu d'article trouvé !" + "Volo.AbpIo.Domain:060003": "Aucun contenu d'article trouvé !", + "SeeMore": "Voir Plus" } } \ 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 aa351262ca..1e8d5a9e9f 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "आपको कम से कम 3 वर्ण दर्ज करने होंगे!", "Volo.AbpIo.Domain:060001": "स्रोत URL (\"{ArticleUrl}\") जीथब URL नहीं है", "Volo.AbpIo.Domain:060002": "लेख सामग्री Github (\"{ArticleUrl}\") संसाधन से उपलब्ध नहीं है।", - "Volo.AbpIo.Domain:060003": "कोई लेख सामग्री नहीं मिली!" + "Volo.AbpIo.Domain:060003": "कोई लेख सामग्री नहीं मिली!", + "SeeMore": "और देखें" } } \ 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 fd128eb596..7c8f3f2fba 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "Þú verður að slá inn að minnsta kosti 3 stafi!", "Volo.AbpIo.Domain:060001": "Upprunaslóð (\"{ArticleUrl} \") er ekki Github slóð", "Volo.AbpIo.Domain:060002": "Innihald greinar er ekki fáanlegt frá Github (\"{ArticleUrl} \") resoursum.", - "Volo.AbpIo.Domain:060003": "Innihald greinar fannst ekki!" + "Volo.AbpIo.Domain:060003": "Innihald greinar fannst ekki!", + "SeeMore": "Sjá Meira" } } \ 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 f104045682..1b305d9b4a 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "Devi inserire almeno 3 caratteri!", "Volo.AbpIo.Domain:060001": "Source URL(\"{ArticleUrl}\") non è un URL di GitHub", "Volo.AbpIo.Domain:060002": "Il contenuto dell'articolo non è disponibile dalla risorsa Github(\"{ArticleUrl}\").", - "Volo.AbpIo.Domain:060003": "Nessun contenuto dell'articolo trovato!" + "Volo.AbpIo.Domain:060003": "Nessun contenuto dell'articolo trovato!", + "SeeMore": "Vedi Altro" } } \ 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 4c78f15d99..a15a1bc102 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "Trebuie să introduceţi cel putin 3 caractere!", "Volo.AbpIo.Domain:060001": "Sursa URL(\"{ArticleUrl}\") nu este URL GitHub", "Volo.AbpIo.Domain:060002": "Conţinutul articolului nu este disponibil din resursa de pe GitHub(\"{ArticleUrl}\").", - "Volo.AbpIo.Domain:060003": "Nu a fost găsit conţinutul articolului!" + "Volo.AbpIo.Domain:060003": "Nu a fost găsit conţinutul articolului!", + "SeeMore": "Vezi mai mult" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json index e01e201969..3a2b2e5f1d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "Musíte zadať aspoň 3 znaky!", "Volo.AbpIo.Domain:060001": "Zdrojová URL(\"{ArticleUrl}\") nie je URL Githubu", "Volo.AbpIo.Domain:060002": "Obsah článku nie je dostupný v Github zdroji(\"{ArticleUrl}\").", - "Volo.AbpIo.Domain:060003": "Nenašiel sa žiadny obsah článku!" + "Volo.AbpIo.Domain:060003": "Nenašiel sa žiadny obsah článku!", + "SeeMore": "Vidět Ví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 f504842e70..15dd748d6e 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json @@ -142,6 +142,7 @@ "MinimumSearchContent": "您必须输入至少 3 个字符!", "Volo.AbpIo.Domain:060001": "源 URL(\"{ArticleUrl}\") 不是 Github URL", "Volo.AbpIo.Domain:060002": "文章内容无法从 Github(\"{ArticleUrl}\") 资源中获得。", - "Volo.AbpIo.Domain:060003": "没有找到文章内容!" + "Volo.AbpIo.Domain:060003": "没有找到文章内容!", + "SeeMore": "查看更多" } } \ No newline at end of file diff --git a/codecov.yml b/codecov.yml index 7ad4672daf..a2bd04f1d8 100644 --- a/codecov.yml +++ b/codecov.yml @@ -2,3 +2,7 @@ codecov: branch: dev require_ci_to_pass: yes allow_coverage_offsets: true + status: + project: + default: + threshold: 1% diff --git a/common.props b/common.props index a343f7cd04..4eb29c0b8e 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ latest - 5.0.1 + 5.1.0 $(NoWarn);CS1591;CS0436 https://abp.io/assets/abp_nupkg.png https://abp.io/ diff --git a/docs/en/Blog-Posts/2021-11-18 v5_0_Preview/POST.md b/docs/en/Blog-Posts/2021-11-18 v5_0_Preview/POST.md index e1bc93e23a..32ab4684f2 100644 --- a/docs/en/Blog-Posts/2021-11-18 v5_0_Preview/POST.md +++ b/docs/en/Blog-Posts/2021-11-18 v5_0_Preview/POST.md @@ -127,13 +127,7 @@ First of all, you need to have EF Core or MongoDB installed into your solution. #### Install the packages -Install the new [Volo.Abp.EventBus.Boxes](https://www.nuget.org/packages/Volo.Abp.EventBus.Boxes) NuGet package to your database layer (to `EntityFrameworkCore` or `MongoDB` project) or to the host application. Open a command-line terminal at the root directory of your database (or host) project and execute the following command: - -````csharp -abp add-package Volo.Abp.EventBus.Boxes -```` - -This will install the package and setup the ABP module dependency. This package depends on [DistributedLock.Core](https://www.nuget.org/packages/DistributedLock.Core) library which provides a distributed locking system for concurrency control in a distributed environment. There are [many distributed lock providers](https://github.com/madelson/DistributedLock#implementations), including Redis, SqlServer and ZooKeeper. You can use the one you like. Here, I will show the Redis provider. +For the outbox & inbox functionality, ABP depends on [DistributedLock.Core](https://www.nuget.org/packages/DistributedLock.Core) library which provides a distributed locking system for concurrency control in a distributed environment. There are [many distributed lock providers](https://github.com/madelson/DistributedLock#implementations), including Redis, SqlServer and ZooKeeper. You can use the one you like. Here, I will show the Redis provider. Add [DistributedLock.Redis](https://www.nuget.org/packages/DistributedLock.Redis) NuGet package to your project, then add the following code into the ConfigureService method of your ABP module class: @@ -285,6 +279,16 @@ This can be a breaking change in rare cases (for example, if you create host sid ## Community News +### ABP Community Talks 2021.12 + +![community-talks](community-talks.png) + +As the core ABP development team, we've decided to organize monthly live meetings with the ABP community. The first live meeting will be at **December 16, 2021, 17:00 (UTC)** on YouTube. ABP core team members will present some of the new features coming with ABP 5.0. + +**Join this event on the Kommunity platform: https://kommunity.com/volosoft/events/abp-community-talks-4afca9c9** + +You can also [subscribe to the Volosoft channel](https://www.youtube.com/channel/UCO3XKlpvq8CA5MQNVS6b3dQ) for reminders for further ABP events and videos. + ### ABP was on ASP.NET Community Startup! It was great for us to be invited to Microsoft's [ASP.NET Community Weekly Standup](https://dotnet.microsoft.com/live/community-standup) show, at September 28. There was a very high attention and that made us very happy. Thanks to the ABP Community and all the watchers :) If you've missed the talk, [you can watch it here](https://www.youtube.com/watch?v=vMWM-_ihjwM). diff --git a/docs/en/Blog-Posts/2021-11-18 v5_0_Preview/community-talks.png b/docs/en/Blog-Posts/2021-11-18 v5_0_Preview/community-talks.png new file mode 100644 index 0000000000..4056d1afb8 Binary files /dev/null and b/docs/en/Blog-Posts/2021-11-18 v5_0_Preview/community-talks.png differ diff --git a/docs/en/Blog-Posts/2021-11-18 v5_0_Preview/cover-50.png b/docs/en/Blog-Posts/2021-11-18 v5_0_Preview/cover-50.png new file mode 100644 index 0000000000..bd343e70b0 Binary files /dev/null and b/docs/en/Blog-Posts/2021-11-18 v5_0_Preview/cover-50.png differ diff --git a/docs/en/Blog-Posts/2021-12-15 v5_0_Release_Stable/POST.md b/docs/en/Blog-Posts/2021-12-15 v5_0_Release_Stable/POST.md new file mode 100644 index 0000000000..329db94af3 --- /dev/null +++ b/docs/en/Blog-Posts/2021-12-15 v5_0_Release_Stable/POST.md @@ -0,0 +1,47 @@ +# ABP.IO Platform 5.0 Final Has Been Released! + +[ABP Framework](https://abp.io/) and [ABP Commercial](https://commercial.abp.io/) 5.0 versions have been released today. + +## What's new with 5.0? + +Since all the new features are already explained in details with the [5.0 RC.1 Announcement Post](https://blog.abp.io/abp/ABP-IO-Platform-5.0-RC-1-Has-Been-Released), I will not repeat all the details again. See the [RC Blog Post](https://blog.abp.io/abp/ABP-IO-Platform-5.0-RC-1-Has-Been-Released) for all the features and enhancements. + +## Getting started with 5.0 + +### Creating new solutions + +You can create a new solution with the ABP Framework version 5.0 by either using the `abp new` command or using the **direct download** tab on the [get started page](https://abp.io/get-started). + +Type the following command in a command-line terminal to install the ABP CLI version 5.0: + +````bash +dotnet tool install -g Volo.Abp.Cli --version 5.0.0 +```` + +To upgrade your existing ABP CLI installation: + +````bash +dotnet tool update -g Volo.Abp.Cli --version 5.0.0 +```` + +Then you can create a new solution using the `abp new` command: + +````bash +abp new Acme.BookStore +```` + +> See the [getting started document](https://docs.abp.io/en/abp/latest/Getting-Started) for details. + +### Upgrading existing solutions + +Check [the migration guide](https://docs.abp.io/en/abp/latest/Migration-Guides/Abp-5_0) for the applications with the version 4.x upgrading to the version 5.0. Also see [the upgrading guide](https://docs.abp.io/en/abp/latest/Upgrading) to understand how to update existing solutions. + +## ABP Community Talks 2021.12 + +![community-talks](community-talks.png) + +As the core ABP development team, we've decided to organize monthly live meetings with the ABP community. The first live meeting will be at **December 16, 2021, 17:00 (UTC)** on YouTube. ABP core team members will present some of the new features coming with ABP 5.0. + +**Join this event on the Kommunity platform: https://kommunity.com/volosoft/events/abp-community-talks-4afca9c9** + +See you in the event! diff --git a/docs/en/Blog-Posts/2021-12-15 v5_0_Release_Stable/community-talks.png b/docs/en/Blog-Posts/2021-12-15 v5_0_Release_Stable/community-talks.png new file mode 100644 index 0000000000..4056d1afb8 Binary files /dev/null and b/docs/en/Blog-Posts/2021-12-15 v5_0_Release_Stable/community-talks.png differ diff --git a/docs/en/Blog-Posts/2022-01-11 v5_1_Preview/POST.md b/docs/en/Blog-Posts/2022-01-11 v5_1_Preview/POST.md new file mode 100644 index 0000000000..f223a67f74 --- /dev/null +++ b/docs/en/Blog-Posts/2022-01-11 v5_1_Preview/POST.md @@ -0,0 +1,94 @@ +# ABP.IO Platform 5.1 RC.1 Has Been Released + +Today, we are releasing the [ABP Framework](https://abp.io/) and the [ABP Commercial](https://commercial.abp.io/) version **5.1 RC** (Release Candidate). This blog post introduces the new features and important changes in this new version. + +> **The planned release date for the [5.1 stable](https://github.com/abpframework/abp/milestone/64) version is February 8, 2022**. + +Please try this version and provide feedback for a more stable ABP version 5.1! Thank you all. + +## Get Started with the 5.1 RC + +follow the steps below to try the version 5.1 RC today; + +1) **Upgrade** the ABP CLI to the version `5.1.0-rc.1` using a command line terminal: + +````bash +dotnet tool update Volo.Abp.Cli -g --version 5.1.0-rc.1 +```` + +**or install** if you haven't installed before: + +````bash +dotnet tool install Volo.Abp.Cli -g --version 5.1.0-rc.1 +```` + +2) Create a **new application** with the `--preview` option: + +````bash +abp new BookStore --preview +```` + +See the [ABP CLI documentation](https://docs.abp.io/en/abp/latest/CLI) for all the available options. + +> You can also use the *Direct Download* tab on the [Get Started](https://abp.io/get-started) page by selecting the **Preview checkbox**. + +You can use any IDE that supports .NET 6.0 development (e.g. [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/)). + +### Migration Notes & Breaking Changes + +TODO: Startup template changes + +## What's new with 5.1? + +In this section, I will introduce some major features released with this version. + +### DRAFT list of things done + +#### ABP Framework + +* Use new hosting model for the app template. [#10928](https://github.com/abpframework/abp/pull/10928) - what to do for existing solutions? +* Async application initialization methods on the AbpModule class [#6828](https://github.com/abpframework/abp/issues/6828) +* Angular 13? +* [#10552](https://github.com/abpframework/abp/pull/10696) Used file scoped namespaces :) +* Support markdown in CMS-Kit comments [#10792](https://github.com/abpframework/abp/pull/10792) +* eShopOnAbp is getting mature +* Nightly builds are working again +* Coming: New ABP.IO design! + +All issues & PRs in [5.1 milesone](https://github.com/abpframework/abp/milestone/60?closed=1). + +#### ABP Commercial + +* CMS Kit Pro: URL forwarding +* LeptonX is coming for ABP Blazor and MVC UI too (share the sample dashboard) + +### Header 1 + +TODO + +## Community News + +### ABP Community Talks 2021.1 + +![abp-community-talks-2022-1](abp-community-talks-2022-1.png) + +This is the second episode of the ABP Community Talks and we are talking about microservice development with the ABP Framework, based on the [eShopOnAbp](https://github.com/abpframework/eShopOnAbp) reference solution. This live meeting will be at **January 20, 2022, 17:00 (UTC)** on YouTube. + +**Join this event on the Kommunity platform: https://kommunity.com/volosoft/events/abp-community-talks-20221-microservice-development-acd0f44b** + +You can also [subscribe to the Volosoft channel](https://www.youtube.com/channel/UCO3XKlpvq8CA5MQNVS6b3dQ) for reminders for further ABP events and videos. + +### New ABP Community posts + +Here, some of the recent posts added to the [ABP community](https://community.abp.io/): + +* [Minimal API development with the ABP Framework](https://community.abp.io/articles/minimal-api-with-abp-hello-world-part-1-sg5i44p8) by [@antosubash](https://github.com/antosubash) (three parts, video tutorial). +* [Integrating the Syncfusion MVC Components to the ABP MVC UI](https://community.abp.io/articles/integrating-the-syncfusion-mvc-components-to-the-abp-mvc-ui-0gpkr1if) by [@EngincanV](https://github.com/EngincanV). +* [Add Tailwind CSS to your ABP Blazor UI](https://community.abp.io/articles/add-tailwindcss-to-your-abp-blazor-ui-vidiwzcy) by [@antosubash](https://github.com/antosubash) (video tutorial). +* [Import external users into the users Table from an ABP Framework application](https://community.abp.io/articles/import-external-users-into-the-users-table-from-an-abp-framework-application-7lnyw415) by [@bartvanhoey](https://github.com/bartvanhoey). + +Thanks to the ABP Community for all the contents they have published. You can also [post your ABP and .NET related (text or video) contents](https://community.abp.io/articles/submit) to the ABP Community. + +## Conclusion + +In this blog post, I summarized the news about that new version and the ABP Community. Please try it and provide feedback by opening issues on [the GitHub repository](https://github.com/abpframework/abp). Thank you all! diff --git a/docs/en/Blog-Posts/2022-01-11 v5_1_Preview/abp-community-talks-2022-1.png b/docs/en/Blog-Posts/2022-01-11 v5_1_Preview/abp-community-talks-2022-1.png new file mode 100644 index 0000000000..92a137e5ac Binary files /dev/null and b/docs/en/Blog-Posts/2022-01-11 v5_1_Preview/abp-community-talks-2022-1.png differ diff --git a/docs/en/CLI.md b/docs/en/CLI.md index b8f69b1ee4..7db562a66e 100644 --- a/docs/en/CLI.md +++ b/docs/en/CLI.md @@ -29,6 +29,7 @@ Here, the list of all available commands before explaining their details: * **`help`**: Shows help on the usage of the ABP CLI. * **`new`**: Generates a new solution based on the ABP [startup templates](Startup-Templates/Index.md). * **`update`**: Automatically updates all ABP related NuGet and NPM packages in a solution. +* **`clean`**: Deletes all `BIN` and `OBJ` folders in the current folder. * **`add-package`**: Adds an ABP package to a project. * **`add-module`**: Adds a [multi-package application module](https://docs.abp.io/en/abp/latest/Modules/Index) to a solution. * **`list-modules`**: Lists names of open-source application modules. @@ -145,6 +146,17 @@ abp update [options] * `--check-all`: Check the new version of each package separately. Default is `false`. * `--version` or `-v`: Specifies the version to use for update. If not specified, latest version is used. +### clean + +Deletes all `BIN` and `OBJ` folders in the current folder. + +Usage: + +````bash +abp clean +```` + + ### add-package Adds an ABP package to a project by, diff --git a/docs/en/Community-Articles/2021-06-17-Using-Elsa-Workflow-with-ABP-Framework/POST.md b/docs/en/Community-Articles/2021-06-17-Using-Elsa-Workflow-with-ABP-Framework/POST.md index 1a07d744e7..5880406ffb 100644 --- a/docs/en/Community-Articles/2021-06-17-Using-Elsa-Workflow-with-ABP-Framework/POST.md +++ b/docs/en/Community-Articles/2021-06-17-Using-Elsa-Workflow-with-ABP-Framework/POST.md @@ -241,8 +241,9 @@ private void ConfigureElsa(ServiceConfigurationContext context, IConfiguration c .WithExposedHeaders("Content-Disposition")) ); - //register controllers inside elsa - context.Services.AddAssemblyOf(); + //Uncomment the below line if your abp version is lower than v4.4 to register controllers of Elsa . + //See https://github.com/abpframework/abp/pull/9299 (we will no longer need to specify this line of code from v4.4) + // context.Services.AddAssemblyOf(); //Disable antiforgery validation for elsa Configure(options => @@ -269,11 +270,7 @@ public override void OnApplicationInitialization(ApplicationInitializationContex } ``` -* In here we've specified the Elsa Server Api's assembly by using the `AddAssemblyOf<>` extension method to register the required services (controllers). These services required for the dashboard (if we create a workflow by using **Elsa Workflow Designer** it calls some services under the hook, therefore we need to be assured about these services get registered). - -* With [v4.4](https://github.com/abpframework/abp/pull/9299), we will no longer need to specify this line of code. - -> **Note:** `AddAssemblyOf<>` extension method can help you to register all your services by convention. You can check [here](https://docs.abp.io/en/abp/latest/Dependency-Injection#conventional-registration) for more information about conventional registration. +* These services required for the dashboard. * We don't need to register our workflows one by one anymore. Because now we use `.AddWorkflowsFrom()`, and this registers workflows on our behalf. @@ -371,12 +368,14 @@ namespace ElsaDemo.Permissions - + - + + + ``` diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/POST.md b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/POST.md new file mode 100644 index 0000000000..6abf984298 --- /dev/null +++ b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/POST.md @@ -0,0 +1,1671 @@ +# Many to Many Relationship with ABP and EF Core + +## Introduction + +In this article, we'll create a **BookStore** application like in [the ABP tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF) and add an extra `Category` feature to demonstrate how we can manage the many-to-many relationship with ABP-based applications (by following DDD rules). + +You can see the ER Diagram of our application below. This diagram will be helpful for us to demonstrate the relations between our entities. + +![ER-Diagram](./er-diagram.png) + +When we've examined the ER Diagram, we can see the one-to-many relationship between the **Author** and the **Book** tables. Also, the many-to-many relationship (**BookCategory** table) between the **Book** and the **Category** tables. (There can be more than one category on each book and vice-versa in our scenario). + +### Source Code + +You can find the source code of the application at https://github.com/EngincanV/ABP-Many-to-Many-Relationship-Demo . + +### Demo of the Final Application + +At the end of this article, we will have created an application same as in the gif below. + +![Demo of The Final Application](./application-final-demo.gif) + +## Creating the Solution + +In this article, we will create a new startup template with EF Core as a database provider and MVC for UI framework. + +* We can create a new startup template by using the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI): + +```bash +abp new BookStore -t app --version 5.0.0-beta.2 +``` + +* Our project boilerplate will be ready after the download is finished. Then, we can open the solution and start developing. + +## Starting the Development + +Let's start with creating our Domain Entities. + +### Step 1 - (Creating the Domain Entities) + +We can create a folder-structure under the `BookStore.Domain` project like in the image below. + +![Domain-Layer-Folder-Structure](./domain-file-structure.png) + +Open the entity classes and add the following codes to each of these classes. + +* **Author.cs** + +```csharp +using System; +using JetBrains.Annotations; +using Volo.Abp; +using Volo.Abp.Domain.Entities.Auditing; + +namespace BookStore.Authors +{ + public class Author : FullAuditedAggregateRoot + { + public string Name { get; private set; } + + public DateTime BirthDate { get; set; } + + public string ShortBio { get; set; } + + /* This constructor is for deserialization / ORM purpose */ + private Author() + { + } + + public Author(Guid id, [NotNull] string name, DateTime birthDate, [CanBeNull] string shortBio = null) + : base(id) + { + SetName(name); + BirthDate = birthDate; + ShortBio = shortBio; + } + + public void SetName([NotNull] string name) + { + Name = Check.NotNullOrWhiteSpace( + name, + nameof(name), + maxLength: AuthorConsts.MaxNameLength + ); + } + } +} +``` + +> We'll create the `AuthorConsts` class later in this step. + +* **Book.cs** + +```csharp +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Volo.Abp; +using Volo.Abp.Domain.Entities.Auditing; + +namespace BookStore.Books +{ + public class Book : FullAuditedAggregateRoot + { + public Guid AuthorId { get; set; } + + public string Name { get; private set; } + + public DateTime PublishDate { get; set; } + + public float Price { get; set; } + + public ICollection Categories { get; private set; } + + private Book() + { + } + + public Book(Guid id, Guid authorId, string name, DateTime publishDate, float price) + : base(id) + { + AuthorId = authorId; + SetName(name); + PublishDate = publishDate; + Price = price; + + Categories = new Collection(); + } + + public void SetName(string name) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name), BookConsts.MaxNameLength); + } + + public void AddCategory(Guid categoryId) + { + Check.NotNull(categoryId, nameof(categoryId)); + + if (IsInCategory(categoryId)) + { + return; + } + + Categories.Add(new BookCategory(bookId: Id, categoryId)); + } + + public void RemoveCategory(Guid categoryId) + { + Check.NotNull(categoryId, nameof(categoryId)); + + if (!IsInCategory(categoryId)) + { + return; + } + + Categories.RemoveAll(x => x.CategoryId == categoryId); + } + + public void RemoveAllCategoriesExceptGivenIds(List categoryIds) + { + Check.NotNullOrEmpty(categoryIds, nameof(categoryIds)); + + Categories.RemoveAll(x => !categoryIds.Contains(x.CategoryId)); + } + + public void RemoveAllCategories() + { + Categories.RemoveAll(x => x.BookId == Id); + } + + private bool IsInCategory(Guid categoryId) + { + return Categories.Any(x => x.CategoryId == categoryId); + } + } +} +``` + +* In our scenario, a book can have more than one category and a category can have more than one book so we need to create a many-to-many relationship between them. + +* For achieving this, we will create a **join entity** named `BookCategory`, and this class will simply have variables named `BookId` and `CategoryId`. + +* To manage this **join entity**, we can add it as a sub-collection to the **Book** entity, as we do above. We add this sub-collection +to **Book** class instead of **Category** class, because a book can have tens (or mostly hundreds) of categories but on the other perspective a category can have more than a hundred (or even way much) books inside of it. + +* It is a significant performance problem to load thousands of items whenever you query a category. Therefore it makes much more sense to add that sub-collection to the `Book` entity. + +> Don't forget: **Aggregate is a pattern in Domain-Driven Design. A DDD aggregate is a cluster of domain objects that can be treated as a single unit.** (See the full [description](https://martinfowler.com/bliki/DDD_Aggregate.html)) + +* Notice that, `BookCategory` is not an **Aggregate Root** so we are not violating one of the base rules about Aggregate Root (Rule: **"Reference Other Aggregates Only by ID"**). + +* If we examine the methods in the `Book` class (such as **RemoveAllCategories**, **RemoveAllCategoriesExceptGivenIds** and **AddCategory**) we will manage our sub-collection `Categories` (**BookCategory** - join table/entity) through them. (Adds or removes categories for books) + +> We'll create the `BookCategory` and `BookConsts` classes later in this step. + +* **BookCategory.cs** + +```csharp +using System; +using Volo.Abp.Domain.Entities; + +namespace BookStore.Books +{ + public class BookCategory : Entity + { + public Guid BookId { get; protected set; } + + public Guid CategoryId { get; protected set; } + + /* This constructor is for deserialization / ORM purpose */ + private BookCategory() + { + } + + public BookCategory(Guid bookId, Guid categoryId) + { + BookId = bookId; + CategoryId = categoryId; + } + + public override object[] GetKeys() + { + return new object[] {BookId, CategoryId}; + } + } +} +``` + +* Here, as you can notice, we've defined the `BookCategory` as the **Join Table/Entity** for our many-to-many relationship and ensured the required properties (BookId and CategoryId) were set in the constructor method of this class to create this object. + +* And also we've derived this class from the `Entity` class and therefore we've had to override the **GetKeys** method of this class to define the **Composite Key**. + +> The composite key is composed of `BookId` and `CategoryId` in our case. And they are unique together. + +> For more information about **Entities with Composite Keys**, you can read the relevant section from [Entities documentation](https://docs.abp.io/en/abp/latest/Entities#entities-with-composite-keys). + +* **BookManager.cs** + +```csharp +using System; +using System.Linq; +using System.Threading.Tasks; +using BookStore.Categories; +using JetBrains.Annotations; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Domain.Services; + +namespace BookStore.Books +{ + public class BookManager : DomainService + { + private readonly IBookRepository _bookRepository; + private readonly IRepository _categoryRepository; + + public BookManager(IBookRepository bookRepository, IRepository categoryRepository) + { + _bookRepository = bookRepository; + _categoryRepository = categoryRepository; + } + + public async Task CreateAsync(Guid authorId, string name, DateTime publishDate, float price, [CanBeNull]string[] categoryNames) + { + var book = new Book(GuidGenerator.Create(), authorId, name, publishDate, price); + + await SetCategoriesAsync(book, categoryNames); + + await _bookRepository.InsertAsync(book); + } + + public async Task UpdateAsync( + Book book, + Guid authorId, + string name, + DateTime publishDate, + float price, + [CanBeNull] string[] categoryNames + ) + { + book.AuthorId = authorId; + book.SetName(name); + book.PublishDate = publishDate; + book.Price = price; + + await SetCategoriesAsync(book, categoryNames); + + await _bookRepository.UpdateAsync(book); + } + + private async Task SetCategoriesAsync(Book book, [CanBeNull] string[] categoryNames) + { + if (categoryNames == null || !categoryNames.Any()) + { + book.RemoveAllCategories(); + return; + } + + var query = (await _categoryRepository.GetQueryableAsync()) + .Where(x => categoryNames.Contains(x.Name)) + .Select(x => x.Id) + .Distinct(); + + var categoryIds = await AsyncExecuter.ToListAsync(query); + if (!categoryIds.Any()) + { + return; + } + + book.RemoveAllCategoriesExceptGivenIds(categoryIds); + + foreach (var categoryId in categoryIds) + { + book.AddCategory(categoryId); + } + } + } +} +``` + +* If we examine the codes in the `BookManager` class, we can see that we've managed the `BookCategory` class (our join table/entity) by using some methods that we've defined in the `Book` class such as **RemoveAllCategories**, **RemoveAllCategoriesExceptGivenIds** and **AddCategory**. + +* These methods basically add or remove categories related to the book by conditions. + +* In the `CreateAsync` method, if the category names are specified, we'll retrieve their ids from the database and by using the **AddCategory** method that we've defined in the `Book` class, we'll add them. + +* In the `UpdateAsync` method, the same logic is also valid. But in this case, the user might want to remove some categories from books, so if the user sends us an empty **categoryNames** array, we remove all categories from the book he wants to update. If the user sends us some category names, we remove the excluded ones and add the new ones according to the **categoryNames** array. + +* **BookWithDetails.cs** + +```csharp +using System; +using Volo.Abp.Auditing; + +namespace BookStore.Books +{ + public class BookWithDetails : IHasCreationTime + { + public Guid Id { get; set; } + + public string Name { get; set; } + + public DateTime PublishDate { get; set; } + + public float Price { get; set; } + + public string AuthorName { get; set; } + + public string[] CategoryNames { get; set; } + + public DateTime CreationTime { get; set; } + } +} +``` + +We will use this class to retrieve books with their sub-categories and author names. + +* **IBookRepository.cs** + +```csharp +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace BookStore.Books +{ + public interface IBookRepository : IRepository + { + Task> GetListAsync( + string sorting, + int skipCount, + int maxResultCount, + CancellationToken cancellationToken = default + ); + + Task GetAsync(Guid id, CancellationToken cancellationToken = default); + } +} +``` + +We need to create two methods named **GetListAsync** and **GetAsync** and specify their return type as `BookWithDetails`. So by implementing these methods, we will return the book/books by their details (author name and categories). + +* **Category.cs** + +```csharp +using System; +using Volo.Abp; +using Volo.Abp.Domain.Entities.Auditing; + +namespace BookStore.Categories +{ + public class Category : AuditedAggregateRoot + { + public string Name { get; private set; } + + /* This constructor is for deserialization / ORM purpose */ + private Category() + { + } + + public Category(Guid id, string name) : base(id) + { + SetName(name); + } + + public Category SetName(string name) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name), CategoryConsts.MaxNameLength); + return this; + } + } +} +``` + +After defining our entities we can seed initial data to our database by using the [Data Seeding](https://docs.abp.io/en/abp/5.0/Data-Seeding#data-seeding) system of the ABP framework. We will create initial data for both the `Category` and `Author` entities because we will not create CRUD pages for these entities. + +> We will create only CRUD pages for the Book entity therefore we don't need to add initial data for the Book entity. We can create a new book by using the create modal of the Book page. (We will create it in the sixth step.) + +Create a class named `BookStoreDataSeederContributor` in your `*.Domain` project and update with the following code: + +* **BookStoreDataSeederContributor.cs** + +```csharp +using System; +using System.Threading.Tasks; +using BookStore.Authors; +using BookStore.Categories; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; + +namespace BookStore +{ + public class BookStoreDataSeederContributor : IDataSeedContributor, ITransientDependency + { + private readonly IGuidGenerator _guidGenerator; + private readonly IRepository _categoryRepository; + private readonly IRepository _authorRepository; + + public BookStoreDataSeederContributor( + IGuidGenerator guidGenerator, + IRepository categoryRepository, + IRepository authorRepository + ) + { + _guidGenerator = guidGenerator; + _categoryRepository = categoryRepository; + _authorRepository = authorRepository; + } + + public async Task SeedAsync(DataSeedContext context) + { + await SeedCategoriesAsync(); + await SeedAuthorsAsync(); + } + + private async Task SeedCategoriesAsync() + { + if (await _categoryRepository.GetCountAsync() <= 0) + { + await _categoryRepository.InsertAsync( + new Category(_guidGenerator.Create(), "History") + ); + + await _categoryRepository.InsertAsync( + new Category(_guidGenerator.Create(), "Unknown") + ); + + await _categoryRepository.InsertAsync( + new Category(_guidGenerator.Create(), "Adventure") + ); + + await _categoryRepository.InsertAsync( + new Category(_guidGenerator.Create(), "Action") + ); + + await _categoryRepository.InsertAsync( + new Category(_guidGenerator.Create(), "Crime") + ); + + await _categoryRepository.InsertAsync( + new Category(_guidGenerator.Create(), "Dystopia") + ); + } + } + + private async Task SeedAuthorsAsync() + { + if (await _authorRepository.GetCountAsync() <= 0) + { + await _authorRepository.InsertAsync( + new Author( + _guidGenerator.Create(), + "George Orwell", + new DateTime(1903, 06, 25), + "Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)." + ) + ); + + await _authorRepository.InsertAsync( + new Author( + _guidGenerator.Create(), + "Dan Brown", + new DateTime(1964, 06, 22), + "Daniel Gerhard Brown (born June 22, 1964) is an American author best known for his thriller novels" + ) + ); + } + } + } +} +``` + +### Step 2 - (Define Consts) + +We can create a folder-structure under the `BookStore.Domain.Shared` project like in the image below. + +![Domain Shared File Structure](./domain-shared-file-structure.png) + +* **AuthorConsts.cs** + +```csharp +namespace BookStore.Authors +{ + public class AuthorConsts + { + public const int MaxNameLength = 128; + + public const int MaxShortBioLength = 256; + } +} +``` + +* **BookConsts.cs** + +```csharp +namespace BookStore.Books +{ + public class BookConsts + { + public const int MaxNameLength = 128; + } +} +``` + +* **CategoryConsts.cs** + +```csharp +namespace BookStore.Categories +{ + public class CategoryConsts + { + public const int MaxNameLength = 64; + } +} +``` + +In these classes, we've defined max text length for our entity properties that we will use in the **Database Integration** section to specify limits for our properties. (E.g. varchar(128) for BookName) + +### Step 3 - (Database Integration) + +After defining our entities, we can configure them for the database integration. +Open the `BookStoreDbContext` class in the `BookStore.EntityFrameworkCore` project and update the following code blocks. + +```csharp +namespace BookStore.EntityFrameworkCore +{ + [ReplaceDbContext(typeof(IIdentityDbContext))] + [ReplaceDbContext(typeof(ITenantManagementDbContext))] + [ConnectionStringName("Default")] + public class BookStoreDbContext : + AbpDbContext, + IIdentityDbContext, + ITenantManagementDbContext + { + //... + + //DbSet properties for our Aggregate Roots + public DbSet Authors { get; set; } + public DbSet Books { get; set; } + public DbSet Categories { get; set; } + + //NOTE: We don't need to add DbSet, because we will be query it via using the Book entity + // public DbSet BookCategories { get; set; } + + //... + + protected override void OnModelCreating(ModelBuilder builder) + { + //... + + /* Configure your own tables/entities inside here */ + builder.Entity(b => + { + b.ToTable(BookStoreConsts.DbTablePrefix + "Authors" + BookStoreConsts.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.Name) + .HasMaxLength(AuthorConsts.MaxNameLength) + .IsRequired(); + + b.Property(x => x.ShortBio) + .HasMaxLength(AuthorConsts.MaxShortBioLength) + .IsRequired(); + }); + + builder.Entity(b => + { + b.ToTable(BookStoreConsts.DbTablePrefix + "Books" + BookStoreConsts.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.Name) + .HasMaxLength(BookConsts.MaxNameLength) + .IsRequired(); + + //one-to-many relationship with Author table + b.HasOne().WithMany().HasForeignKey(x => x.AuthorId).IsRequired(); + + //many-to-many relationship with Category table => BookCategories + b.HasMany(x => x.Categories).WithOne().HasForeignKey(x => x.BookId).IsRequired(); + }); + + builder.Entity(b => + { + b.ToTable(BookStoreConsts.DbTablePrefix + "Categories" + BookStoreConsts.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.Name) + .HasMaxLength(CategoryConsts.MaxNameLength) + .IsRequired(); + }); + + builder.Entity(b => + { + b.ToTable(BookStoreConsts.DbTablePrefix + "BookCategories" + BookStoreConsts.DbSchema); + b.ConfigureByConvention(); + + //define composite key + b.HasKey(x => new { x.BookId, x.CategoryId }); + + //many-to-many configuration + b.HasOne().WithMany(x => x.Categories).HasForeignKey(x => x.BookId).IsRequired(); + b.HasOne().WithMany().HasForeignKey(x => x.CategoryId).IsRequired(); + + b.HasIndex(x => new { x.BookId, x.CategoryId }); + }); + } + } +} +``` + +* In this class, we've defined the **DbSet** properties for our **Aggregate Roots** (**Book**, **Author** and **Category**). Notice, we didn't define the **DbSet** for the `BookCategory` class (our join table/entity). Because, the `Book` aggregate is responsible for managing it via sub-collection. + +* After that, we can use the **FluentAPI** to configure our tables in the `OnModelCreating` method of this class. + +```csharp +builder.Entity(b => +{ + //... + + //one-to-many relationship with Author table + b.HasOne().WithMany().HasForeignKey(x => x.AuthorId).IsRequired(); + + //many-to-many relationship with Category table => BookCategories + b.HasMany(x => x.Categories).WithOne().HasForeignKey(x => x.BookId).IsRequired(); +}); +``` + +Here, we have provided the one-to-many relationship between the **Book** and the **Author** in the above code-block. + +```csharp +builder.Entity(b => +{ + //... + + //define composite key + b.HasKey(x => new { x.BookId, x.CategoryId }); + + //many-to-many configuration + b.HasOne().WithMany(x => x.Categories).HasForeignKey(x => x.BookId).IsRequired(); + b.HasOne().WithMany().HasForeignKey(x => x.CategoryId).IsRequired(); + + b.HasIndex(x => new { x.BookId, x.CategoryId }); +}); +``` + +Here, firstly we've defined the composite key for our `BookCategory` entity. `BookId` and `CategoryId` are together as composite keys for the `BookCategory` table. Then we've configured the many-to-many relationship between the `Book` and the `Category` tables like in the above code-block. + +#### Implementing the `IBookRepository` Interface + +After making the relevant configurations for the database integration, we can now implement the `IBookRepository` interface. To do this, create a folder named `Books` in the `BookStore.EntityFrameworkCore` project and inside of this folder, create a class named `EfCoreBookRepository` and update this class with the following code: + +```csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading; +using System.Threading.Tasks; +using BookStore.Authors; +using BookStore.Categories; +using BookStore.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace BookStore.Books +{ + public class EfCoreBookRepository : EfCoreRepository, IBookRepository + { + public EfCoreBookRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public async Task> GetListAsync( + string sorting, + int skipCount, + int maxResultCount, + CancellationToken cancellationToken = default + ) + { + var query = await ApplyFilterAsync(); + + return await query + .OrderBy(!string.IsNullOrWhiteSpace(sorting) ? sorting : nameof(Book.Name)) + .PageBy(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public async Task GetAsync(Guid id, CancellationToken cancellationToken = default) + { + var query = await ApplyFilterAsync(); + + return await query + .Where(x => x.Id == id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); + } + + private async Task> ApplyFilterAsync() + { + var dbContext = await GetDbContextAsync(); + + return (await GetDbSetAsync()) + .Include(x => x.Categories) + .Join(dbContext.Set(), book => book.AuthorId, author => author.Id, + (book, author) => new {book, author}) + .Select(x => new BookWithDetails + { + Id = x.book.Id, + Name = x.book.Name, + Price = x.book.Price, + PublishDate = x.book.PublishDate, + CreationTime = x.book.CreationTime, + AuthorName = x.author.Name, + CategoryNames = (from bookCategories in x.book.Categories + join category in dbContext.Set() on bookCategories.CategoryId equals category.Id + select category.Name).ToArray() + }); + } + + public override Task> WithDetailsAsync() + { + return base.WithDetailsAsync(x => x.Categories); + } + } +} +``` + +* Here, we've implemented our custom repository methods and returned the book with details (author name and categories). + +### Step 4 - (Database Migration) + +* We've integrated our entities with the database in the previous step, now we can create a new database migration and apply it to the database. So let's do that. + +* Open the `BookStore.EntityFrameworkCore` project in the terminal. And create a new database migration by using the following command: + +```bash +dotnet ef migrations add +``` + +* Then, run the `BookStore.DbMigrator` application to create the database. + +### Step 5 - (Create Application Services) + +* Let's start with defining our DTOs and application service interfaces in the `BookStore.Application.Contracts` layer. We can create a folder-structure like in the image below: + +![Application Contracts Folder Structure](./application-contracts-folder-structure.png) + +* We can use the [`CrudAppService`](https://docs.abp.io/en/abp/latest/Application-Services#crud-application-services) base class of the ABP Framework to create application services to **Get**, **Create**, **Update** and **Delete** authors and categories. + +* **AuthorDto.cs** + +```csharp +using System; +using Volo.Abp.Application.Dtos; + +namespace BookStore.Authors +{ + public class AuthorDto : EntityDto + { + public string Name { get; set; } + + public DateTime BirthDate { get; set; } + + public string ShortBio { get; set; } + } +} +``` + +* **AuthorLookupDto.cs** + +```csharp +using System; +using Volo.Abp.Application.Dtos; + +namespace BookStore.Authors +{ + public class AuthorLookupDto : EntityDto + { + public string Name { get; set; } + } +} +``` + +We will use this DTO class as output DTO to get all the authors and list them in a select box in the book creation model. (Like in the image below.) + +![Book Create Modal](./book-creation-modal.png) + +* **CreateUpdateAuthorDto.cs** + +```csharp +using System; + +namespace BookStore.Authors +{ + public class CreateUpdateAuthorDto + { + public string Name { get; set; } + + public DateTime BirthDate { get; set; } + + public string ShortBio { get; set; } + } +} +``` + +* **IAuthorAppService.cs** + +```csharp +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace BookStore.Authors +{ + public interface IAuthorAppService : + ICrudAppService + { + } +} +``` + +* **BookDto.cs** + +```csharp +using System; +using Volo.Abp.Application.Dtos; + +namespace BookStore.Books +{ + public class BookDto : EntityDto + { + public string AuthorName { get; set; } + + public string Name { get; set; } + + public DateTime PublishDate { get; set; } + + public float Price { get; set; } + + public string[] CategoryNames { get; set; } + } +} +``` + +When listing the Book/Books we will retrieve them with all their details (author name and category names). + +* **BookGetListInput.cs** + +```csharp +using Volo.Abp.Application.Dtos; + +namespace BookStore.Books +{ + public class BookGetListInput : PagedAndSortedResultRequestDto + { + } +} +``` + +* **CreateUpdateBookDto.cs** + +```csharp +using System; + +namespace BookStore.Books +{ + public class CreateUpdateBookDto + { + public Guid AuthorId { get; set; } + + public string Name { get; set; } + + public DateTime PublishDate { get; set; } + + public float Price { get; set; } + + public string[] CategoryNames { get; set; } + } +} +``` + +To create or update a book we will use this input DTO. + +* **IBookAppService.cs** + +```csharp +using System; +using System.Threading.Tasks; +using BookStore.Authors; +using BookStore.Categories; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace BookStore.Books +{ + public interface IBookAppService : IApplicationService + { + Task> GetListAsync(BookGetListInput input); + + Task GetAsync(Guid id); + + Task CreateAsync(CreateUpdateBookDto input); + + Task UpdateAsync(Guid id, CreateUpdateBookDto input); + + Task DeleteAsync(Guid id); + + Task> GetAuthorLookupAsync(); + + Task> GetCategoryLookupAsync(); + } +} +``` + +* We will create custom application service method for managing Books instead of using the `CrudAppService`'s methods. + +* Also we will create two additional methods and they are `GetAuthorLookupAsync` and `GetCategoryLookupAsync`. We will use these two methods to retrieve all the authors and categories without pagination and list them as a select box item in create/update modals for the Book page. +(You can see the usage of these two methods in the gif below.) + +![New Book](./book-create.gif) + +* **CategoryDto.cs** + +```csharp +using System; +using Volo.Abp.Application.Dtos; + +namespace BookStore.Categories +{ + public class CategoryDto : EntityDto + { + public string Name { get; set; } + } +} +``` + +* **CategoryLookupDto.cs** + +```csharp +using System; +using Volo.Abp.Application.Dtos; + +namespace BookStore.Categories +{ + public class CategoryLookupDto : EntityDto + { + public string Name { get; set; } + } +} +``` + +We will use this DTO class as an output DTO to get all categories without pagination and list them in a select box in the book create/update modals. + +* **CreateUpdateCategoryDto.cs** + +```csharp +namespace BookStore.Categories +{ + public class CreateUpdateCategoryDto + { + public string Name { get; set; } + } +} +``` + +* **ICategoryAppService.cs** + +```csharp +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace BookStore.Categories +{ + public interface ICategoryAppService : + ICrudAppService + { + } +} +``` + +After creating the DTOs and application service interfaces, now we can define the implementation of those interfaces. So, we can create a folder-structure like in the image below for the `BookStore.Application` layer. Open the application service classes and add the following codes to each of these classes. + +![Application Folder Structure](./application-folder-structure.png) + +* **AuthorAppService.cs** + +```csharp +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace BookStore.Authors +{ + public class AuthorAppService : + CrudAppService, + IAuthorAppService + { + public AuthorAppService(IRepository repository) : base(repository) + { + } + } +} +``` + +* **CategoryAppService.cs** + +```csharp +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace BookStore.Categories +{ + public class CategoryAppService : + CrudAppService, + ICategoryAppService + { + public CategoryAppService(IRepository repository) : base(repository) + { + } + } +} +``` + +Thanks to the `CrudAppService`, we don't need to manually implement the crud methods for **AuthorAppService** and **CategoryAppService**. + +* **BookAppService.cs** + +```csharp +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using BookStore.Authors; +using BookStore.Categories; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Domain.Repositories; + +namespace BookStore.Books +{ + public class BookAppService : BookStoreAppService, IBookAppService + { + private readonly IBookRepository _bookRepository; + private readonly BookManager _bookManager; + private readonly IRepository _authorRepository; + private readonly IRepository _categoryRepository; + + public BookAppService( + IBookRepository bookRepository, + BookManager bookManager, + IRepository authorRepository, + IRepository categoryRepository + ) + { + _bookRepository = bookRepository; + _bookManager = bookManager; + _authorRepository = authorRepository; + _categoryRepository = categoryRepository; + } + + public async Task> GetListAsync(BookGetListInput input) + { + var books = await _bookRepository.GetListAsync(input.Sorting, input.SkipCount, input.MaxResultCount); + var totalCount = await _bookRepository.CountAsync(); + + return new PagedResultDto(totalCount, ObjectMapper.Map, List>(books)); + } + + public async Task GetAsync(Guid id) + { + var book = await _bookRepository.GetAsync(id); + + return ObjectMapper.Map(book); + } + + public async Task CreateAsync(CreateUpdateBookDto input) + { + await _bookManager.CreateAsync( + input.AuthorId, + input.Name, + input.PublishDate, + input.Price, + input.CategoryNames + ); + } + + public async Task UpdateAsync(Guid id, CreateUpdateBookDto input) + { + var book = await _bookRepository.GetAsync(id, includeDetails: true); //return type is: Book (not BookWithDetails) Because, we don't need author name + + await _bookManager.UpdateAsync( + book, + input.AuthorId, + input.Name, + input.PublishDate, + input.Price, + input.CategoryNames + ); + } + + public async Task DeleteAsync(Guid id) + { + await _bookRepository.DeleteAsync(id); + } + + public async Task> GetAuthorLookupAsync() + { + var authors = await _authorRepository.GetListAsync(); + + return new ListResultDto( + ObjectMapper.Map, List>(authors) + ); + } + + public async Task> GetCategoryLookupAsync() + { + var categories = await _categoryRepository.GetListAsync(); + + return new ListResultDto( + ObjectMapper.Map, List>(categories) + ); + } + } +} +``` + +* As you can notice here, we've used our **Domain Service** class named `BookManager` in the **CreateAsync** and **UpdateAsync** methods. (Defined them in step 1) + +* As you may remember, in these methods, new categories are added to the book or removed from the sub-collection (**Categories** (`BookCategory`)) according to the relevant category names. + +* After implementing the application services, we need to define the mappings for our services to work. So open the `BookStoreApplicationAutoMapperProfile` class and update it with the following code: + +```csharp +using AutoMapper; +using BookStore.Authors; +using BookStore.Books; +using BookStore.Categories; + +namespace BookStore +{ + public class BookStoreApplicationAutoMapperProfile : Profile + { + public BookStoreApplicationAutoMapperProfile() + { + CreateMap(); + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); + CreateMap(); + + CreateMap(); + } + } +} + +``` + +### Step 6 - (UI) + +The only thing we need to do is, by using the application service methods that we've defined in the previous step to create the UI. + +![Web Folder Structure](./web-folder-structure.png) + +> To keep the article shorter, I'll just show you how to create the Book page (with Create/Edit modals). If you want to implement it to other pages, you can access the source code of the application at https://github.com/EngincanV/ABP-Many-to-Many-Relationship-Demo and copy-paste the relevant code-blocks to your application. + +#### Book Page + +* Create a razor page named **Index.cshtml** under the **Pages/Books** folder of the `BookStore.Web` project and paste the following code to that page. + +* **Index.cshtml** + +```html +@page +@model BookStore.Web.Pages.Books.Index + +@section scripts +{ + +} + + + + + + Books + + + + + + + + + + +``` + +In here we've added a **New Book** button and a table with an id named "BooksTable". We'll create an `Index.js` file and by using [datatable.js](https://datatables.net) we will fill the table with our records. + +* **Index.js** + +```js +$(function () { + var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); + var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal'); + + var bookService = bookStore.books.book; + + var dataTable = $('#BooksTable').DataTable( + abp.libs.datatables.normalizeConfiguration({ + serverSide: true, + paging: true, + order: [[1, "asc"]], + searching: false, + scrollX: true, + ajax: abp.libs.datatables.createAjax(bookService.getList), + columnDefs: [ + { + title: 'Actions', + rowAction: { + items: + [ + { + text: 'Edit', + action: function (data) { + editModal.open({ id: data.record.id }); + } + }, + { + text: 'Delete', + confirmMessage: function (data) { + return "Are you sure to delete the book '" + data.record.name +"'?"; + }, + action: function (data) { + bookService + .delete(data.record.id) + .then(function() { + abp.notify.info("Successfully deleted!"); + dataTable.ajax.reload(); + }); + } + } + ] + } + }, + { + title: 'Name', + data: "name" + }, + { + title: 'Publish Date', + data: "publishDate", + render: function (data) { + return luxon + .DateTime + .fromISO(data, { + locale: abp.localization.currentCulture.name + }).toLocaleString(); + } + }, + { + title: 'Author Name', + data: "authorName" + }, + { + title: 'Price', + data: "price" + }, + { + title: 'Categories', + data: "categoryNames", + render: function (data) { + return data.join(", "); + } + } + ] + }) + ); + + createModal.onResult(function () { + dataTable.ajax.reload(); + }); + + editModal.onResult(function () { + dataTable.ajax.reload(); + }); + + $('#NewBookButton').click(function (e) { + e.preventDefault(); + createModal.open(); + }); +}); + +``` + +> `abp.libs.datatables.normalizeConfiguration` is a helper function defined by the ABP Framework. It simplifies the Datatables configuration by providing conventional default values for missing options. + +* Let's examine what we've done in the `Index.js` file. + +* Firstly, we've defined our `createModal` and `editModal` modals by using the [ABP Modals](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Modals). Then, we've created the DataTable and fetched our books by using the dynamic JavaScript proxy function (`bookStore.books.book.getList`) (It sends a request to the **GetListAsync** method that we've defined in the `BookAppService` under the hook) and we've shown them in the table with an id named "BooksTable". + +* Now let's run the application and navigate to the **/Books** route to see how our Book page looks. + +![Demo](./demo.png) + +We need to see a page similar to the image above. Our app is working properly, we can continue developing. + +> If you are stuck in any point, you can examine the [source codes](https://github.com/EngincanV/ABP-Many-to-Many-Relationship-Demo). + +#### Model Classes and Mapping Configurations + +Create a folder named **Models** and add a class named `CategoryViewModel` inside of it. We will use this view modal class to determine which categories are selected or not in our Create/Edit modals. + +* **CategoryViewModel.cs** + +```csharp +using System; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc; + +namespace BookStore.Web.Models +{ + public class CategoryViewModel + { + [HiddenInput] + public Guid Id { get; set; } + + public bool IsSelected { get; set; } + + [Required] + [HiddenInput] + public string Name { get; set; } + } +} +``` + +Then, we can open the `BookStoreWebAutoMapperProfile` class and define the required mappings as follows: + +```csharp +using AutoMapper; +using BookStore.Authors; +using BookStore.Books; +using BookStore.Categories; +using BookStore.Web.Models; +using BookStore.Web.Pages.Books; +using Volo.Abp.AutoMapper; + +namespace BookStore.Web +{ + public class BookStoreWebAutoMapperProfile : Profile + { + public BookStoreWebAutoMapperProfile() + { + CreateMap() + .Ignore(x => x.IsSelected); + + CreateMap(); + + CreateMap(); + + CreateMap(); + } + } +} +``` + +#### Create/Edit Modals + +After creating our index page for Books and configuring mappings, let's continue with creating the Create/Edit modals for Books. + +Create a razor page named **CreateModal.cshtml** (and **CreateModal.cshtml.cs**). + +* **CreateModal.cshtml** + +```html +@page +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model BookStore.Web.Pages.Books.CreateModal + +@{ + Layout = null; +} + +
+ + + + + +
+ + + + + + +
+
+ +
+ @for (var i = 0; i < Model.Categories.Count; i++) + { + var category = Model.Categories[i]; + + + } +
+
+
+
+ +
+
+``` + +* **CreateModal.cshtml.cs** + +```csharp +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BookStore.Books; +using BookStore.Categories; +using BookStore.Web.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace BookStore.Web.Pages.Books +{ + public class CreateModal : BookStorePageModel + { + [BindProperty] + public CreateUpdateBookDto Book { get; set; } + + [BindProperty] + public List Categories { get; set; } + + public List AuthorList { get; set; } + + private readonly IBookAppService _bookAppService; + + public CreateModal(IBookAppService bookAppService) + { + _bookAppService = bookAppService; + } + + public async Task OnGetAsync() + { + Book = new CreateUpdateBookDto(); + + //Get all authors and fill the select list + var authorLookup = await _bookAppService.GetAuthorLookupAsync(); + AuthorList = authorLookup.Items + .Select(x => new SelectListItem(x.Name, x.Id.ToString())) + .ToList(); + + //Get all categories + var categoryLookupDto = await _bookAppService.GetCategoryLookupAsync(); + Categories = ObjectMapper.Map, List>(categoryLookupDto.Items.ToList()); + } + + public async Task OnPostAsync() + { + ValidateModel(); + + var selectedCategories = Categories.Where(x => x.IsSelected).ToList(); + if (selectedCategories.Any()) + { + var categoryNames = selectedCategories.Select(x => x.Name).ToArray(); + Book.CategoryNames = categoryNames; + } + + await _bookAppService.CreateAsync(Book); + return NoContent(); + } + } +} +``` + +Here, we've got all categories and authors inside of the `OnGetAsync` method. And use them inside of the create modal to list them so the user can choose when creating a new book. + +![Create Book Modal](./book-creation-modal.png) + +* When the user submits the form, the `OnPostAsync` method runs. Inside of this method, we get the selected categories and pass them into the **CategoryNames** array of the Book object and call the `IBookAppService.CreateAsync` method to create a new book. + +Create a razor page named **EditModal.cshtml** (and **EditModal.cshtml.cs**). + +* **EditModal.cshtml** + +```html +@page +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model BookStore.Web.Pages.Books.EditModal + +@{ + Layout = null; +} + +
+ + + + + +
+ + + + + + + +
+
+ +
+ @for (var i = 0; i < Model.Categories.Count; i++) + { + var category = Model.Categories[i]; + + + } +
+
+
+
+ +
+
+``` + +* **EditModal.cshtml.cs** + +```csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BookStore.Books; +using BookStore.Categories; +using BookStore.Web.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace BookStore.Web.Pages.Books +{ + public class EditModal : BookStorePageModel + { + [HiddenInput] + [BindProperty(SupportsGet = true)] + public Guid Id { get; set; } + + [BindProperty] + public CreateUpdateBookDto EditingBook { get; set; } + + [BindProperty] + public List Categories { get; set; } + + public List AuthorList { get; set; } + + private readonly IBookAppService _bookAppService; + + public EditModal(IBookAppService bookAppService) + { + _bookAppService = bookAppService; + } + + public async Task OnGetAsync() + { + var bookDto = await _bookAppService.GetAsync(Id); + EditingBook = ObjectMapper.Map(bookDto); + + //get all authors + var authorLookup = await _bookAppService.GetAuthorLookupAsync(); + AuthorList = authorLookup.Items + .Select(x => new SelectListItem(x.Name, x.Id.ToString())) + .ToList(); + + //get all categories + var categoryLookupDto = await _bookAppService.GetCategoryLookupAsync(); + Categories = ObjectMapper.Map, List>(categoryLookupDto.Items.ToList()); + + //mark as Selected for Categories in the book + if (EditingBook.CategoryNames != null && EditingBook.CategoryNames.Any()) + { + Categories + .Where(x => EditingBook.CategoryNames.Contains(x.Name)) + .ToList() + .ForEach(x => x.IsSelected = true); + } + } + + public async Task OnPostAsync() + { + ValidateModel(); + + var selectedCategories = Categories.Where(x => x.IsSelected).ToList(); + if (selectedCategories.Any()) + { + var categoryNames = selectedCategories.Select(x => x.Name).ToArray(); + EditingBook.CategoryNames = categoryNames; + } + + await _bookAppService.UpdateAsync(Id, EditingBook); + return NoContent(); + } + } +} +``` + +* As in the `CreateModal.cshtml.cs`, we've got all categories and authors inside of the `OnGetAsync` method. And also we get the book by id and mark the selected categories properties' as `IsSelected = true`. + +* When the user updates the inputs and submits the form, the `OnPostAsync` method runs. Inside of this method, we get the selected categories and pass them into the **CategoryNames** array of the Book object and call the `IBookAppService.UpdateAsync` method to update the book. + +![Edit Book Modal](./book-update-modal.png) + +### Conclusion + +In this article, I've tried to explain how to create a many-to-many relationship by using the ABP framework. (by following DDD principles) + +Thanks for reading this article, I hope it was helpful. diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/application-contracts-folder-structure.png b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/application-contracts-folder-structure.png new file mode 100644 index 0000000000..c0ed574489 Binary files /dev/null and b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/application-contracts-folder-structure.png differ diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/application-final-demo.gif b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/application-final-demo.gif new file mode 100644 index 0000000000..61289a8ffb Binary files /dev/null and b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/application-final-demo.gif differ diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/application-folder-structure.png b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/application-folder-structure.png new file mode 100644 index 0000000000..0d37a552d4 Binary files /dev/null and b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/application-folder-structure.png differ diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/book-create.gif b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/book-create.gif new file mode 100644 index 0000000000..0e534802f2 Binary files /dev/null and b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/book-create.gif differ diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/book-creation-modal.png b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/book-creation-modal.png new file mode 100644 index 0000000000..8661e016cd Binary files /dev/null and b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/book-creation-modal.png differ diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/book-update-modal.png b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/book-update-modal.png new file mode 100644 index 0000000000..d9d312399a Binary files /dev/null and b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/book-update-modal.png differ diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/demo.png b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/demo.png new file mode 100644 index 0000000000..9b954bd7d6 Binary files /dev/null and b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/demo.png differ diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/domain-file-structure.png b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/domain-file-structure.png new file mode 100644 index 0000000000..6a5405dafe Binary files /dev/null and b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/domain-file-structure.png differ diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/domain-shared-file-structure.png b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/domain-shared-file-structure.png new file mode 100644 index 0000000000..22a8de6599 Binary files /dev/null and b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/domain-shared-file-structure.png differ diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/er-diagram.png b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/er-diagram.png new file mode 100644 index 0000000000..5948366e0e Binary files /dev/null and b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/er-diagram.png differ diff --git a/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/web-folder-structure.png b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/web-folder-structure.png new file mode 100644 index 0000000000..ed4a63a06c Binary files /dev/null and b/docs/en/Community-Articles/2021-10-31-Many-to-Many-Relationship-with-ABP-and-EF-Core/web-folder-structure.png differ diff --git a/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/POST.md b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/POST.md new file mode 100644 index 0000000000..edaeaf46db --- /dev/null +++ b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/POST.md @@ -0,0 +1,321 @@ +# Integrating the Syncfusion MVC Components to the ABP MVC UI + +## Introduction + +In this article we will see how we can integrate the Syncfusion MVC Components into our ABP application. + +## Source Code + +You can find the source code of the application at https://github.com/EngincanV/ABP-Syncfusion-Components-Demo. + +## Prerequisites + +* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) + + * In this article, we will create a new startup template in v5.0.0-rc.2 and if you follow this article from top to bottom and create a new startup template with me, you need to install the [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) before starting. + +**NOTE:** ABP v5.X stable version has been released. You can replace v5.0.0-rc.2 with the latest stable version in your steps. + +Also, you need to update your ABP CLI to the v5.0.0-rc.2, you can use the command below to update your CLI version: + +```bash +dotnet tool update Volo.Abp.Cli -g --version 5.0.0-rc.2 +``` + +or install it if you haven't installed it before: + +```bash +dotnet tool install Volo.Abp.Cli -g --version 5.0.0-rc.2 +``` + +## Creating the Solution + +In this article, we will create a new startup template with EF Core as a database provider and MVC for the UI framework. But if you already have a project with MVC UI, you don't need to create a new startup template, you can directly implement the following steps to your existing project. + +> If you already have a project with MVC/Razor Pages UI, you can skip this section. + +We can create a new startup template by using the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI): + +```bash +abp new SyncfusionComponentsDemo -t app --preview +``` + +Our project boilerplate will be ready after the download is finished. Then, we can open the solution and start developing. + +## Starting the Development + +### Pre-requisite + +> If you've already had a license from Syncfusion, you can skip this section. + +* The first thing we need to do is create an account to be able to get a license from Syncfusion. + +* So, let's navigate to https://www.syncfusion.com/aspnet-core-ui-controls and click the "Download Free Trial" button. + +* Then fill the form and start your 30-day free trial. + +* After that, navigate to https://www.syncfusion.com/account/manage-trials/downloads to get our license key that will be used in our application. + +![](./manage-trial-1.png) + +Click the "Get License Key" link for "ASP.NET Core (Essential JS 2)". + +![](./manage-trial-2.png) + +Then a modal will be opened like in the above image, select a version and click the "Get License Key" button. + +![](./copy-license-key.png) + +Lastly, copy the generated license key value. + +In order to use the relevant components, Syncfusion needs to check this license key to know that our license is valid. + +### Configurations + +After providing a license key from Syncfusion, we can start with the configuration that needs to be done in our application. + +#### 1-) Install the Syncfusion.EJ2.AspNet.Core package + +We need to install the `Syncfusion.EJ2.AspNet.Core` Nuget package to our Web project (*.Web). + +We can install it via **Visual Studio's Nuget Package Manager**: + +![](./syncfusion-package.png) + +or via dotnet cli: + +```bash +dotnet add package Syncfusion.EJ2.AspNet.Core --version 19.3.0.57 +``` + +> In this article, I've used the package in version 19.3.0.57. + +#### 2-) Register the License Key + +* After installing the package, we need to register our license key to be able to use the Syncfusion Components. + +* To register the license key, open your web module class and update the `ConfigureServices` method as follows: + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + var hostingEnvironment = context.Services.GetHostingEnvironment(); + var configuration = context.Services.GetConfiguration(); + + //Register Syncfusion license + Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense(licenseKey: configuration["Syncfusion:LicenseKey"].ToString()); + + ConfigureUrls(configuration); + ConfigureBundles(); + ConfigureAuthentication(context, configuration); + ConfigureAutoMapper(); + ConfigureVirtualFileSystem(hostingEnvironment); + ConfigureLocalizationServices(); + ConfigureNavigationServices(); + ConfigureAutoApiControllers(); + ConfigureSwaggerServices(context.Services); +} +``` + +Instead of writing the license key directly in here we can define it in the **appsettings.json** file and use it here by using the Configuration system of .NET. + + +* Open your **appsettings.json** file and add a new section named "Syncfusion" as below: + +```json +{ + //... + + "Syncfusion": { + "LicenseKey": "" + } +} +``` + +> Replace the ` part with your license key that we've obtained in the previous section.` + +* To be able to use the Syncfusion Components we need to define them in our **_ViewImports.cshtml** file. By doing that we can use the Syncfusion components everywhere in our application. + +* Open your **/Pages/_ViewImports.cshtml** file and add a new tag helper: + +```cshtml +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling +@addTagHelper *, Syncfusion.EJ2 //use Syncfusion components +``` + +#### 3-) Adding Syncfusion styles and scripts to our application + +Firstly, let's install the `@syncfusion/ej2` package from **npm**. + +* Open your **package.json** file and add the `@syncfusion/ej2` package with version **19.3.57**: + +```json +{ + "version": "1.0.0", + "name": "my-app", + "private": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui.theme.basic": "^5.0.0-rc.2", + "@syncfusion/ej2": "^19.3.57" + } +} +``` + +* Then, open the **abp.resourcemapping.js** file and update the **mappings** section: + +```js +module.exports = { + aliases: { + + }, + mappings: { + "@node_modules/@syncfusion/ej2/dist/ej2.min.js": "@libs/syncfusion/", + "@node_modules/@syncfusion/ej2/material.css": "@libs/syncfusion/" + } +}; +``` + +> ABP copies related packages from **node_modules** folder to the **libs** folder by examining this file. You can read this [document](docs.abp.io/en/abp/latest/UI/AspNetCore/Client-Side-Package-Management#mapping-the-library-resources) for more info. + +* Then run the `abp install-libs` to install the dependencies and copy them into the libs folder by your mappings configuration. After running this command, in your **libs** folder it should be a folder named **syncfusion** folder. + +![](./syncfusion-libs.png) + +The last thing we need to do is, add some style and script files provided by Syncfusion, between our head-body tags. + +* We can do this by creating two view components (one for Styles and the other for Scripts). Let's do that. + +First, create a folder structure as shown below under the **Components** folder. + +![](./component-folder-structure.png) + +Then open the related files and add the following codes to each of these files. + +* **Default.cshtml** (/Components/Syncfusion/Script/Default.cshtml) + +```cshtml +@addTagHelper *, Syncfusion.EJ2 //add this line + + + + + + +``` + +* **SyncfusionScriptComponent.cs** + +```csharp +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; + +namespace SyncfusionComponentsDemo.Web.Components.Syncfusion.Script +{ + public class SyncfusionScriptComponent : AbpViewComponent + { + public IViewComponentResult Invoke() + { + return View("~/Components/Syncfusion/Script/Default.cshtml"); + } + } +} +``` + +* **Default.cshtml** (/Components/Syncfusion/Style/Default.cshtml) + +```cshtml + + +``` + +* SyncfusionStyleComponent.cs + +```csharp +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; + +namespace SyncfusionComponentsDemo.Web.Components.Syncfusion.Style +{ + public class SyncfusionStyleComponent : AbpViewComponent + { + public IViewComponentResult Invoke() + { + return View("~/Components/Syncfusion/Style/Default.cshtml"); + } + } +} +``` + +After creating these two components, we can use the [**Layout Hooks**](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Layout-Hooks) system of ABP to inject these two components between head and script tags. + +To do this, open your web module class and update the `ConfigureServices` method as below: + + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + var hostingEnvironment = context.Services.GetHostingEnvironment(); + var configuration = context.Services.GetConfiguration(); + + //Register Syncfusion license + var licenseKey = configuration["Syncfusion:LicenseKey"].ToString(); + Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense(licenseKey: licenseKey); + + Configure(options => + { + //Now, the SyncfusionStyleComponent code will be inserted in the head of the page as the last item. + options.Add(LayoutHooks.Head.Last, typeof(SyncfusionStyleComponent)); + + //the SyncfusionScriptComponent will be inserted in the body of the page as the last item. + options.Add(LayoutHooks.Body.Last, typeof(SyncfusionScriptComponent)); + }); + + ConfigureUrls(configuration); + ConfigureBundles(); + ConfigureAuthentication(context, configuration); + ConfigureAutoMapper(); + ConfigureVirtualFileSystem(hostingEnvironment); + ConfigureLocalizationServices(); + ConfigureNavigationServices(); + ConfigureAutoApiControllers(); + ConfigureSwaggerServices(context.Services); +} +``` + +After injecting the Syncfusion style and script into our application, our configurations have been completed. We can try with a simple component to see if it works as we expected. + +* Let's try with the [Calendar](https://www.syncfusion.com/aspnet-core-ui-controls/calendar) component. Open your **Index.cshtml** file and update with the below content: + +```cshtml +@page +@using Microsoft.AspNetCore.Mvc.Localization +@using SyncfusionComponentsDemo.Localization +@using Volo.Abp.Users +@model SyncfusionComponentsDemo.Web.Pages.IndexModel + +@section styles { + +} + +@section scripts { + +} + +
+

Syncfusion - Calendar Component

+ +
+``` + +* Then when we run the application, we need to see the relevant calendar component as below. + +![](./calendar-component.png) + +### Conclusion + +In this article, we've explained how to integrate the **Syncfusion Components** into our applications. After following this article, you can use the Syncfusion components in your application. + +Thanks for reading the article, I hope you've found it useful :) diff --git a/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/calendar-component.png b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/calendar-component.png new file mode 100644 index 0000000000..8e7f36367a Binary files /dev/null and b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/calendar-component.png differ diff --git a/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/component-folder-structure.png b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/component-folder-structure.png new file mode 100644 index 0000000000..0dabbe0a06 Binary files /dev/null and b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/component-folder-structure.png differ diff --git a/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/copy-license-key.png b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/copy-license-key.png new file mode 100644 index 0000000000..847be0363d Binary files /dev/null and b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/copy-license-key.png differ diff --git a/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/manage-trial-1.png b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/manage-trial-1.png new file mode 100644 index 0000000000..8e84229549 Binary files /dev/null and b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/manage-trial-1.png differ diff --git a/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/manage-trial-2.png b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/manage-trial-2.png new file mode 100644 index 0000000000..57ef370bdb Binary files /dev/null and b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/manage-trial-2.png differ diff --git a/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/syncfusion-libs.png b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/syncfusion-libs.png new file mode 100644 index 0000000000..d75935d8fd Binary files /dev/null and b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/syncfusion-libs.png differ diff --git a/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/syncfusion-package.png b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/syncfusion-package.png new file mode 100644 index 0000000000..6792ea0c32 Binary files /dev/null and b/docs/en/Community-Articles/2021-12-13-Integrating-the-Syncfusion-MVC-Components-to-the-ABP-MVC-UI/syncfusion-package.png differ diff --git a/docs/en/Connection-Strings.md b/docs/en/Connection-Strings.md index c5a70174bf..76e535a960 100644 --- a/docs/en/Connection-Strings.md +++ b/docs/en/Connection-Strings.md @@ -1,15 +1,17 @@ # Connection Strings -ABP Framework is designed to be [modular](Module-Development-Basics.md), [microservice compatible](Microservice-Architecture.md) and [multi-tenancy](Multi-Tenancy.md) aware. Connection string management is also designed to support these scenarios; +> Connection string system is especially needed when you want to create or use a modular system. If you have a monolithic application with a single database, you can go with the [ABP startup solution template](Startup-Templates/Application.md), which is properly configured for you. -* Allows to set separate connection strings for every module, so every module can have its own physical database. Modules even might be configured to use different DBMSs. +ABP Framework is designed to be [modular](Module-Development-Basics.md) and [multi-tenancy](Multi-Tenancy.md) aware. Connection string management is also designed to support these scenarios; + +* Allows to set separate connection strings for every module, so every module can have its own physical database. Modules even might be configured to use different database providers. * Allows to set separate connection string and use a separate database per tenant (in a SaaS application). It also supports hybrid scenarios; -* Allows to group modules into databases (all modules into a single shared database, 2 modules to database A, 3 modules to database B, 1 module to database C and rest of the modules to database D... etc.) +* Allows to group modules into databases (e.g., all modules into a single shared database or two modules to database A, three modules to database B, one module to database C and rest of the modules to database D) * Allows to group tenants into databases, just like the modules. -* Allows to separate databases per tenant per module (which might be harder to maintain for you because of too many databases, but the ABP framework supports it). +* Allows to separate databases per tenant per module (which might be hard to maintain for you because of too many databases, but the ABP framework supports it). All the [pre-built application modules](Modules/Index.md) are designed to be compatible these scenarios. @@ -29,15 +31,19 @@ See the following configuration: This configuration defines three different connection strings: -* `MyMainDb` (the `Default` connection string) is the main connection string of the application. If you don't specify a connection string for a module, it fallbacks to the `Default` connection string. The [application startup template](Startup-Templates/Application.md) is configured to use a single connection string, so all the modules uses a single shared database. -* `MyIdsDb` is used by the [IdentityServer](Modules/IdentityServer.md) module. -* `MyPermissionDb` is used by the [Permission Management](Modules/Permission-Management.md) module. +* `MyMainDb` (the `Default` connection string) is the main connection string of the application. If you don't specify a connection string for a module, it fallbacks to the `Default` connection string. The [application startup template](Startup-Templates/Application.md) is configured to use a single connection string, so all the modules uses a single, shared database. +* `MyIdsDb` (the `AbpIdentityServer` connection string) is used by the [IdentityServer](Modules/IdentityServer.md) module. +* `MyPermissionDb` (the `AbpPermissionManagement` connection string) is used by the [Permission Management](Modules/Permission-Management.md) module. -[Pre-built application modules](Modules/Index.md) define constants for the connection string names. For example, the IdentityServer module defines a ` ConnectionStringName ` constant in the ` AbpIdentityServerDbProperties ` class (located in the ` Volo.Abp.IdentityServer ` namespace). Other modules similarly define constants, so you can investigate the connection string name. +[Pre-built application modules](Modules/Index.md) define constants for the connection string names. For example, the [IdentityServer module](Modules/IdentityServer.md) defines a ` ConnectionStringName ` constant in the ` AbpIdentityServerDbProperties ` class (located in the ` Volo.Abp.IdentityServer ` namespace). Other modules similarly define constants, so you can investigate the connection string name. ### AbpDbConnectionOptions -ABP actually uses the `AbpDbConnectionOptions` to get the connection strings. If you set the connection strings as explained above, `AbpDbConnectionOptions` is automatically filled. However, you can set or override the connection strings using [the options pattern](Options.md). You can configure the `AbpDbConnectionOptions` in the `ConfigureServices` method of your [module](Module-Development-Basics.md) as shown below: +`AbpDbConnectionOptions` is the options class that is used to set the connection strings and configure database structures. + +#### Setting the connection strings + +ABP uses the `AbpDbConnectionOptions` to get the connection strings. If you configure the connection strings as explained above, `AbpDbConnectionOptions` is automatically filled. However, you can set or override the connection strings using [the options pattern](Options.md). You can configure the `AbpDbConnectionOptions` in the `ConfigureServices` method of your [module](Module-Development-Basics.md) as shown below: ````csharp public override void ConfigureServices(ServiceConfigurationContext context) @@ -50,6 +56,52 @@ public override void ConfigureServices(ServiceConfigurationContext context) } ```` +#### Configuring the database structures + +`Databases` property of the `AbpDbConnectionOptions` class is used to group multiple connection strings (of multiple modules) to a single connection string. + +See the following connection strings: + +````json +"ConnectionStrings": { + "Default": "Server=localhost;Database=MyMainDb;Trusted_Connection=True;", + "AbpIdentity": "Server=localhost;Database=MySecondaryDb;Trusted_Connection=True;", + "AbpIdentityServer": "Server=localhost;Database=MySecondaryDb;Trusted_Connection=True;", + "AbpPermissionManagement": "Server=localhost;Database=MySecondaryDb;Trusted_Connection=True;" +} +```` + +In this example, we've defined four connection strings, but the last three of them are the same; `AbpIdentity`, `AbpIdentityServer` and `AbpPermissionManagement` uses the same database, named `MySecondaryDb`. The main application and the other modules use the `Default` connection string, hence the `MyMainDb` database. + +What we want to do here is to group three modules (`AbpIdentity`, `AbpIdentityServer` and `AbpPermissionManagement`) in a single database, but we needed to specify each one manually. Because the fallback connection string is the `Default` one, if we don't specify it for a module. + +To eliminate the repetitive connection string definition, we can configure the `AbpDbConnectionOptions.Databases` property to group these connection strings, as shown in the following code (we place that in the `ConfigureServices` method of our [module class](Module-Development-Basics.md)): + +````csharp +Configure(options => +{ + options.Databases.Configure("MySecondaryDb", db => + { + db.MappedConnections.Add("AbpIdentity"); + db.MappedConnections.Add("AbpIdentityServer"); + db.MappedConnections.Add("AbpPermissionManagement"); + }); +}); +```` + +Then we can change the `appsettings.json` file as shown in the following code block: + +````json +"ConnectionStrings": { + "Default": "Server=localhost;Database=MyMainDb;Trusted_Connection=True;", + "MySecondaryDb": "Server=localhost;Database=MySecondaryDb;Trusted_Connection=True;" +} +```` + +`MySecondaryDb` becomes the new connection string for the mapped connections. + +> ABP first looks for the module-specific connection string, then looks if a database mapping is available, finally fallbacks to the `Default` connection string. + ## Set the Connection String Name A module typically has a unique connection string name associated to its `DbContext` class using the `ConnectionStringName` attribute. Example: @@ -62,15 +114,13 @@ public class IdentityServerDbContext } ```` -For [Entity Framework Core](Entity-Framework-Core.md) and [MongoDB](MongoDB.md), write this to your `DbContext` class (and the interface if it has). - -> If you are developing a reusable, database provider independent module see also [the best practices guide](Best-Practices/Index.md). +For [Entity Framework Core](Entity-Framework-Core.md) and [MongoDB](MongoDB.md), write this to your `DbContext` class (and the interface if it has). In this way, ABP uses the specified connection string for the related `DbContext` instances. ## Database Migrations for the Entity Framework Core Relational databases require to create the database and the database schema (tables, views... etc.) before using it. -The startup template (with EF Core ORM) comes with a single database and a `.EntityFrameworkCore` project that contains related classes and the migration files for that database. This project mainly defines a *YourProjectName*DbContext that calls the `Configure...()` methods of the used modules, like `builder.ConfigurePermissionManagement()`. +The startup template (with EF Core ORM) comes with a single database and a `.EntityFrameworkCore` project that contains related classes and the migration files for that database. This project mainly defines a `YourProjectNameDbContext` class that calls the `Configure...()` methods of the used modules, like `builder.ConfigurePermissionManagement()`. Once you want to separate a module's database, you typically will need to create a second migration path. See the [EF Core Migrations](Entity-Framework-Core-Migrations.md) document to learn how to create and use a different database for a desired module. diff --git a/docs/en/Customizing-Application-Modules-Extending-Entities.md b/docs/en/Customizing-Application-Modules-Extending-Entities.md index c2bcd314d4..e1cb02e849 100644 --- a/docs/en/Customizing-Application-Modules-Extending-Entities.md +++ b/docs/en/Customizing-Application-Modules-Extending-Entities.md @@ -63,8 +63,6 @@ You can then use the same extra properties system defined in the previous sectio Another approach can be **creating your own entity** mapped to **the same database table** (or collection for a MongoDB database). -`AppUser` entity in the [application startup template](Startup-Templates/Application.md) already implements this approach. [EF Core Migrations document](Entity-Framework-Core-Migrations.md) describes how to implement it and manage **EF Core database migrations** in such a case. It is also possible for MongoDB, while this time you won't deal with the database migration problems. - ## Creating a New Entity with Its Own Database Table/Collection Mapping your entity to an **existing table** of a depended module has a few disadvantages; diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index f60fcfc490..989123c060 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -4,7 +4,7 @@ This document explains how to integrate EF Core as an ORM provider to ABP based ## Installation -`Volo.Abp.EntityFrameworkCore` is the main nuget package for the EF Core integration. Install it to your project (for a layered application, to your data/infrastructure layer): +`Volo.Abp.EntityFrameworkCore` is the main NuGet package for the EF Core integration. Install it to your project (for a layered application, to your data/infrastructure layer): ```` shell Install-Package Volo.Abp.EntityFrameworkCore @@ -30,15 +30,13 @@ namespace MyCompany.MyProject ### Database Management System Selection -Entity Framework Core supports various database management systems ([see all](https://docs.microsoft.com/en-us/ef/core/providers/)). ABP framework and this document doesn't depend on any specific DBMS. +Entity Framework Core supports various database management systems ([see all](https://docs.microsoft.com/en-us/ef/core/providers/)). ABP framework and this document don't depend on any specific DBMS. If you are creating a [reusable application module](Modules/Index.md), avoid to depend on a specific DBMS package. However, in a final application you eventually will select a DBMS. -If you are creating a [reusable application module](Modules/Index.md), avoid to depend on a specific DBMS package. However, in a final application you eventually will select a DBMS. - -See [Switch to Another DBMS for Entity Framework Core](Entity-Framework-Core-Other-DBMS.md) document to learn how to switch the DBMS. +> See [Switch to Another DBMS for Entity Framework Core](Entity-Framework-Core-Other-DBMS.md) document to learn how to switch the DBMS. ## Creating DbContext -You can create your DbContext as you normally do. It should be derived from `AbpDbContext` as shown below: +Your `DbContext` class should be derived from `AbpDbContext` as shown below: ````C# using Microsoft.EntityFrameworkCore; @@ -107,7 +105,7 @@ protected override void OnModelCreating(ModelBuilder builder) ### Configure the Connection String Selection -If you have multiple databases in your application, you can configure the connection string name for your DbContext using the `[ConnectionStringName]` attribute. Example: +If you have multiple databases in your application, you can configure the connection string name for your `DbContext` using the `[ConnectionStringName]` attribute. Example: ```csharp [ConnectionStringName("MySecondConnString")] @@ -119,9 +117,65 @@ public class MyDbContext : AbpDbContext If you don't configure, the `Default` connection string is used. If you configure a specific connection string name, but not define this connection string name in the application configuration then it fallbacks to the `Default` connection string (see [the connection strings document](Connection-Strings.md) for more information). +### AbpDbContextOptions + +`AbpDbContextOptions` is used to configure the `DbContext` options. When you create a new solution with the ABP's application startup template, you will see a simple configuration (in the `EntityFrameworkCore` integration project's module class) as shown below: + +````csharp +Configure(options => +{ + options.UseSqlServer(); +}); +```` + +That configuration configures the default DBMS as SQL Server for all the `DbContext`s of the application. That configuration was a shorthand notation and it can be done with the following code block: + +````csharp +Configure(options => +{ + options.Configure(opts => + { + opts.UseSqlServer(); + }); +}); +```` + +`options.Configure(...)` method has more options to configure. For example, you can set `DbContextOptions` (EF Core's native options) as shown below: + +````csharp +Configure(options => +{ + options.Configure(opts => + { + opts.DbContextOptions.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + }); +}); +```` + +If you have a single `DbContext` or you have multiple `DbContext`s but want to use the same DBMS and configuration for all, you can leave it as is. However, if you need to configure a different DBMS or customize the configuration for a specific `DbContext`, you can specify it as shown below: + +````csharp +Configure(options => +{ + // Default configuration for all DbContexts + options.Configure(opts => + { + opts.UseSqlServer(); + }); + + // Customized configuration for a specific DbContext + options.Configure(opts => + { + opts.UseMySQL(); + }); +}); +```` + +> See [Switch to Another DBMS for Entity Framework Core](Entity-Framework-Core-Other-DBMS.md) document to learn how to configure the DBMS. + ## Registering DbContext To Dependency Injection -Use `AddAbpDbContext` method in your module to register your DbContext class for [dependency injection](Dependency-Injection.md) system. +Use `AddAbpDbContext` method in your module to register your `DbContext` class for [dependency injection](Dependency-Injection.md) system. ````C# using Microsoft.Extensions.DependencyInjection; diff --git a/docs/en/Getting-Started-Running-Solution.md b/docs/en/Getting-Started-Running-Solution.md index 3f508589a3..27e6ac3dee 100644 --- a/docs/en/Getting-Started-Running-Solution.md +++ b/docs/en/Getting-Started-Running-Solution.md @@ -139,7 +139,7 @@ Ensure that the `.IdentityServer` project is the startup project. Run the applic > 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. -You can login, but you cannot enter to the main application here. This is just the authentication server. +You can login, but you cannot enter to the main application here. This is **just the authentication server**. Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a Swagger UI: diff --git a/docs/en/Getting-Started.md b/docs/en/Getting-Started.md index b3fd215e35..0304eb2320 100644 --- a/docs/en/Getting-Started.md +++ b/docs/en/Getting-Started.md @@ -15,6 +15,6 @@ This tutorial explains how to **create and run** a new web application using the ABP Framework. Follow the steps below; -1. [Setup your development environment](Getting-Started-Setup-Environment) +1. [Setup your development environment](Getting-Started-Setup-Environment.md) 2. [Creating a new solution](Getting-Started-Create-Solution.md) 3. [Running the solution](Getting-Started-Running-Solution.md) \ No newline at end of file diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 210ecbe966..93bf3d4a0b 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -13,7 +13,7 @@ In this tutorial series, you will build an ABP based web application named `Acme * **{{DB_Value}}** as the database provider. * **{{UI_Value}}** as the UI Framework. -This tutorial is organized as the following parts; +This tutorial is organized as the following parts: - **Part 1: Creating the server side (this part)** - [Part 2: The book list page](Part-2.md) @@ -34,7 +34,7 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer * [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) * [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) -> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). +> If you encounter the "filename too long" or "unzip" error on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). > If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path > `git config --system core.longpaths true` @@ -49,14 +49,14 @@ This part is also recorded as a video tutorial and ** @@ -147,7 +147,7 @@ public class BookStoreMongoDbContext : AbpMongoDbContext ### Map the Book Entity to a Database Table -Locate to `OnModelCreating` method in the `BookStoreDbContext` class and add the mapping code for the `Book` entity: +Navigate to the `OnModelCreating` method in the `BookStoreDbContext` class and add the mapping code for the `Book` entity: ````csharp using Acme.BookStore.Books; @@ -185,8 +185,8 @@ namespace Acme.BookStore.EntityFrameworkCore } ```` -* `BookStoreConsts` has constant values for schema and table prefixes for your tables. You don't have to use it, but suggested to control the table prefixes in a single point. -* `ConfigureByConvention()` method gracefully configures/maps the inherited properties. Always use it for all your entities. +* `BookStoreConsts` has constant values for the schema and table prefixes for your tables. You don't have to use it, but it's suggested to control the table prefixes in a single point. +* The `ConfigureByConvention()` method gracefully configures/maps the inherited properties. Always use it for all your entities. ### Add Database Migration @@ -202,13 +202,13 @@ This will add a new migration class to the project: ![bookstore-efcore-migration](./images/bookstore-efcore-migration.png) -> If you are using Visual Studio, you may want to use `Add-Migration Created_Book_Entity -c BookStoreMigrationsDbContext` and `Update-Database -c BookStoreMigrationsDbContext` commands in the *Package Manager Console (PMC)*. In this case, ensure that {{if UI=="MVC"}}`Acme.BookStore.Web`{{else if UI=="BlazorServer"}}`Acme.BookStore.Blazor`{{else if UI=="Blazor" || UI=="NG"}}`Acme.BookStore.HttpApi.Host`{{end}} is the startup project and `Acme.BookStore.EntityFrameworkCore` is the *Default Project* in PMC. +> If you are using Visual Studio, you may want to use the `Add-Migration Created_Book_Entity -c BookStoreMigrationsDbContext` and `Update-Database -c BookStoreMigrationsDbContext` commands in the *Package Manager Console (PMC)*. In this case, ensure that {{if UI=="MVC"}}`Acme.BookStore.Web`{{else if UI=="BlazorServer"}}`Acme.BookStore.Blazor`{{else if UI=="Blazor" || UI=="NG"}}`Acme.BookStore.HttpApi.Host`{{end}} is the startup project and `Acme.BookStore.EntityFrameworkCore` is the *Default Project* in PMC. {{end}} ### Add Sample Seed Data -> It's good to have some initial data in the database before running the application. This section introduces the [Data Seeding](../Data-Seeding.md) system of the ABP framework. You can skip this section if you don't want to create seed data, but it is suggested to follow it to learn this useful ABP Framework feature. +> It's good to have some initial data in the database before running the application. This section introduces the [Data Seeding](../Data-Seeding.md) system of the ABP framework. You can skip this section if you don't want to create the data seeding, but it is suggested to follow along and learn this useful ABP Framework feature. Create a class deriving from the `IDataSeedContributor` in the `*.Domain` project by copying the following code: @@ -263,7 +263,7 @@ namespace Acme.BookStore } ``` -* This code simply uses the `IRepository` (the default [repository](../Repositories.md)) to insert two books to the database, if there is no book currently in the database. +* This code simply uses the `IRepository` (the default [repository](../Repositories.md)) to insert two books to the database in case there weren't any books in it. ### Update the Database @@ -271,7 +271,6 @@ Run the `Acme.BookStore.DbMigrator` application to update the database: ![bookstore-dbmigrator-on-solution](images/bookstore-dbmigrator-on-solution.png) - `.DbMigrator` is a console application that can be run to **migrate the database schema** and **seed the data** on **development** and **production** environments. ## Create the Application Service @@ -307,10 +306,10 @@ namespace Acme.BookStore.Books ```` * **DTO** classes are used to **transfer data** between the *presentation layer* and the *application layer*. See the [Data Transfer Objects document](https://docs.abp.io/en/abp/latest/Data-Transfer-Objects) for more details. -* `BookDto` is used to transfer book data to the presentation layer in order to show the book information on the UI. -* `BookDto` is derived from the `AuditedEntityDto` which has audit properties just like the `Book` entity defined above. +* The `BookDto` is used to transfer the book data to the presentation layer in order to show the book information on the UI. +* The `BookDto` is derived from the `AuditedEntityDto` which has audit properties just like the `Book` entity defined above. -It will be needed to map `Book` entities to `BookDto` objects while returning books to the presentation layer. [AutoMapper](https://automapper.org) library can automate this conversion when you define the proper mapping. The startup template comes with AutoMapper pre-configured. So, you can just define the mapping in the `BookStoreApplicationAutoMapperProfile` class in the `Acme.BookStore.Application` project: +It will be needed to map the `Book` entities to the `BookDto` objects while returning books to the presentation layer. [AutoMapper](https://automapper.org) library can automate this conversion when you define the proper mapping. The startup template comes with AutoMapper pre-configured. So, you can just define the mapping in the `BookStoreApplicationAutoMapperProfile` class in the `Acme.BookStore.Application` project: ````csharp using Acme.BookStore.Books; @@ -359,10 +358,10 @@ namespace Acme.BookStore.Books } ```` -* This `DTO` class is used to get book information from the user interface while creating or updating a book. +* This `DTO` class is used to get a book information from the user interface while creating or updating the book. * It defines data annotation attributes (like `[Required]`) to define validations for the properties. `DTO`s are [automatically validated](https://docs.abp.io/en/abp/latest/Validation) by the ABP framework. -Just like done for the `BookDto` above, we should define the mapping from the `CreateUpdateBookDto` object to the `Book` entity. The final class will be like shown below: +As done to the `BookDto` above, we should define the mapping from the `CreateUpdateBookDto` object to the `Book` entity. The final class will be as shown below: ````csharp using Acme.BookStore.Books; @@ -440,13 +439,13 @@ namespace Acme.BookStore.Books * `BookAppService` is derived from `CrudAppService<...>` which implements all the CRUD (create, read, update, delete) methods defined by the `ICrudAppService`. * `BookAppService` injects `IRepository` which is the default repository for the `Book` entity. ABP automatically creates default repositories for each aggregate root (or entity). See the [repository document](https://docs.abp.io/en/abp/latest/Repositories). -* `BookAppService` uses `IObjectMapper` service ([see](../Object-To-Object-Mapping.md)) to map `Book` objects to `BookDto` objects and `CreateUpdateBookDto` objects to `Book` objects. The Startup template uses the [AutoMapper](http://automapper.org/) library as the object mapping provider. We have defined the mappings before, so it will work as expected. +* `BookAppService` uses `IObjectMapper` service ([see](../Object-To-Object-Mapping.md)) to map the `Book` objects to the `BookDto` objects and `CreateUpdateBookDto` objects to the `Book` objects. The Startup template uses the [AutoMapper](http://automapper.org/) library as the object mapping provider. We have defined the mappings before, so it will work as expected. ## Auto API Controllers -In a typical ASP.NET Core application, you create **API Controllers** to expose application services as **HTTP API** endpoints. This allows browsers or 3rd-party clients to call them over HTTP. +In a typical ASP.NET Core application, you create **API Controllers** to expose the application services as **HTTP API** endpoints. This allows browsers or 3rd-party clients to call them over HTTP. -ABP can [**automagically**](../API/Auto-API-Controllers.md) configures your application services as MVC API Controllers by convention. +ABP can [**automagically**](../API/Auto-API-Controllers.md) configure your application services as MVC API Controllers by convention. ### Swagger UI diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 7c8efde9d0..6d636d5ecb 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -10,10 +10,10 @@ In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies: -* **{{DB_Value}}** as the ORM provider. +* **{{DB_Value}}** as the ORM provider. * **{{UI_Value}}** as the UI Framework. -This tutorial is organized as the following parts; +This tutorial is organized as the following parts: - [Part 1: Creating the server side](Part-1.md) - **Part 2: The book list page (this part)** @@ -53,7 +53,7 @@ This part is also recorded as a video tutorial and ** Razor Page** menu item. Name it as `Index`: +Create a `Books` folder under the `Pages` folder of the `Acme.BookStore.Web` project. Add a new Razor Page by right clicking the Books folder then selecting **Add > Razor Page** menu item. Name it as `Index`: ![bookstore-add-index-page](images/bookstore-add-index-page-v2.png) @@ -187,7 +187,7 @@ namespace Acme.BookStore.Web.Pages.Books { public void OnGet() { - + } } } @@ -213,21 +213,21 @@ context.Menu.AddItem( ); ```` -Run the project, login to the application with the username `admin` and the password `1q2w3E*` and see the new menu item has been added to the main menu: +Run the project, login to the application with the username `admin` and the password `1q2w3E*` and you can see that the new menu item has been added to the main menu: ![bookstore-menu-items](images/bookstore-new-menu-item.png) -When you click to the Books menu item under the Book Store parent, you are being redirected to the new empty Books Page. +When you click on the Books menu item under the Book Store parent, you will be redirected to the new empty Books Page. ### Book List -We will use the [Datatables.net](https://datatables.net/) jQuery library to show the book list. Datatables library completely work via AJAX, it is fast, popular and provides a good user experience. +We will use the [Datatables.net](https://datatables.net/) jQuery library to show the book list. Datatables library completely works via AJAX, it is fast, popular and provides a good user experience. -> Datatables library is configured in the startup template, so you can directly use it in any page without including any style or script file to your page. +> Datatables library is configured in the startup template, so you can directly use it in any page without including any style or script file for your page. #### Index.cshtml -Change the `Pages/Books/Index.cshtml` as following: +Change the `Pages/Books/Index.cshtml` as the following: ````html @page @@ -250,8 +250,8 @@ Change the `Pages/Books/Index.cshtml` as following: ```` -* `abp-script` [tag helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro) is used to add external **scripts** to the page. It has many additional features compared to standard `script` tag. It handles **minification** and **versioning**. See the [bundling & minification document](../UI/AspNetCore/Bundling-Minification.md) for details. -* `abp-card` is a tag helper for Twitter Bootstrap's [card component](https://getbootstrap.com/docs/4.5/components/card/). There are other useful tag helpers provided by the ABP Framework to easily use most of the [bootstrap](https://getbootstrap.com/) components. You could use the regular HTML tags instead of these tag helpers, but using tag helpers reduces HTML code and prevents errors by help the of IntelliSense and compile time type checking. Further information, see the [tag helpers](../UI/AspNetCore/Tag-Helpers/Index.md) document. +* `abp-script` [tag helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro) is used to add external **scripts** to the page. It has many additional features compared to the standard `script` tag. It handles **minification** and **versioning**. Check the [bundling & minification document](../UI/AspNetCore/Bundling-Minification.md) for details. +* `abp-card` is a tag helper for Twitter Bootstrap's [card component](https://getbootstrap.com/docs/4.5/components/card/). There are other useful tag helpers provided by the ABP Framework to easily use most of [bootstrap](https://getbootstrap.com/)'s components. You could use the regular HTML tags instead of these tag helpers, but using tag helpers reduces HTML code and prevents errors by the help of the IntelliSense and compiles time type checking. For further information, check the [tag helpers](../UI/AspNetCore/Tag-Helpers/Index.md) document. #### Index.js @@ -316,9 +316,9 @@ $(function () { }); ```` -* `abp.localization.getResource` gets a function that is used to localize text using the same JSON file defined in the server side. In this way, you can share the localization values with the client side. +* `abp.localization.getResource` gets a function that is used to localize text using the same JSON file defined on the server side. In this way, you can share the localization values with the client side. * `abp.libs.datatables.normalizeConfiguration` is a helper function defined by the ABP Framework. There's no requirement to use it, but it simplifies the [Datatables](https://datatables.net/) configuration by providing conventional default values for missing options. -* `abp.libs.datatables.createAjax` is another helper function to adapt ABP's dynamic JavaScript API proxies to [Datatable](https://datatables.net/)'s expected parameter format +* `abp.libs.datatables.createAjax` is another helper function to adapt the ABP's dynamic JavaScript API proxies to the [Datatable](https://datatables.net/)'s expected parameter format * `acme.bookStore.books.book.getList` is the dynamic JavaScript proxy function introduced before. * [luxon](https://moment.github.io/luxon/) library is also a standard library that is pre-configured in the solution, so you can use to perform date/time operations easily. @@ -336,9 +336,9 @@ This is a fully working, server side paged, sorted and localized table of books. ## Install NPM packages -> Notice: This tutorial is based on the ABP Framework v3.1.0+ If your project version is older, then please upgrade your solution. See the [migration guide](../UI/Angular/Migration-Guide-v3.md) if you are upgrading an existing project with v2.x. +> Notice: This tutorial is based on the ABP Framework v3.1.0+ If your project version is older, then please upgrade your solution. Check the [migration guide](../UI/Angular/Migration-Guide-v3.md) if you are upgrading an existing project with v2.x. -If you haven't done it before, open a new command line interface (terminal window) and go to your `angular` folder and then run `yarn` command to install the NPM packages: +If you haven't done it before, open a new command line interface (terminal window) and go to your `angular` folder and then run the `yarn` command to install the NPM packages: ```bash yarn @@ -400,7 +400,7 @@ export class BookModule { } ### Routing -Generated code places the new route definition to the `src/app/app-routing.module.ts` file as shown below: +The generated code places the new route definition to the `src/app/app-routing.module.ts` file as shown below: ````js const routes: Routes = [ @@ -409,7 +409,7 @@ const routes: Routes = [ ]; ```` -Now, open the `src/app/route.provider.ts` file replace the `configureRoutes` function declaration as shown below: +Now, open the `src/app/route.provider.ts` file and replace the `configureRoutes` function declaration as shown below: ```js function configureRoutes(routes: RoutesService) { @@ -443,18 +443,18 @@ function configureRoutes(routes: RoutesService) { `RoutesService` is a service provided by the ABP Framework to configure the main menu and the routes. * `path` is the URL of the route. -* `name` is the localized menu item name (see the [localization document](../UI/Angular/Localization.md) for details). +* `name` is the localized menu item name (check the [localization document](../UI/Angular/Localization.md) for details). * `iconClass` is the icon of the menu item (you can use [Font Awesome](https://fontawesome.com/) icons by default). * `order` is the order of the menu item. * `layout` is the layout of the BooksModule's routes (there are three types of pre-defined layouts: `eLayoutType.application`, `eLayoutType.account` or `eLayoutType.empty`). -For more information, see the [RoutesService document](../UI/Angular/Modifying-the-Menu.md#via-routesservice). +For more information, check the [RoutesService document](../UI/Angular/Modifying-the-Menu.md#via-routesservice). ### Service Proxy Generation -[ABP CLI](../CLI.md) provides `generate-proxy` command that generates client proxies for your HTTP APIs to make easy to consume your HTTP APIs from the client side. Before running `generate-proxy` command, your host must be up and running. +[ABP CLI](../CLI.md) provides a `generate-proxy` command that generates client proxies for your HTTP APIs to make your HTTP APIs easy to consume by the client side. Before running the `generate-proxy` command, your host must be up and running. -> **Warning**: There is a problem with IIS Express; it doesn't allow to connect to the application from another process. If you are using Visual Studio, select the `Acme.BookStore.HttpApi.Host` instead of IIS Express in the run button drop-down list, as shown in the figure below: +> **Warning**: There is a problem with IIS Express; it doesn't allow connecting to the application from another process. If you are using Visual Studio, select the `Acme.BookStore.HttpApi.Host` instead of IIS Express in the run button drop-down list, as shown in the figure below: ![vs-run-without-iisexpress](images/vs-run-without-iisexpress.png) @@ -499,9 +499,9 @@ export class BookComponent implements OnInit { ``` * We imported and injected the generated `BookService`. -* We are using the [ListService](../UI/Angular/List-Service.md), a utility service of the ABP Framework which provides easy pagination, sorting and searching. +* We are using the [ListService](../UI/Angular/List-Service.md), a utility service from the ABP Framework which provides easy pagination, sorting and searching. -Open the `/src/app/book/book.component.html` and replace the content as below: +Open the `/src/app/book/book.component.html` and replace the content as shown below: ```html
@@ -546,7 +546,7 @@ Now you can see the final result on your browser: ## Create a Books Page -It's time to create something visible and usable! Right click to the `Pages` folder under the `Acme.BookStore.Blazor` project and add a new **razor component**, named `Books.razor`: +It's time to create something visible and usable! Right click on the `Pages` folder under the `Acme.BookStore.Blazor` project and add a new **razor component**, named `Books.razor`: ![blazor-add-books-component](images/blazor-add-books-component.png) @@ -562,7 +562,7 @@ Replace the contents of this component as shown below: } ```` -### Add Books Page to the Main Menu +### Add the Books Page to the Main Menu Open the `BookStoreMenuContributor` class in the `Blazor` project add the following code to the end of the `ConfigureMainMenuAsync` method: @@ -582,17 +582,17 @@ context.Menu.AddItem( ); ```` -Run the project, login to the application with the username `admin` and the password `1q2w3E*` and see the new menu item has been added to the main menu: +Run the project, login to the application with the username `admin` and the password `1q2w3E*` and see that the new menu item has been added to the main menu: ![blazor-menu-bookstore](images/blazor-menu-bookstore.png) -When you click to the Books menu item under the Book Store parent, you are being redirected to the new empty Books Page. +When you click on the Books menu item under the Book Store parent, you will be redirected to the new empty Books Page. ### Book List -We will use the [Blazorise library](https://blazorise.com/) as the UI component kit. It is a very powerful library that supports major HTML/CSS frameworks, including the Bootstrap. +We will use the [Blazorise library](https://blazorise.com/) as the UI component kit. It is a very powerful library that supports major HTML/CSS frameworks, including Bootstrap. -ABP Framework provides a generic base class, `AbpCrudPageBase<...>`, to create CRUD style pages. This base class is compatible to the `ICrudAppService` that was used to build the `IBookAppService`. So, we can inherit from the `AbpCrudPageBase` to automate the code behind for the standard CRUD stuff. +ABP Framework provides a generic base class - `AbpCrudPageBase<...>`, to create CRUD style pages. This base class is compatible with the `ICrudAppService` that was used to build the `IBookAppService`. So, we can inherit from the `AbpCrudPageBase` to automate the code behind for the standard CRUD stuff. Open the `Books.razor` and replace the content as the following: @@ -651,17 +651,17 @@ Open the `Books.razor` and replace the content as the following: ```` -> If you see some syntax errors, you can ignore them if your application properly built and run. Visual Studio still has some bugs with Blazor. +> If you see some syntax errors, you can ignore them if your application is properly built and running. Visual Studio still has some bugs with Blazor. -* Inherited from the `AbpCrudPageBase` which implements all the CRUD details for us. -* `Entities`, `TotalCount`, `PageSize`, `OnDataGridReadAsync` are defined in the base blass. +* Inherited from `AbpCrudPageBase` which implements all the CRUD details for us. +* `Entities`, `TotalCount`, `PageSize`, `OnDataGridReadAsync` are defined in the base class. * Injected `IStringLocalizer` (as `L` object) and used for localization. -While the code above pretty easy to understand, you can check the Blazorise [Card](https://blazorise.com/docs/components/card/) and [DataGrid](https://blazorise.com/docs/extensions/datagrid/) documents to understand them better. +While the code above is pretty easy to understand, you can check the Blazorise [Card](https://blazorise.com/docs/components/card/) and [DataGrid](https://blazorise.com/docs/extensions/datagrid/) documents to understand them better. #### About the AbpCrudPageBase -We will continue to benefit from the `AbpCrudPageBase` for the books page. You could just inject the `IBookAppService` and perform all the server side calls yourself (thanks to the [Dynamic C# HTTP API Client Proxy](../API/Dynamic-CSharp-API-Clients.md) system of the ABP Framework). We will do it manually for the authors page to demonstrate how to call server side HTTP APIs in your Blazor applications. +We will continue benefitting from `AbpCrudPageBase` for the books page. You could just inject the `IBookAppService` and perform all the server side calls yourself (thanks to the [Dynamic C# HTTP API Client Proxy](../API/Dynamic-CSharp-API-Clients.md) system of the ABP Framework). We will do it manually for the authors page to demonstrate how to call the server side HTTP APIs in your Blazor applications. ## Run the Final Application @@ -675,4 +675,4 @@ This is a fully working, server side paged, sorted and localized table of books. ## The Next Part -See the [next part](Part-3.md) of this tutorial. +Check the [next part](Part-3.md) of this tutorial. diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index 92e04d0b41..f6a59f1108 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -13,7 +13,7 @@ In this tutorial series, you will build an ABP based web application named `Acme * **{{DB_Value}}** as the ORM provider. * **{{UI_Value}}** as the UI Framework. -This tutorial is organized as the following parts; +This tutorial is organized as the following parts: - [Part 1: Creating the server side](Part-1.md) - [Part 2: The book list page](Part-2.md) @@ -51,19 +51,19 @@ This part is also recorded as a video tutorial and ** ``` -* Added `New book` button to the card header.. +* Added a `New book` button to the card header.. * Added the `abp-modal` which renders a modal to allow user to create a new book. `abp-modal` is a pre-built component to show modals. While you could use another approach to show a modal, `abp-modal` provides additional benefits. -You can open your browser and click **New book** button to see the new modal. +You can open your browser and click the **New book** button to see the new modal. ![Empty modal for new book](images/bookstore-empty-new-book-modal.png) @@ -801,11 +801,11 @@ export class BookComponent implements OnInit { ``` * Imported `FormGroup`, `FormBuilder` and `Validators` from `@angular/forms`. -* Added `form: FormGroup` property. -* Added `bookTypes` property as a list of `BookType` enum members. That will be used in form options. +* Added a `form: FormGroup` property. +* Added a `bookTypes` property as a list of `BookType` enum members. That will be used in form options. * Injected `FormBuilder` into the constructor. [FormBuilder](https://angular.io/api/forms/FormBuilder) provides convenient methods for generating form controls. It reduces the amount of boilerplate needed to build complex forms. -* Added `buildForm` method to the end of the file and executed the `buildForm()` in the `createBook` method. -* Added `save` method. +* Added a `buildForm` method to the end of the file and executed the `buildForm()` in the `createBook` method. +* Added a `save` method. Open `/src/app/book/book.component.html` and replace ` ` with the following code part: @@ -863,7 +863,7 @@ Also replace ` ` with the following code p ### Datepicker -We've used [NgBootstrap datepicker](https://ng-bootstrap.github.io/#/components/datepicker/overview) in this component. So, need to arrange dependencies related to this component. +We've used [NgBootstrap datepicker](https://ng-bootstrap.github.io/#/components/datepicker/overview) in this component. So, we need to arrange the dependencies related to this component. Open `/src/app/book/book.module.ts` and replace the content as below: @@ -959,7 +959,7 @@ export class BookComponent implements OnInit { ``` * Imported ` NgbDateNativeAdapter` and `NgbDateAdapter`. -* We added a new provider `NgbDateAdapter` that converts Datepicker value to `Date` type. See the [datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview) for more details. +* We added a new provider `NgbDateAdapter` that converts the Datepicker value to `Date` type. Check out the [datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview) for more details. Now, you can open your browser to see the changes: @@ -1054,14 +1054,14 @@ export class BookComponent implements OnInit { ``` * We declared a variable named `selectedBook` as `BookDto`. -* We added `editBook` method. This method fetches the book with the given `id` and sets it to `selectedBook` object. +* We added an `editBook` method. This method fetches the book with the given `id` and sets it to `selectedBook` object. * We replaced the `buildForm` method so that it creates the form with the `selectedBook` data. * We replaced the `createBook` method so it sets `selectedBook` to an empty object. * We changed the `save` method to handle both of create and update operations. ### Add "Actions" Dropdown to the Table -Open the `/src/app/book/book.component.html`  and add the following `ngx-datatable-column` definition as the first column in the `ngx-datatable`: +Open `/src/app/book/book.component.html`  and add the following `ngx-datatable-column` definition as the first column in the `ngx-datatable`: ```html ``` -This template will show **Edit** text for edit record operation, **New Book** for new record operation in the title. +This template will show the **Edit** text for edit record operation, **New Book** for new record operation in the title. ## Deleting a Book -Open the `/src/app/book/book.component.ts` and inject the `ConfirmationService`. +Open the `/src/app/book/book.component.ts` file and inject the `ConfirmationService`. Replace the constructor as below: @@ -1137,7 +1137,7 @@ delete(id: string) { * We injected `ConfirmationService` to the constructor. * Added a `delete` method. -> See the [Confirmation Popup documentation](../UI/Angular/Confirmation-Service) for more about this service. +> Check out the [Confirmation Popup documentation](../UI/Angular/Confirmation-Service) for more about this service. ### Add a Delete Button @@ -1169,7 +1169,7 @@ Clicking the "Delete" action calls the `delete` method which then shows a confir In this section, you will learn how to create a new modal dialog form to create a new book. Since we've inherited from the `AbpCrudPageBase`, we only need to develop the view part. -### Add "New Button" Button +### Add a "New Button" Button Open the `Books.razor` and replace the `` section with the following code: @@ -1191,7 +1191,7 @@ This will change the card header by adding a "New book" button to the right side ![blazor-add-book-button](images/blazor-add-book-button.png) -Now, we can add a modal that will be opened when we click to the button. +Now, we can add a modal that will be opened when we click the button. ### Book Creation Modal @@ -1259,7 +1259,7 @@ This code requires a service; Inject the `AbpBlazorMessageLocalizerHelper` at ```` * The form implements validation and the `AbpBlazorMessageLocalizerHelper` is used to simply localize the validation messages. -* `CreateModal` object, `CloseCreateModalAsync` and `CreateEntityAsync` method are defined by the base class. See the [Blazorise documentation](https://blazorise.com/docs/) if you want to understand the `Modal` and the other components. +* The `CreateModal` object, `CloseCreateModalAsync` and `CreateEntityAsync` methods are defined by the base class. Check out the [Blazorise documentation](https://blazorise.com/docs/) if you want to understand the `Modal` and the other components. That's all. Run the application and try to add a new book: @@ -1267,7 +1267,7 @@ That's all. Run the application and try to add a new book: ## Updating a Book -Editing a books is similar to the creating a new book. +Editing a book is similar to creating a new book. ### Actions Dropdown @@ -1287,7 +1287,7 @@ Open the `Books.razor` and add the following `DataGridEntityActionsColumn` secti * `OpenEditModalAsync` is defined in the base class which takes the entity (book) to edit. -`DataGridEntityActionsColumn` component is used to show an "Actions" dropdown for each row in the `DataGrid`. `DataGridEntityActionsColumn` shows a **single button** instead of a dropdown if there is only one available action inside it: +The `DataGridEntityActionsColumn` component is used to show an "Actions" dropdown for each row in the `DataGrid`. The `DataGridEntityActionsColumn` shows a **single button** instead of a dropdown if there is only one available action inside it: ![blazor-edit-book-action](images/blazor-edit-book-action-2.png) @@ -1384,7 +1384,7 @@ You can now run the application and try to edit a book. ## Deleting a Book -Open the `Books.razor` page and add the following `EntityAction` under the "Edit" action inside the `EntityActions`: +Open the `Books.razor` page and add the following `EntityAction` code under the "Edit" action inside `EntityActions`: ````xml TodoItems { get; set; } ```` -Then locate to `OnModelCreating` method in the `TodoAppDbContext` class and add the mapping code for the `TodoItem ` entity: +Then navigate to the `OnModelCreating` method in the `TodoAppDbContext` class and add the mapping code for the `TodoItem ` entity: ````csharp protected override void OnModelCreating(ModelBuilder builder) @@ -171,7 +171,7 @@ protected override void OnModelCreating(ModelBuilder builder) } ```` -We've mapped `TodoItem` entity to a `TodoItems` table in the database. +We've mapped the `TodoItem` entity to the `TodoItems` table in the database. ### Code First Migrations @@ -193,11 +193,11 @@ You can apply changes to the database using the following command, in the same c dotnet ef database update ```` -> If you are using Visual Studio, you may want to use `Add-Migration Added_TodoItem` and `Update-Database` commands in the *Package Manager Console (PMC)*. In this case, ensure that {{if UI=="MVC"}}`TodoApp.Web`{{else if UI=="BlazorServer"}}`TodoApp.Blazor`{{else if UI=="Blazor" || UI=="NG"}}`TodoApp.HttpApi.Host`{{end}} is the startup project and `TodoApp.EntityFrameworkCore` is the *Default Project* in PMC. +> If you are using Visual Studio, you may want to use the `Add-Migration Added_TodoItem` and `Update-Database` commands in the *Package Manager Console (PMC)*. In this case, ensure that {{if UI=="MVC"}}`TodoApp.Web`{{else if UI=="BlazorServer"}}`TodoApp.Blazor`{{else if UI=="Blazor" || UI=="NG"}}`TodoApp.HttpApi.Host`{{end}} is the startup project and `TodoApp.EntityFrameworkCore` is the *Default Project* in PMC. {{else if DB=="Mongo"}} -Next step is to setup the [MongoDB](../../MongoDB.md) configuration. Open the `TodoAppMongoDbContext` class in the `MongoDb` folder of the *TodoApp.MongoDB* project and make the following changes; +Next step is to setup the [MongoDB](../../MongoDB.md) configuration. Open the `TodoAppMongoDbContext` class in the `MongoDb` folder of the *TodoApp.MongoDB* project and make the following changes: 1. Add a new property to the class: @@ -216,13 +216,13 @@ modelBuilder.Entity(b => {{end}} -Now, we can use ABP repositories to save and retrieve todo items, as we'll do in the next section. +Now, we can use the ABP repositories to save and retrieve the todo items, as we'll do in the next section. ## Application Layer -An [Application Service](../../Application-Services.md) is used to perform use cases of the application. We need to perform the following use cases: +An [Application Service](../../Application-Services.md) is used to perform the use cases of the application. We need to perform the following use cases: -* Get the list of todo items +* Get the list of the todo items * Create a new todo item * Delete an existing todo item @@ -264,7 +264,7 @@ namespace TodoApp } ```` -This is a very simple DTO class that matches to our `TodoItem` entity. We are ready to implement the `ITodoAppService`. +This is a very simple DTO class that matches our `TodoItem` entity. We are ready to implement the `ITodoAppService`. ### Application Service Implementation @@ -313,7 +313,7 @@ public async Task> GetListAsync() } ```` -We are simply getting the complete `TodoItem` list from database, mapping them to `TodoItemDto` objects and returning as the result. +We are simply getting the complete `TodoItem` list from the database, mapping them to `TodoItemDto` objects and returning as the result. #### Creating a New Todo Item @@ -334,7 +334,7 @@ public async Task CreateAsync(string text) } ```` -Repository's `InsertAsync` method inserts the given `TodoItem` to database and returns the same `TodoItem` object. It also sets the `Id`, so we can use it on the returning object. We are simply returning a `TodoItemDto` by creating from the new `TodoItem` entity. +The repository's `InsertAsync` method inserts the given `TodoItem` to the database and returns the same `TodoItem` object. It also sets the `Id`, so we can use it on the returning object. We are simply returning a `TodoItemDto` by creating from the new `TodoItem` entity. #### Deleting a Todo Item @@ -351,7 +351,7 @@ The application service is ready to be used from the UI layer. ## User Interface Layer -It is time to show the todo items on the UI! Before starting to write the code, it would be good to remember what we are trying to build. Here, a sample screenshot from the final UI: +It is time to show the todo items on the UI! Before starting to write the code, it would be good to remember what we are trying to build. Here's a sample screenshot from the final UI: ![todo-list](todo-list.png) @@ -388,11 +388,11 @@ namespace TodoApp.Web.Pages } ```` -This class uses the `ITodoAppService` to get the list of todo items and assign the the `TodoItems` property. We will use it to render the todo items on the razor page. +This class uses the `ITodoAppService` to get the list of todo items and assign the `TodoItems` property. We will use it to render the todo items on the razor page. ### Index.cshtml -Open the `Index.cshtml` file in the `Pages` folder of the *TodoApp.Web* project and replace with the following content: +Open the `Index.cshtml` file in the `Pages` folder of the *TodoApp.Web* project and replace it with the following content: ````xml @page @@ -442,7 +442,7 @@ This page imports a CSS and a JavaScript file, so we should also create them. ### Index.js -Open the `Index.js` file in the `Pages` folder of the *TodoApp.Web* project and replace with the following content: +Open the `Index.js` file in the `Pages` folder of the *TodoApp.Web* project and replace it with the following content: ````js $(function () { @@ -473,15 +473,15 @@ $(function () { }); ```` -In the first part, we are subscribing to click events of the trash icons near to the todo items, deleting the related item on the server and showing a notification on the UI. Also, we are removing the deleted item from DOM, so we don't need to refresh the page. +In the first part, we are subscribing to the click events of the trash icons near the todo items, deleting the related item on the server and showing a notification on the UI. Also, we are removing the deleted item from the DOM, so we don't need to refresh the page. -In the second part, we are creating a new todo item on the server. If it succeeds, we are then manipulating DOM to insert a new `
  • ` element to the todo list. This way we don't need to refresh the whole page after creating a new todo item. +In the second part, we are creating a new todo item on the server. If it succeeds, we are then manipulating the DOM to insert a new `
  • ` element to the todo list. This way we don't need to refresh the whole page after creating a new todo item. The interesting part here is how we communicate with the server. See the *Dynamic JavaScript Proxies & Auto API Controllers* section to understand how it works. But now, let's continue and complete the application. ### Index.css -As the final touch, open the `Index.css` file in the `Pages` folder of the *TodoApp.Web* project and replace with the following content: +As the final touch, open the `Index.css` file in the `Pages` folder of the *TodoApp.Web* project and replace it with the following content: ````css #TodoList{ @@ -516,9 +516,9 @@ Now, you can run the application again and see the result. ### Dynamic JavaScript Proxies & Auto API Controllers -In the `Index.js` file, we've used `todoApp.todo.delete(...)` and `todoApp.todo.create(...)` functions to communicate with the server. These functions are dynamically created by the ABP Framework, thanks to the [Dynamic JavaScript Client Proxy](../../UI/AspNetCore/Dynamic-JavaScript-Proxies.md) system. They perform HTTP API calls to the server and return a promise, so you can register a callback to the `then` function as we've done above. +In the `Index.js` file, we've used the `todoApp.todo.delete(...)` and `todoApp.todo.create(...)` functions to communicate with the server. These functions are dynamically created by the ABP Framework, thanks to the [Dynamic JavaScript Client Proxy](../../UI/AspNetCore/Dynamic-JavaScript-Proxies.md) system. They perform HTTP API calls to the server and return a promise, so you can register a callback to the `then` function as we've done above. -However, you may notice that we haven't created any API Controller, so how server handles these requests? This question brings us the [Auto API Controller](../../API/Auto-API-Controllers.md) feature of the ABP Framework. It automatically converts the application services to API Controllers by convention. +However, you may notice that we haven't created any API Controllers, so how does the server handle these requests? This question brings us to the [Auto API Controller](../../API/Auto-API-Controllers.md) feature of the ABP Framework. It automatically converts the application services to API Controllers by convention. If you open the [Swagger UI](https://swagger.io/tools/swagger-ui/) by entering the `/swagger` URL in your application, you can see the Todo API: @@ -567,7 +567,7 @@ namespace TodoApp.Blazor.Pages } ```` -This class uses the `ITodoAppService` to perform operations for the todo items. It manipulates the `TodoItems` list after create and delete operations. This way, we don't need to refresh the whole todo list from the server. +This class uses `ITodoAppService` to perform operations for the todo items. It manipulates the `TodoItems` list after create and delete operations. This way, we don't need to refresh the whole todo list from the server. {{if UI=="Blazor"}} @@ -619,7 +619,7 @@ Open the `Index.razor` file in the `Pages` folder of the *TodoApp.Blazor* projec ### Index.razor.css -As the final touch, open the `Index.razor.css` file in the `Pages` folder of the *TodoApp.Blazor* project and replace with the following content: +As the final touch, open the `Index.razor.css` file in the `Pages` folder of the *TodoApp.Blazor* project and replace it with the following content: ````css #TodoList{ @@ -660,7 +660,7 @@ In the `Index.razor.cs` file, we've injected (with the `[Inject]` attribute) and The magic is done by the ABP Framework's [Dynamic C# Client Proxy](../../API/Dynamic-CSharp-API-Clients.md) system. It uses the standard `HttpClient` and performs HTTP API requests to the remote server. It also handles all the standard tasks for us, including authorization, JSON serialization and exception handling. -However, you may ask that we haven't created any API Controller, so how server handles these requests? This question brings us the [Auto API Controller](../../API/Auto-API-Controllers.md) feature of the ABP Framework. It automatically converts the application services to API Controllers by convention. +However, you may ask that we haven't created any API Controller, so how does the server handle these requests? This question brings us to the [Auto API Controller](../../API/Auto-API-Controllers.md) feature of the ABP Framework. It automatically converts the application services to API Controllers by convention. If you run the `TodoApp.HttpApi.Host` application, you can see the Todo API: @@ -676,7 +676,7 @@ ABP provides a handy feature to automatically create client-side services to eas You first need to run the `TodoApp.HttpApi.Host` project since the proxy generator reads API definitions from the server application. -> **Warning**: There is an issue with IIS Express: it doesn't allow to connect to the application from another process. If you are using Visual Studio, select the `TodoApp.HttpApi.Host` instead of IIS Express in the run button drop-down list, as shown in the figure below: +> **Warning**: There is an issue with IIS Express: it doesn't allow connecting to the application from another process. If you are using Visual Studio, select the `TodoApp.HttpApi.Host` instead of IIS Express in the run button drop-down list, as shown in the figure below: ![run-without-iisexpress](run-without-iisexpress.png) @@ -686,7 +686,7 @@ Once you run the `TodoApp.HttpApi.Host` project, open a command-line terminal in abp generate-proxy -t ng ```` -If everything goes well, it should generate an output like shown below: +If everything goes well, it should generate an output as shown below: ````bash CREATE src/app/proxy/generate-proxy.json (170978 bytes) @@ -696,7 +696,7 @@ CREATE src/app/proxy/models.ts (66 bytes) CREATE src/app/proxy/index.ts (58 bytes) ```` -We can then use the `todoService` to use the server-side HTTP APIs, as we'll do in the next section. +We can then use `todoService` to use the server-side HTTP APIs, as we'll do in the next section. ### home.component.ts @@ -745,7 +745,7 @@ export class HomeComponent implements OnInit { ```` -We've used the `todoService` to get the list of todo items and assigned the returning value to the `todoItems` array. We've also added `create` and `delete` methods. These methods will be used in the view side. +We've used `todoService` to get the list of todo items and assigned the returning value to the `todoItems` array. We've also added `create` and `delete` methods. These methods will be used on the view side. ### home.component.html @@ -819,7 +819,7 @@ Now, you can run the application again to see the result. ## Conclusion -In this tutorial, we've built a very simple application to warm up to the ABP Framework. If you are looking to build a serious application, please check the [web application development tutorial](../Part-1.md) which covers all the aspects of a real-life web application development. +In this tutorial, we've built a very simple application to warm up for the ABP Framework. If you are looking to build a serious application, please check the [web application development tutorial](../Part-1.md) which covers all the aspects of real-life web application development. ## Source Code diff --git a/docs/en/UI/Angular/Theming.md b/docs/en/UI/Angular/Theming.md index 21e28953f8..3b7e3d1afd 100644 --- a/docs/en/UI/Angular/Theming.md +++ b/docs/en/UI/Angular/Theming.md @@ -204,25 +204,78 @@ constructor(private sessionState: SessionStateService) { } ```` + + ##### User Menu -User menu is a component that can be replaceable. See an example to learn how can you replace it: +`UserMenuService` is used to get the user menu's items and render on the dropdown. You can add/update/remove an item by using the service. + +You can either pass a `component`, a piece of `html` or a `textTemplate` to render an item. +All of the options are shown below. You can choose either of them. + +**Example: Adding/updating/removing an user menu item** ````ts -import { eThemeBasicComponents } from '@abp/ng.theme.basic'; -import { NavItemsService } from '@abp/ng.theme.shared'; +import { eUserMenuItems } from '@abp/ng.theme.basic'; +import { UserMenuService } from '@abp/ng.theme.shared'; import { Component } from '@angular/core'; +import { Router } from '@angular/router'; + +// make sure that you import this component in a NgModule +@Component({ + selector: 'abp-current-user-test', + template: ` + + + {{ data.textTemplate.text | abpLocalization }} + + `, +}) +export class UserMenuItemComponent { + // you can inject the data through `INJECTOR_PIPE_DATA_TOKEN` token + constructor(@Inject(INJECTOR_PIPE_DATA_TOKEN) public data: UserMenu) {} +} @Component({/* component metadata */}) export class AppComponent { - constructor(private navItems: NavItemsService) { - this.navItems.patchItem(eThemeBasicComponents.CurrentUser, { component: MyUserMenuComponent }); + constructor(private userMenu: UserMenuService, private router: Router) { + this.userMenu.addItems([ + { + id: 'UserMenu.MyAccount', + order: 1, + + // HTML example + html: `My account`, + + // text template example + textTemplate: { + text: 'AbpAccount::MyAccount', + icon: 'fa fa-cog', + }, + // component example + component: UserMenuItemComponent, + + action: () => { + this.router.navigateByUrl('/account/manage'); + }, + }, + ]); + + this.userMenu.patchItem(eUserMenuItems.MyAccount, { + html: `My profile`, + }); + + this.userMenu.removeItem(eUserMenuItems.Logout); } } ```` -[`ConfigStateService`](Config-State-Service.md) service can be used to obtain the `application-configuration` API response (e.g. getting current user or tenant). +In the example above, added a new user menu item labeled "Reports", updated the "My account" item HTML content, and removed the "Logout" item. + +See the result: + +![image](https://user-images.githubusercontent.com/34455572/148387770-5b5e25fb-f855-447c-8ead-c04c69b6d6f7.png) #### Page Alerts -`PageAlertService` service is used to get the current page alerts to render on the layout. See the [Page Alerts](Page-Alerts.md) document to learn more. \ No newline at end of file +`PageAlertService` service is used to get the current page alerts to render on the layout. See the [Page Alerts](Page-Alerts.md) document to learn more. diff --git a/docs/en/Virtual-File-System.md b/docs/en/Virtual-File-System.md index 4fa29f0738..4a13bf5740 100644 --- a/docs/en/Virtual-File-System.md +++ b/docs/en/Virtual-File-System.md @@ -119,7 +119,7 @@ The Virtual File System is well integrated to ASP.NET Core: ### Static Virtual File Folders -By default, ASP.NET Core only allows the `wwwroot` folder to contain the static files consumed by the clients. When you use the birtual File System, the following folders also can contain static files: +By default, ASP.NET Core only allows the `wwwroot` folder to contain the static files consumed by the clients. When you use the virtual File System, the following folders also can contain static files: * Pages * Views diff --git a/docs/zh-Hans/Background-Workers.md b/docs/zh-Hans/Background-Workers.md index a7563349ab..3f0e7fb369 100644 --- a/docs/zh-Hans/Background-Workers.md +++ b/docs/zh-Hans/Background-Workers.md @@ -2,7 +2,6 @@ ## 介绍 -背景工人在应用简单独立的线程在后台运行.一般来说,他们定期运行,以执行一些任务.例子; 后台工作者在应用程序后台运行的简单的独立线程,一般来说它们定期运行执行一些任务.例如; * 后台工作者可以定期**删除过时的日志**. diff --git a/docs/zh-Hans/CLI.md b/docs/zh-Hans/CLI.md index b310f96b2c..b2f3825c64 100644 --- a/docs/zh-Hans/CLI.md +++ b/docs/zh-Hans/CLI.md @@ -29,6 +29,7 @@ dotnet tool update -g Volo.Abp.Cli * **`help`**: 展示ABP CLI的用法帮助信息. * **`new`**:生成基于ABP的[启动模板](Startup-Templates/Index.md). * **`update`**:自动更新的ABP解决方案ABP相关的NuGet和NPM包. +* **`clean`**: 删除当前目录下所有的 `BIN` 和 `OBJ` 子目录. * **`add-package`**: 添加ABP包到项目. * **`add-module`**: 添加[应用模块](https://docs.abp.io/en/abp/latest/Modules/Index)到解决方案. * **`generate-proxy`**: 生成客户端代理以使用HTTP API端点. @@ -129,6 +130,16 @@ abp update [options] * `--check-all`: 分别检查每个包的新版本. 默认是 `false`. * `--version` or `-v`: 指定用于升级的版本. 如果没有指定,则使用最新版本. +### clean + +删除当前目录下所有的 `BIN` 和 `OBJ` 子目录. + +用法: + +````bash +abp clean +```` + ### add-package 通过以下方式将ABP包添加到项目中 diff --git a/docs/zh-Hans/Entities.md b/docs/zh-Hans/Entities.md index 85e1132d2b..e54258d61f 100644 --- a/docs/zh-Hans/Entities.md +++ b/docs/zh-Hans/Entities.md @@ -26,7 +26,7 @@ public class Book : Entity 如果你的实体Id类型为 `Guid`,有一些好的实践可以实现: * 创建一个构造函数,获取ID作为参数传递给基类. - * 如果没有为GUID Id斌值,**ABP框架会在保存时设置它**,但是在将实体保存到数据库之前最好在实体上有一个有效的Id. + * 如果没有为GUID Id赋值,**ABP框架会在保存时设置它**,但是在将实体保存到数据库之前最好在实体上有一个有效的Id. * 如果使用带参数的构造函数创建实体,那么还要创建一个 `private` 或 `protected` 构造函数. 当数据库提供程序从数据库读取你的实体时(反序列化时)将使用它. * 不要使用 `Guid.NewGuid()` 来设置Id! 在创建实体的代码中**使用[`IGuidGenerator`服务](Guid-Generation.md)**传递Id参数. `IGuidGenerator`经过优化可以产生连续的GUID.这对于关系数据库中的聚集索引非常重要. diff --git a/docs/zh-Hans/Getting-Started-AspNetCore-Application.md b/docs/zh-Hans/Getting-Started-AspNetCore-Application.md index 1e676d0c67..f55316739c 100644 --- a/docs/zh-Hans/Getting-Started-AspNetCore-Application.md +++ b/docs/zh-Hans/Getting-Started-AspNetCore-Application.md @@ -1,12 +1,12 @@ -# 在AspNet Core MVC Web Application中使用ABP +# 在ASP.NET Core MVC Web 应用程序中使用ABP -本教程将介绍如何开始以最少的依赖关系开始使用ABP开发. +本教程将介绍如何开始以最少的依赖关系开始使用ABP开发. -通常情况下你需要下载一个 **[启动模板](Getting-Started-AspNetCore-MVC-Template.md)** +通常情况下你希望从 **[启动模板](Getting-Started-AspNetCore-MVC-Template.md)** 开始. ## 创建一个新项目 -1. 使用Visual Studio 2019 (16.4.0+)创建一个新的AspNet Core Web Application: +1. 使用Visual Studio 2022 (17.0.0+)创建一个新的ASP.NET Core Web应用程序: ![](images/create-new-aspnet-core-application-v2.png) @@ -21,7 +21,7 @@ ## 安装 Volo.Abp.AspNetCore.Mvc 包 -Volo.Abp.AspNetCore.Mvc是ABP集成AspNet Core MVC的包,请安装它到你项目中: +Volo.Abp.AspNetCore.Mvc是ABP集成ASP.NET Core MVC的包,请安装它到你项目中: ```` Install-Package Volo.Abp.AspNetCore.Mvc @@ -29,7 +29,7 @@ Install-Package Volo.Abp.AspNetCore.Mvc ## 创建ABP模块 -ABP是一个模块化框架,它需要一个**启动 (根) 模块**继承自``AbpModule``: +ABP是一个模块化框架,它需要一个**启动(根)模块**继承自 `AbpModule`: ````C# using Microsoft.AspNetCore.Builder; @@ -43,21 +43,20 @@ namespace BasicAspNetCoreApplication [DependsOn(typeof(AbpAspNetCoreMvcModule))] public class AppModule : AbpModule { - public override void OnApplicationInitialization( - ApplicationInitializationContext context) + public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); var env = context.GetEnvironment(); + // Configure the HTTP request pipeline. if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else { app.UseExceptionHandler("/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); } + app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseConfiguredEndpoints(); @@ -68,49 +67,44 @@ namespace BasicAspNetCoreApplication ``AppModule`` 是应用程序启动模块的好名称. -ABP的包定义了这个模块类,模块可以依赖其它模块.在上面的代码中 ``AppModule`` 依赖于 ``AbpAspNetCoreMvcModule`` (模块存在于[Volo.Abp.AspNetCore.Mvc](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc)包中). 安装新的ABP的包后添加``DependsOn``是很常见的做法. +ABP的包定义了模块类,模块可以依赖其它模块.在上面的代码中 ``AppModule`` 依赖于 ``AbpAspNetCoreMvcModule`` (由[Volo.Abp.AspNetCore.Mvc](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc)包定义). 安装新的ABP的包后添加``DependsOn``特性是很常见的做法. 我们在此模块类中配置ASP.NET Core管道,而不是Startup类中. ### 启动类 -接下来修改启动类集成ABP模块系统: +接下来修改启动类集成到ABP模块系统: ````C# -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; +using BasicAspNetCoreApplication; -namespace BasicAspNetCoreApplication -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - services.AddApplication(); - } +var builder = WebApplication.CreateBuilder(args); - public void Configure(IApplicationBuilder app) - { - app.InitializeApplication(); - } - } -} +builder.Services.ReplaceConfiguration(builder.Configuration); + +builder.Services.AddApplication(); + +var app = builder.Build(); + +app.InitializeApplication(); + +app.Run(); ```` - -``services.AddApplication()``添加了所有``AppModule``模块中定义的全部服务. -``Configure``方法中的``app.InitializeApplication()``完成初始化并启动应用程序. +``services.AddApplication()``添加了从``AppModule``中启动的所有模块中定义的所有服务. + +``app.InitializeApplication()`` 初始化并启动应用程序. ## 运行应用程序! -启动该应用,它将按预期运行. +启动该应用,它将按预期运行. ## 使用 Autofac 依赖注入框架 -虽然AspNet Core的依赖注入(DI)系统适用于基本要求,但[Autofac](https://autofac.org/)提供了属性注入和方法拦截等高级功能,这些功能是ABP执行高级应用程序框架功能所必需的. +虽然ASP.NET Core的依赖注入(DI)系统适用于基本要求,但[Autofac](https://autofac.org/)提供了属性注入和方法拦截等高级功能,这些功能是ABP执行高级应用程序框架功能所必需的. -用Autofac取代AspNet Core的DI系统并集成到ABP非常简单. +用Autofac取代ASP.NET Core的DI系统并集成到ABP非常简单. 1. 安装 [Volo.Abp.Autofac](https://www.nuget.org/packages/Volo.Abp.Autofac) 包 @@ -132,27 +126,21 @@ public class AppModule : AbpModule 3. 修改``Program.cs``以使用Autofac: ````C# -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; +using BasicAspNetCoreApplication; -namespace BasicAspNetCoreApplication -{ - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } +var builder = WebApplication.CreateBuilder(args); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }) - .UseAutofac(); // 添加这一行 - } -} +builder.Host.UseAutofac(); //Add this line + +builder.Services.ReplaceConfiguration(builder.Configuration); + +builder.Services.AddApplication(); + +var app = builder.Build(); + +app.InitializeApplication(); + +app.Run(); ```` ## 源码 diff --git a/docs/zh-Hans/Getting-Started-Create-Solution.md b/docs/zh-Hans/Getting-Started-Create-Solution.md new file mode 100644 index 0000000000..1ef4879a08 --- /dev/null +++ b/docs/zh-Hans/Getting-Started-Create-Solution.md @@ -0,0 +1,73 @@ +# 入门教程 + +````json +//[doc-params] +{ + "UI": ["MVC", "Blazor", "BlazorServer", "NG"], + "DB": ["EF", "Mongo"], + "Tiered": ["Yes", "No"] +} +```` + +> 本文档假设你更喜欢使用 **{{ UI_Value }}** 作为 UI 框架, 使用 **{{ DB_Value }}** 作为数据库提供程序. 对于其他选项, 请更改本文档顶部的首选项. + +## 创建新项目 + +我们将使用 ABP CLI 创建一个新的 ABP 项目. + +> 或者, 你可以使用[ABP Framework 网站](https://abp.io/get-started)页面上的选项轻松的 **创建并下载** 项目. + +使用 ABP CLI 的 `new` 命令创建一个新项目: + +````shell +abp new Acme.BookStore{{if UI == "NG"}} -u angular{{else if UI == "Blazor"}} -u blazor{{else if UI == "BlazorServer"}} -u blazor-server{{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes"}}{{if UI == "MVC" || UI == "BlazorServer"}} --tiered{{else}} --separate-identity-server{{end}}{{end}} +```` + +*你可以使用不同级别的命名空间, 例如: BookStore、Acme.BookStore或 Acme.Retail.BookStore.* + +{{ if Tiered == "Yes" }} + +{{ if UI == "MVC" || UI == "BlazorServer" }} + +* `--tified` 参数用于创建认证服务器、 UI 和 API 实际分隔的 N-层解决方案. + +{{ else }} + +* `--separate-identity-server` 参数用于将Identity Server应用程序与API主机应用程序分隔开. 如果未指定, 则服务器上将只有一个端点. + +{{ end }} + +{{ end }} + +> [ABP CLI 文档](./CLI.md) 涵盖了所有可用的命令和选项. + +## 移动端开发 + +如果你想要在你的解决方案中包含 [React Native](https://reactnative.dev/) 项目, 将 `-m react-native` (or `--mobile react-native`) 参数添加到项目创建命令. 这是一个基础的 React Native 启动模板, 用于开发基于你的 ABP 后端的移动应用程序. + +请参阅 [React Native 入门](Getting-Started-React-Native.md) 文档, 了解如何配置和运行 React Native 应用程序. + +### 解决方案结构 + +该解决方案具有分层结构 (基于 [域驱动设计](Domain-Driven-Design.md)), 并包含单元 & 集成测试项目. 请参阅 [应用程序模板文档](Startup-Templates/Application.md) 以详细了解解决方案结构. + +{{ if DB == "Mongo" }} + +#### MongoDB 事务 + +[启动模板](Startup-templates/Index.md) 默认在`.MongoDB`项目中**禁用**事务. 如果你的MongoDB服务器支持事务, 你可以在*YourProjectMongoDbModule*类中的`ConfigureServices`方法开启它: + + ```csharp +Configure(options => +{ + options.TransactionBehavior = UnitOfWorkTransactionBehavior.Auto; +}); + ``` + +> 或者你可以删除该代码, 因为 `Auto` 已经是默认行为. + +{{ end }} + +## 下一步 + +* [运行解决方案](Getting-Started-Running-Solution.md) \ No newline at end of file diff --git a/docs/zh-Hans/Getting-Started-Running-Solution.md b/docs/zh-Hans/Getting-Started-Running-Solution.md new file mode 100644 index 0000000000..91ee846bc2 --- /dev/null +++ b/docs/zh-Hans/Getting-Started-Running-Solution.md @@ -0,0 +1,199 @@ +# 入门教程 + +````json +//[doc-params] +{ + "UI": ["MVC", "Blazor", "BlazorServer", "NG"], + "DB": ["EF", "Mongo"], + "Tiered": ["Yes", "No"] +} +```` + +> 本文档假设你更喜欢使用 **{{ UI_Value }}** 作为 UI 框架, 使用 **{{ DB_Value }}** 作为数据库提供程序. 对于其他选项, 请更改本文档顶部的首选项. + +## 创建数据库 + +### 连接字符串 + +检查在 {{if Tiered == "Yes"}}`.IdentityServer` 和`.HttpApi.Host` 项目{{else}}{{if UI=="MVC"}}`.Web` 项目{{else if UI=="BlazorServer"}}`.Blazor` 项目{{else}}`.HttpApi.Host` 项目{{end}}{{end}} 中 `appsettings.json` 文件里的**连接字符串**. + +{{ if DB == "EF" }} + +````json +"ConnectionStrings": { + "Default": "Server=(LocalDb)\MSSQLLocalDB;Database=BookStore;Trusted_Connection=True" +} +```` + +> **关于连接字符串和数据库管理系统** +> +> 解决方案配置为默认使用 **Entity Framework Core** 与 **MS SQL Server**. 但是, 如果在执行ABP CLI 的`new`命令时使用了`-dbms`参数来选择其他DBMS (如`-dbms MySQL`), 那么连接字符串可能不同. +> +> EF Core 支持 [多种](https://docs.microsoft.com/en-us/ef/core/providers/) 据库提供程序, 因此你可以使用任何受支持的DBMS. 你可以需要时候参阅[Entity Framework 集成文档](Entity-Framework-Core.md) 来学习如何[切换到另一个DBMS](Entity-Framework-Core-Other-DBMS.md). + +### 数据库迁移 + +该解决方案使用[Entity Framework Core Code First 迁移](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli). 带有 `.DbMigrator` 的控制台程序用于 **应用迁移** 和 **初始化种子数据**. 它在**开发**和**生产**环境中都很有用. + +> `.dbMigator` 项目有自己的 `appsettings.json`. 因此, 如果你更改了之前的连接字符串, 那么也应该更改这个连接字符串. + +### 初次迁移 + +`.dbMigator` 应用程序在首次运行时自动**创建初始迁移**. + +**如果你使用的是 Visual Studio, 你可以跳到 *运行 dbMigrator* 部分.** 但是, 其他 IDE (例如 Rider) 在首次运行时可能会遇到问题, 因为它会添加初始迁移并编译项目. 在这种情况下, 请在 `.dbMigration` 项目的文件夹中打开命令行终端, 然后运行以下命令: + +````bash +dotnet run +```` + +下次, 你可以像往常一样在 IDE 中运行它. + +### 运行迁移 + +右键单击 `.dbMigration` 项目, 然后选择 **设置为启动项目** + +![set-as-startup-project](images/set-as-startup-project.png) + + 按F5(或Ctrl + F5) 运行应用程序. 它将具有如下所示的输出: + + ![db-migrator-output](images/db-migrator-output.png) + +> 初始的[种子数据](Data-Seeding.md)在数据库中创建了 `admin` 用户(密码为`1q2w3E*`) 用于登录应用程序. 所以, 对于新数据库至少使用 `.DbMigrator` 一次. + +{{ else if DB == "Mongo" }} + +````json +"ConnectionStrings": { + "Default": "mongodb://localhost:27017/BookStore" +} +```` + +该解决方案配置为在本地计算机中使用 **MongoDB**, 因此你需要启动并运行 MongoDB 服务器实例, 或者将连接字符串更改为另一台 MongoDB 服务器. + +### 种子初始数据 + +该解决方案带有 `.DbMigrator` 的控制台程序用于 **初始化种子数据**. 它在**开发**和**生产**环境中都很有用. + +> `.dbMigator` 项目有自己的 `appsettings.json`. 因此, 如果你更改了之前的连接字符串, 那么也应该更改这个连接字符串. + +右键单击 `.dbMigration` 项目, 然后选择 **设置为启动项目** + +![set-as-startup-project](images/set-as-startup-project.png) + + 按F5(或Ctrl + F5) 运行应用程序. 它将具有如下所示的输出: + + ![db-migrator-output](images/db-migrator-output.png) + +> 初始的[种子数据](Data-Seeding.md)在数据库中创建了 `admin` 用户(密码为`1q2w3E*`) 用于登录应用程序. 所以, 对于新数据库至少使用 `.DbMigrator` 一次. + +{{ end }} + +## 运行应用程序 + +{{ if UI == "MVC" || UI == "BlazorServer" }} + +{{ if Tiered == "Yes" }} + +> 分层解决方案使用 **Redis** 作为分布式缓存. 确保它已安装并在本地计算机上运行. 如果你使用的是远程 Redis 服务器, 请修改项目的 ` appsettings.json` 文件中的配置. + +1. 确保 `.IdentityServer` 项目是启动项目. 运行此应用程序, 它将在浏览器中打开 **登录** 页面. + +> 在 Visual Studio 中使用 Ctrl+F5(而不是F5) 在不进行调试的情况下运行应用程序. 如果你没有调试目的, 这会更快. + +你可以登录, 但不能在这里进入主应用程序. 这 **只是身份验证服务器**. + +2. 确保 `.httpapi.Host` 项目是启动项目, 然后运行应用将在浏览器中打开 **Swagger UI**. + +![swagger-ui](images/swagger-ui.png) + +这是 Web 应用程序使用的 HTTP API. + +3. 最后, 确保 {{if UI=="MVC"}}`.Web`{{else}}`.Blazor`{{end}} 项目是启动项目,然后运行应用程序,它将在浏览器中打开 **欢迎** 页面 + +![mvc-tiered-app-home](images/bookstore-home.png) + +单击 **login** 按钮, 它将重定向到 *身份验证服务器* 以登录到应用程序: + +![bookstore-login](images/bookstore-login.png) + +{{ else # Tiered != "Yes" }} + +确保 {{if UI=="MVC"}}`.Web`{{else}}`.Blazor`{{end}} 项目是启动项目. 运行应用程序将会在浏览器中打开 **login** 页面: + +> 在 Visual Studio 中使用 Ctrl+F5(而不是F5) 在不进行调试的情况下运行应用程序. 如果你没有调试目的, 这会更快. + +![bookstore-login](images/bookstore-login.png) + +{{ end # Tiered }} + +{{ else # UI != MVC || BlazorServer }} + +### 运行 HTTP API 主机(服务器端) + +{{ if Tiered == "Yes" }} + +> 分层解决方案使用 Redis 作为分布式缓存. 确保它已安装并在本地计算机上运行. 如果你使用的是远程 Redis 服务器, 请修改项目的 `appsettings.json` 文件中的配置. + +确保 `.IdentityServer` 项目是启动项目. 运行此应用程序, 它将在浏览器中打开 **登录** 页面. + +> 在 Visual Studio 中使用 Ctrl+F5(而不是F5) 在不进行调试的情况下运行应用程序. 如果你没有调试目的, 这会更快. + +你可以登录, 但不能在这里进入主应用程序. 这 **只是身份验证服务器**. + +确保 `.HttpApi.Host` 项目是启动项目, 然后运行应用程序将打开 Swagger UI 的: + +{{ else # Tiered == "No" }} + +确保 `.HttpApi.Host` 项目是启动项目, 然后运行应用程序将打开 Swagger UI 的: + +> 在 Visual Studio 中使用 Ctrl+F5(而不是F5) 在不进行调试的情况下运行应用程序. 如果你没有调试目的, 这会更快. + +{{ end # Tiered }} + +![swagger-ui](images/swagger-ui.png) + +你可以在这里查看应用程序的API并测试它们. 获取Swagger UI的[更多信息](https://swagger.io/tools/swagger-ui/). + +{{ end # UI }} + +{{ if UI == "Blazor" }} + +### 运行 Blazor 应用程序 (客户端) + +确保 `.Blazor` 项目是启动项目并运行应用程序. + +> 在 Visual Studio 中使用 Ctrl+F5(而不是F5) 在不进行调试的情况下运行应用程序. 如果你没有调试目的, 这会更快. + +应用程序启动后, 单击页头上的 **Login** 链接, 你将重定向到身份验证服务器以输入用户名和密码: + +![bookstore-login](images/bookstore-login.png) + +{{ else if UI == "NG" }} + +### 运行 Angular 应用程序 (客户端) + +转到 `Angular` 文件夹, 打开命令行终端, 键入 `yarn` 命令(我们建议使用 [yarn](https://yarnpkg.com/) 软件包管理器, 而 `npm install` 也可以使用) + +```bash +yarn +``` + +当所有node模块加载完毕后, 执行 `yarn start` (或 `npm start`) 命令: + +```bash +yarn start +``` + +初次构建可能需要更长的时间. 完成后, 它会在默认浏览器中使用 [localhost:4200](http://localhost:4200/) 地址打开 Angular UI. + +![bookstore-login](images/bookstore-login.png) + +{{ end }} + +输入用户名 **admin** 和 密码 **1q2w3E*** 登录应用程序. 应用程序已启动并正在运行. 你可以根据此启动模板开始开发应用程序. + +## 另请参见 + +* [Web 应用程序开发教程](Tutorials/Part-1.md) +* [应用程序启动模板](Startup-Templates/Application.md) diff --git a/docs/zh-Hans/Getting-Started-Setup-Environment.md b/docs/zh-Hans/Getting-Started-Setup-Environment.md new file mode 100644 index 0000000000..c0c2d5d924 --- /dev/null +++ b/docs/zh-Hans/Getting-Started-Setup-Environment.md @@ -0,0 +1,53 @@ +# 入门教程 + +````json +//[doc-params] +{ + "UI": ["MVC", "Blazor", "BlazorServer", "NG"], + "DB": ["EF", "Mongo"], + "Tiered": ["Yes", "No"] +} +```` + +> 本文档假设你更喜欢使用 **{{ UI_Value }}** 作为 UI 框架, 使用 **{{ DB_Value }}** 作为数据库提供程序. 对于其他选项, 请更改本文档顶部的首选项. + +## 设置你的开发环境 + +第一件事! 在创建项目之前, 让我们先设置你的开发环境. + +### 先决条件 + +开发计算机上应安装以下工具: + +* 一个集成开发环境 (比如: [Visual Studio](https://visualstudio.microsoft.com/vs/)) 它需要支持 [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) 的开发. +{{ if UI != "Blazor" }} +* [Node v12 或 v14](https://nodejs.org/) +* [Yarn v1.20+ (不是v2)](https://classic.yarnpkg.com/en/docs/install) [1](#f-yarn) 或 npm v6+ (已跟随Node一起安装) +{{ end }} +{{ if Tiered == "Yes" }} +* [Redis](https://redis.io/) (启动解决方案使用 Redis 作为 [分布式缓存](Caching.md)). +{{ end }} + +{{ if UI != "Blazor" }} + +1 _Yarn v2 工作方式不同, 不被支持._ [↩](#a-yarn) + +{{ end }} + +### 安装 ABP CLI + +[ABP CLI](./CLI.md) 是一个命令行界面, 用于自动执行基于 ABP 的解决方案的一些常见任务. 首先, 你需要使用以下命令安装 ABP CLI: + +````shell +dotnet tool install -g Volo.Abp.Cli +```` + +如果已安装, 则可以使用以下命令对其进行更新: + +````shell +dotnet tool update -g Volo.Abp.Cli +```` + +## 下一步 + +* [创建新的解决方案](Getting-Started-Create-Solution.md) \ No newline at end of file diff --git a/docs/zh-Hans/Getting-Started.md b/docs/zh-Hans/Getting-Started.md index c6b3bd70df..1cf1430a35 100644 --- a/docs/zh-Hans/Getting-Started.md +++ b/docs/zh-Hans/Getting-Started.md @@ -1,374 +1,20 @@ -## 入门 +# 入门教程 ````json //[doc-params] { - "UI": ["MVC","NG"], + "UI": ["MVC", "Blazor", "BlazorServer", "NG"], "DB": ["EF", "Mongo"], "Tiered": ["Yes", "No"] } ```` -本教程介绍了如何创建一个新的{{if UI == "MVC"}} ASP.NET Core MVC web {{else if UI == "NG"}} Angular {{end}}. 配置并运行它. +> 本文档假设你更喜欢使用 **{{ UI_Value }}** 作为 UI 框架, 使用 **{{ DB_Value }}** 作为数据库提供程序. 对于其他选项, 请更改本文档顶部的首选项. -## 设置你的开发环境 +## 内容 -创建第一个项目之前,需要正确的设置你的开发环境. +本教程介绍如何使用 ABP 框架 **创建和运行** 新的 Web 应用程序. 请按照以下步骤操作; -### 预先要求 - -你需要安装以下工具: - -* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).[1](#f-editor) -* [.NET Core 3.1+](https://www.microsoft.com/net/download/dotnet-core/) - -* [Node v12 或 v14](https://nodejs.org/en/) -* [Yarn v1.19+](https://classic.yarnpkg.com/) -* [Yarn v1.20+ (not v2)](https://classic.yarnpkg.com/en/docs/install) [2](#f-yarn) 或 npm v6+ (与Node一起安装) -{{ if Tiered == "Yes" }} - -* [Redis](https://redis.io/): 入门解决方案将Redis用作[分布式缓存](Caching.md). 因此你需要安装并运行Redis. - -{{ end }} - -1 _只要支持.NET Core和ASP.NET Core,就可以使用其他编辑器代替Visual Studio._ [↩](#a-editor) - -2 _Yarn v2 的工作方式不同,不受支持._ [↩](#a-yarn) - -### 安装ABP CLI - -[ABP CLI](./CLI.md)是一个命令行页面,用于自动执行一些基于ABP的应用程序的常见任务. - -> ABP CLI是ABP框架一个免费开源的工具. - -你需要使用以下命令安排ABP CLI: - -````shell -dotnet tool install -g Volo.Abp.Cli -```` - -如果你已经安装,你可以使用以下命令更新到最新版本: - -````shell -dotnet tool update -g Volo.Abp.Cli -```` - -## 创建新项目 - -> 本文假设你使用 **{{ UI_Value }}** 做为UI框架 **{{ DB_Value }}** 做为数据库提供程序,对于其它选项,你可以更改文档顶部的首选项. - -### 使用ABP CLI创建一个新项目 - -使用ABP CLI的 `new` 命令创建新项目: - -````shell -abp new Acme.BookStore{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}} --mobile react-native -```` - -* 此命令还会在解决方案文件夹内创建一个React Native移动应用程序. 如果你不想要它,可以安全地删除它或从`abp new`命令中删除`--mobile react-native`选项, 以使其完全不包含在解决方案中. - -{{ if UI == "NG" }} - -* `-u` 指定UI框架, 本例中是 `angular`. - -{{ if Tiered == "Yes" }} - -* `--separate-identity-server` 参数用于将Identity服务器应用程序与API主机应用程序分隔开. 如果未指定,则服务器上将只有一个端点. - -{{ end }} - -{{ end }} - -{{ if DB == "Mongo" }} - -* `-d` 指定数据库提供程序, 本例中是 `mongodb`. - -{{ end }} - -{{ if Tiered == "Yes" && UI != "NG" }} - -* `--tiered` 参数用于创建n层解决方案,其中身份验证服务器层,UI层和API层在物理上是分离的. - -{{ end }} - -> 你可以使用不同级别的命令空间; 例如. BookStore, Acme.BookStore or Acme.Retail.BookStore. - -#### ABP CLI 命令 & 选项 - -[ABP CLI文档](./CLI.md)涵盖了ABP CLI的所有可用命令和选项. 本文档使用[应用程序启动模板](Startup-Templates/Application.md)创建新的Web应用程序. 有关其他模板,请参见[ABP启动模板](Startup-Templates/Index.md)文档. - -> 或者,您可以从[ABP Framework网站](https://abp.io/get-started)中选择"直接下载"选项卡创建新的解决方案. - -## 解决方案结构 - -{{ if UI == "MVC" }} - -创建项目后你会有以下解决方案目录和文件: - -![](images/solution-files-mvc.png) - -在Visual Studio中打开 `.sln` 文件时,将看到以下解决方案结构: - -{{if DB == "Mongo"}} - -![vs-default-app-solution-structure](images/vs-app-solution-structure-mongodb.png) - -{{else}} - -![vs-default-app-solution-structure](images/vs-app-solution-structure{{if Tiered == "Yes"}}-tiered{{end}}.png) - -{{end}} - -{{ else if UI == "NG" }} - -在创建的解决方案中有三个文件夹: - -![](images/solution-files-non-mvc.png) - -* `angular` 文件夹包含Angular UI应用程序. -* `aspnet-core` 文件夹包含后端应用程序. -* `react-native` 文件夹包含React Native UI 应用程序. - -打开 `aspnet-core` 文件夹下的 `.sln`(`Visual Studio`解决方案)文件: - -![vs-angular-app-backend-solution-structure](images/vs-spa-app-backend-structure{{if DB == "Mongo"}}-mongodb{{end}}.png) - -{{ end }} - -> ###### 关于解决方案中的项目 -> -> 根据你的**UI**,**数据库**和其他选项,你的解决方案的结构可能略有不同. - -该解决方案具有分层结构(基于[Domain Driven Design](Domain-Driven-Design.md)), 并包含配置好的的单元&集成测试项目. - -{{ if DB == "EF" }} - -集成测试项目已配置为可与 **EF Core** & **SQLite 内存** database同时使用. - -{{ else if DB == "Mongo" }} - -集成测试项目已配置为每个测试创建的内存中的**MongoDB**数据库(使用的[Mongo2Go](https://github.com/Mongo2Go/Mongo2Go)库). - -{{ end }} - -> 请参阅[应用程序模板文档](Startup-Templates/Application.md)详细了解解决方案结构. - -{{ if DB == "Mongo" }} - -> [启动模板](Startup-templates/Index.md)默认在 `.MongoDB` 项目中**禁用**了工作单元事务. 如果你的MongoDB服务器支持事务,你可以手动启用工作单元的事务: - - ```csharp - Configure(options => - { - options.TransactionBehavior = UnitOfWorkTransactionBehavior.Enabled; - }); - ``` - -{{ end }} - -## 创建数据库 - -### 连接字符串 - -检查 {{if UI == "MVC"}}{{if Tiered == "Yes"}}`.IdentityServer` 和 `.HttpApi.Host` 项目{{else}}`.Web` 项目{{end}}{{else if UI == "NG" }}`.HttpApi.Host` 项目{{end}}下 `appsettings.json` 文件中的 **链接字符串**: - -{{ if DB == "EF" }} - -````json -"ConnectionStrings": { - "Default": "Server=localhost;Database=BookStore;Trusted_Connection=True" -} -```` - -该解决方案配置为**Entity Framework Core**与**MS SQL Server**一起使用. EF Core支持[各种](https://docs.microsoft.com/en-us/ef/core/providers/)数据库提供程序,因此你可以使用任何受支持的DBMS. 请参阅[Entity Framework集成文档](https://docs.abp.io/en/abp/latest/Entity-Framework-Core)了解如何切换到另一个DBMS. - -### 数据库连接字符串 - -查看`.Web`项目下`appsettings.json`文件中的 **连接字符串**: - -````json -{ - "ConnectionStrings": { - "Default": "Server=localhost;Database=BookStore;Trusted_Connection=True" - } -} -```` - -解决方案使用 **Entity Framework Core** 和 **MS SQL Server**. EF Core支持[各种](https://docs.microsoft.com/zh-cn/ef/core/providers/)数据库提供程序,因此你可以根据实际需要使用其他DBMS. 如果需要,请更改连接字符串. - -### 应用迁移 - -该解决方案使用[Entity Framework Core Code First 迁移](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli). 你需要应用迁移来创建数据库,有两种方法迁移数据库. - -#### 使用DbMigrator应用程序应用迁移 - -该解决方案包含一个控制台应用程序(在此示例中名为`Acme.BookStore.DbMigrator`),可以创建数据库,应用迁移和初始化数据. 它对开发和生产环境都很有用. - -> `.DbMigrator`项目有自己的`appsettings.json`. 因此,如果你更改了上面的连接字符串,则还应更改此字符串. - -右键单击`.DbMigrator`项目并选择 **设置为启动项目**: - -![set-as-startup-project](images/set-as-startup-project.png) - -按F5(或Ctrl + F5)运行应用程序. 它将具有如下所示的输出: - -![set-as-startup-project](images/db-migrator-app.png) - -#### 使用EF Core Update-Database命令 - -Ef Core具有`Update-Database`命令, 可根据需要创建数据库并应用挂起的迁移. 右键单击`.Web`项目并选择**设置为启动项目**: - -{{ if UI == "MVC" }} - -右键单击{{if Tiered == "Yes"}}`.IdentityServer`{{else}}`.Web`{{end}}项目并选择**设置为启动项目**: - -{{ else if UI != "MVC" }} - -右键单击`.HttpApi.Host`项目并选择**设置为启动项目**: - -{{ end }} - -![set-as-startup-project](images/set-as-startup-project.png) - -打开**包管理器控制台(Package Manager Console)**, 选择`.EntityFrameworkCore.DbMigrations`项目作为**默认项目**并运行`Update-Database`命令: - -![package-manager-console-update-database](images/package-manager-console-update-database.png) - -这将基于配置的连接字符串创建新数据库. - -> **使用`.DbMigrator`工具是建议的方法**, 因为它能初始化初始数据能够正确运行Web应用程序. -> -> 如果你只是使用 `Update-Database` 命令,你会得到一个空数据库,所以你无法登录到应用程序因为数据库中没有初始管理用户. 不需要种子数据库时,可以在开发期间使用 `Update-Database` 命令. 但是使用 `.DbMigrator` 应用程序会更简单,你始终可以使用它来迁移模式并为数据库添加种子. - -{{ else if DB == "Mongo" }} - -````json -"ConnectionStrings": { - "Default": "mongodb://localhost:27017/BookStore" -} -```` - -该解决方案被配置为在你的本地计算机中使用 **MongoDB**,因此你需要启动并运行一个MongoDB服务器实例或者将连接字符串更改为另一个MongoDB服务器. - -### 初始化种子数据 - -该解决方案附带一个 `.DbMigrator` 控制台应用程序,该应用程序为初始数据提供了种子. 它对于开发以及生产环境都很有用. - -> `.DbMigrator` 项目有自己的 `appsettings.json`.如果你更改了其他项目的 `appsettings.json`,也应该更改这个. - -右键点击 `.DbMigrator` 并选择 **设置为启动项目**. - -![set-as-startup-project](images/set-as-startup-project.png) - - 按F5(或Ctrl+F5)启动应用程序,你会看到以下输出: - - ![db-migrator-output](images/db-migrator-output.png) - -> 数据库创建后会初始化[种子数据](Data-Seeding.md), 其中包含用于登录的 `admin` 用户. 所以你至少使用 `.DbMigrator` 一次. - -{{ end }} - -### 运行应用程序 - -{{ if UI == "MVC" }} - -{{ if Tiered == "Yes" }} - -确保 `.IdentityServer` 是启动项目,运行应用程序后会在你的浏览器打开一个 **login** 页面. - -> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间. - -你可以登录,但是不能在这里进入主应用程序,它仅是验证服务器. - -确保 `.HttpApi.Host` 是启动项目,运行应用程序后会在你的浏览器打开一个 **Swagger UI** 页面. - -![swagger-ui](images/swagger-ui.png) - -这里是Web应用程序使用的API应用程序. - -最后确保 `.Web` 是启动项目,运行应用程序后会在你的浏览器打开一个 **welcome** 页面. - -![mvc-tiered-app-home](images/bookstore-home.png) - -点击 **login** 按钮重定向到 `Identity Server` 来登录应用程序. - -![bookstore-login](images/bookstore-login.png) - -{{ else }} - -最后确保 `.Web` 是启动项目,运行应用程序后会在你的浏览器打开一个 **login** 页面. - -> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间. - -![bookstore-login](images/bookstore-login.png) - -{{ end }} - -{{ else if UI != "MVC" }} - -#### 运行HTTP API Host (服务器端) - -{{ if Tiered == "Yes" }} - -确保 `.IdentityServer` 是启动项目,运行应用程序后会在你的浏览器打开一个 **login** 页面. - -> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间. - -你可以登录,但是不能在这里进入主应用程序,它仅是验证服务器. - -{{ end }} - -确保 `.HttpApi.Host` 是启动项目,运行应用程序后会在你的浏览器打开一个 **Swagger UI** 页面. - -{{ if Tiered == "No" }} - -> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间. - -{{ end }} - -![swagger-ui](images/swagger-ui.png) - -你可以看到应用程序的API并进行测试. 更多信息,请参阅[Swagger UI](https://swagger.io/tools/swagger-ui/). - -> ##### Swagger UI 授权 -> -> 大多数的HTTP API都需要身份验证和授权. 如果你要测试授权API, 请手动进入 `/Account/Login` 页面, 输入用户名: `admin` 和密码: `1q2w3E*` 登录到应用程序. 然后你可以访问授权API. - -{{ end }} - -{{ if UI == "NG" }} -#### 运行 Angular 应用程序 (客户端) - -在 `angular` 下打开命令行终端, 输入 `yarn` 命令(我们推荐使用[yarn](https://yarnpkg.com/)包管理, `npm install` 在大多数情况下也可以工作). - -```bash -yarn -``` - -等到所有node模块加载成功, 执行 `yarn start` (或 `npm start`) 命令: - -```bash -yarn start -``` - -等待 `Angular CLI` 使用 `BrowserSync` 启动 `Webpack` dev-server. -它会负责编译你的 `TypeScript`代码, 并自动重新加载浏览器. -完成后 `Angular Live Development Server` 会监听 localhost:4200. -打开你的浏览器并导航到[localhost:4200](http://localhost:4200/). - -![bookstore-login](images/bookstore-login.png) - -{{ end }} - -输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序,应用程序已经启动并执行,你可以基于此启动模板开始开发应用程序. - -#### 移动开发 - -当你创建一个新的应用程序时. 可以添加`-m react-native`选项以在解决方案中包含 `react-native`项目. 这是一个基础的[React Native](https://reactnative.dev/)启动模板,用于开发与基于ABP的后端集成的移动应用程序. - - -请参阅"[React Native入门](Getting-Started-React-Native.md)"文档了解如何配置和运行React Native应用程序. - -## 下一步是什么? - -[Web应用程序开发教程](Tutorials/Part-1.md) +1. [设置你的开发环境](Getting-Started-Setup-Environment.md) +2. [创建新的解决方案](Getting-Started-Create-Solution.md) +3. [运行解决方案](Getting-Started-Running-Solution.md) \ No newline at end of file diff --git a/docs/zh-Hans/Modules/Cms-Kit.md b/docs/zh-Hans/Modules/Cms-Kit.md deleted file mode 100644 index e13f1e585b..0000000000 --- a/docs/zh-Hans/Modules/Cms-Kit.md +++ /dev/null @@ -1,3 +0,0 @@ -# CMS Kit Module - -TODO \ No newline at end of file diff --git a/docs/zh-Hans/Modules/Cms-Kit/Blogging.md b/docs/zh-Hans/Modules/Cms-Kit/Blogging.md new file mode 100644 index 0000000000..1d2c3ecd9e --- /dev/null +++ b/docs/zh-Hans/Modules/Cms-Kit/Blogging.md @@ -0,0 +1,3 @@ +# CMS Kit: Blogging + +TODO... \ No newline at end of file diff --git a/docs/zh-Hans/Modules/Cms-Kit/Comments.md b/docs/zh-Hans/Modules/Cms-Kit/Comments.md new file mode 100644 index 0000000000..faeb6116c9 --- /dev/null +++ b/docs/zh-Hans/Modules/Cms-Kit/Comments.md @@ -0,0 +1,3 @@ +# CMS Kit: Comments + +TODO... \ No newline at end of file diff --git a/docs/zh-Hans/Modules/Cms-Kit/Index.md b/docs/zh-Hans/Modules/Cms-Kit/Index.md new file mode 100644 index 0000000000..0751eb0ad0 --- /dev/null +++ b/docs/zh-Hans/Modules/Cms-Kit/Index.md @@ -0,0 +1,79 @@ +# 内容管理系统套件模块 + +此模块为您的应用程序提供内容管理系统 (Content Management System, CMS) 功能. 它提供 **核心构建块** 和完整工作的 **子系统**, 以创建启用 CMS 功能的您自己的网站, 或出于任何目的使用网站中的构建块. + +> **此模块目前仅适用于 MVC / Razor 页面 UI**. 虽然没有官方的 Blazor 软件包, 但它也可以在 Blazor 服务器 UI 中工作, 因为实际上 Blazor 服务器 UI 实际上是一个运行在 ASP.NET Core MVC / Razor 页面应用程序的混合型应用程序. + +目前提供以下功能: + +* 提供 [**页面**](Pages.md) 管理系统来管理具有动态 URL 的动态页面. +* 提供 [**博客**](Blogging.md) 系统来创建发表具有多种博客支持的博客文章. +* 提供 [**标签**](Tags.md) 系统来标记任何资源, 如博客文章. +* 提供 [**评论**](Comments.md) 系统来添加对任何资源的评论功能, 如博客文章或产品评价页面. +* 提供 [**反应**](Reactions.md) 系统来添加对任何资源的反应 (表情符号) 功能, 如博客文章或评论. +* 提供 [**评级**](Ratings.md) 系统来添加对任何资源的评级功能. +* 提供 [**菜单**](Menus.md) 系统来动态管理公共菜单. + +点击功能以了解和学习如何去使用它. + +所有功能均可单独使用. 如果你禁用了一个功能, 则在 [全局功能](../../Global-Features.md) 系统的帮助下, 该功能会从你的应用程序甚至数据库表中完全消失. + +## 预备要求 +- 此模块依赖于 [Blob 存储](../../Blob-Storing.md) 模块来保存媒体内容. +> 确保 `BlobStoring` 模块已安装并至少正确地配置了一个提供程序. 请查阅 [文档](../../Blob-Storing.md) 了解更多信息. + +- CMS Kit 使用 [分布式缓存](../../Caching.md) 来提高响应速度. +> 强烈建议在分布式/集群部署中为实现数据一致性使用分布式缓存, 如 [Redis](../../Redis-Cache.md). + +## 如何安装 + +可以使用 [ABP CLI](../../CLI.md) 的 `add-module` 命令为解决方案安装模块. 您可以使用以下命令在命令行中安装 CMS Kit 模块: + +```bash +abp add-module Volo.CmsKit +``` + +> 默认情况下, Cms-Kit `GlobalFeature` 被禁用. 因此初始迁移将为空. 所以, 当你使用 EF Core 安装时,你可以添加 `--skip-db-migrations` 命令来跳过迁移. 启用 Cms-Kit 全局功能后, 请添加新的迁移. + +安装过程完成后, 在您的解决方案 `Domain.Shared` 项目中打开 `GlobalFeatureConfigurator` 类, 并将以下代码写入 `Configure` 方法中, 以启用 CMS Kit 模块的全部功能. + +```csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.EnableAll(); +}); +``` + +你可能更愿意逐个启用这些功能, 而不是启用全部功能. 以下示例仅启用了 [标签](Tags.md) 和 [评论](Comments.md) 功能: + +````csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.Tags.Enable(); + cmsKit.Comments.Enable(); +}); +```` + +> 如果你使用 EF Core, 不要忘记添加一个新的迁移并更新你的数据库. + +## 软件包 + +此模块遵循 [模块开发最佳实践指南](https://docs.abp.io/zh-Hans/abp/latest/Best-Practices/Index), 由多个 NuGet 和 NPM 软件包组成. 如果你想了解软件包及其之间的关系, 请参阅指南. + +CMS Kit 软件包专为各种使用场景而设计. 如果您查阅了 [CMS Kit 软件包](https://www.nuget.org/packages?q=Volo.CmsKit) 您将看到一些有 `Admin` 和 `Public` 后缀的软件包. 该模块有两个应用程序层, 原因是他们可能被用于不同类型的应用程序. 这些应用程序层仅使用一个领域层. + + - `Volo.CmsKit.Admin.*` 软件包包括管理员 (后台) 应用程序所必须的功能. + - `Volo.CmsKit.Public.*` 软件包包括被用于用户阅读博客文章和发表评论的公共网站上的功能. + - `Volo.CmsKit.*` (不带 Admin/Public 后缀) 软件包称为统一包. 统一包分别是添加 Admin 和 Public (相关层的) 软件包的快照. 如果您有一个用于管理和公共网站的单应用程序, 您可以使用这些软件包. + +## 内部结构 + +### 表/集合 前缀&架构 + +所有表/集合使用 `Cms` 作为默认前缀. 如果需要更改表的前缀或者设置一个架构名称 (如果你的数据库提供程序支持), 请在 `CmsKitDbProperties` 类中设置静态属性. + +### 连接字符串 + +此模块使用 `CmsKit` 作为连接字符串的名称. 如果您未使用此名称定义连接字符串, 它将回退为 `Default` 连接字符串. + +有关详细信息, 请参阅 [连接字符串](https://docs.abp.io/en/abp/latest/Connection-Strings) 文档. diff --git a/docs/zh-Hans/Modules/Cms-Kit/Menus.md b/docs/zh-Hans/Modules/Cms-Kit/Menus.md new file mode 100644 index 0000000000..fb214e358c --- /dev/null +++ b/docs/zh-Hans/Modules/Cms-Kit/Menus.md @@ -0,0 +1,3 @@ +# CMS Kit: Pages + +TODO... \ No newline at end of file diff --git a/docs/zh-Hans/Modules/Cms-Kit/Pages.md b/docs/zh-Hans/Modules/Cms-Kit/Pages.md new file mode 100644 index 0000000000..fb214e358c --- /dev/null +++ b/docs/zh-Hans/Modules/Cms-Kit/Pages.md @@ -0,0 +1,3 @@ +# CMS Kit: Pages + +TODO... \ No newline at end of file diff --git a/docs/zh-Hans/Modules/Cms-Kit/Ratings.md b/docs/zh-Hans/Modules/Cms-Kit/Ratings.md new file mode 100644 index 0000000000..2c6cf44460 --- /dev/null +++ b/docs/zh-Hans/Modules/Cms-Kit/Ratings.md @@ -0,0 +1,3 @@ +# Rating System + +TODO... \ No newline at end of file diff --git a/docs/zh-Hans/Modules/Cms-Kit/Reactions.md b/docs/zh-Hans/Modules/Cms-Kit/Reactions.md new file mode 100644 index 0000000000..9b714adf26 --- /dev/null +++ b/docs/zh-Hans/Modules/Cms-Kit/Reactions.md @@ -0,0 +1,3 @@ +# Reaction System + +TODO... \ No newline at end of file diff --git a/docs/zh-Hans/Modules/Cms-Kit/Tags.md b/docs/zh-Hans/Modules/Cms-Kit/Tags.md new file mode 100644 index 0000000000..3977d06f5a --- /dev/null +++ b/docs/zh-Hans/Modules/Cms-Kit/Tags.md @@ -0,0 +1,3 @@ +# Tag Management + +TODO... \ No newline at end of file diff --git a/docs/zh-Hans/Modules/Identity.md b/docs/zh-Hans/Modules/Identity.md index 420b696a7c..bf715bc7b6 100644 --- a/docs/zh-Hans/Modules/Identity.md +++ b/docs/zh-Hans/Modules/Identity.md @@ -1,28 +1,320 @@ # 身份管理模块 -身份模块基于Microsoft Identity库用于管理[组织单元](Organization-Units.md), 角色, 用户和他们的权限. +身份模块基于 [Microsoft Identity 库](https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/identity) 用于管理角色,用户及其权限. -> 参阅 [源码](https://github.com/abpframework/abp/tree/dev/modules/identity). 文档很快会被完善. +## 如何安装 -## Identity安全日志 +当你使用 ABP 框架 [创建一个新的解决方案](https://abp.io/get-started) 时, 此模块将被预安装 (作为 NuGet/NPM 包). 你可以继续用其作为包并轻松地获取更新, 也可以将其源代码包含在解决方案中(请参阅 `get-source` [CLI](../CLI.md))以开发自定义模块. -安全日志可以记录账户的一些重要的操作或者改动, 你可以在在一些功能中保存安全日志. +### 源代码 -你可以注入和使用 `IdentitySecurityLogManager` 或 `ISecurityLogManager` 来保存安全日志. 默认它会创建一个安全日志对象并填充常用的值. 如 `CreationTime`, `ClientIpAddress`, `BrowserInfo`, `current user/tenant`等等. 当然你可以自定义这些值. +可以 [在此处](https://github.com/abpframework/abp/tree/dev/modules/identity) 访问源代码。源代码使用 [MIT](https://choosealicense.com/licenses/mit/) 许可, 所以你可以免费使用和自定义它. + +## 用户界面 + +此模块提供了 [Blazor](../UI/Blazor/Overall.md), [Angular](../UI/Angular/Quick-Start.md) 和 [MVC / Razor Pages](../UI/AspNetCore/Overall.md) 的 UI 可选. + +### 菜单项 + +此模块在 *管理* 菜单下添加了一个 *身份管理* 菜单项: + +![身份模块菜单](../images/identity-module-menu.png) + +这个菜单项和相关页面已获得授权. 这意味着当前用户必须拥有相关权限才能使其可见. `admin` 角色 (和拥有此角色的用户, 如 `admin` 用户) 已经拥有这些权限. 如果你想要使其他角色/用户也启用权限, 请打开 *角色* 或 *用户* 页面的 *权限* 对话框, 并检查如下所示的权限: + +![身分模块权限](../images/identity-module-permissions.png) + +请参阅 [授权文档](../Authorization.md) 以了解权限系统. + +### 页面 + +本节介绍此模块提供的主要页面. + +#### 用户 + +此页用于查看用户列表. 您可以创建/编辑和删除用户, 将角色分配给用户. + +![身分模块用户](../images/identity-module-users.png) + +一个用户可以有零个或多个角色. 用户从其角色继承权限. 此外, 你可以给用户直接分配权限 (通过点击 *操作* 按钮, 然后选择 *权限*) . + +#### 角色 + +角色用于按分组给用户分配权限. + +![身份模块角色](../images/identity-module-roles.png) + +角色除了角色名称之外还有2个属性: + +* `Default`: 如果某个角色被标记为 "default", 那么当新用户 (使用 [账户模块](Account.md)) 注册到应用程序时, 其将被作为默认角色分配给新用户. +* `Public`: 用户的公共角色可以被应用程序中的其他用户看到. 这个功能在身份模块中没有用到, 但被提供作为你可能希望在你自己的应用程序中使用的功能. + +## 其他功能 + +本节包括此模块提供的没有 UI 页面的其他功能. + +### 组织单元 + +组织单元 (OU) 可被用于 **按层级对用户和实体进行分组**. + +#### 组织单元实体 + +组织单元由 **OrganizationUnit** 实体表示. 它的基本属性是: + +- **TenantId**: 组织单元的租户Id. 对于宿主可以是 null. +- **ParentId**: 父级组织单元的Id. 如果这是一个根级组织单元,它可以是 null. +- **Code**: 对于租户唯一的层级字符串编码. +- **DisplayName**: 组织单元的显示名称. + +#### 组织树 + +由于组织单元可以有父级, 因此租户的所有组织单元是一个 **树** 结构. 这个树有一些规则; + +- 可以有多个根级 (`ParentId` 是 `null` 的) . +- 一个组织单元的第一级子项数量有限制 (因为下面解释的固定的组织单元编码单位长度) . + +#### 组织单元编码 + +组织单元编码是通过 `OrganizationUnitManager` 服务自动生成和维护的. 它是一个字符串, 像这样: + +"**00001.00042.00005**" + +通常用这种编码可以轻易地 (递归) 查询出数据库中组织单元的所有子项. 这个编码有一些规则 (当你使用 `OrganizationUnitManager` 时自动被应用的) : + +- 对 [租户](../Multi-Tenancy.md) 是 **唯一的**. +- 同一组织单元的全部子项都具有 **以父级组织单元编码开头的** 编码. +- 如示例中所示, 它是固定长度的且是基于树中组织单元的级别的. +- 尽管组织单元编码是唯一的, 但如果你移动了相关的组织单元, 它也可以被更改. + +请注意, 你必须根据Id引用一个组织单元, 而不是编码, 因为编码后续是可以被更改的. + +#### 组织单元管理 + +`OrganizationUnitManager` 类可以被 [注入](../Dependency-Injection.md) 并用来管理组织单元. 常见用例如下: + +- 创建, 更新和删除组织单元. +- 在组织单元树中移动一个组织单元. +- 获取关于组织单元树及其项的信息. + +### 身份安全日志 + +安全日志系统可以记录账户的一些重要的操作或者改动 (例如 *登录* 和 *更改密码*) . 如果需要, 你也可以保存安全日志. + +你可以注入和使用 `IdentitySecurityLogManager` 或 `ISecurityLogManager` 来写入安全日志. 默认它会创建一个日志对象并填充常用的值, 如 `CreationTime`, `ClientIpAddress`, `BrowserInfo`, `current user/tenant` 等等. 当然你可以覆盖这些值. ```cs await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { - Identity = "IdentityServer"; - Action = "ChangePassword"; + Identity = "IdentityServer"; + Action = "ChangePassword"; }); ``` -通过配置 `AbpSecurityLogOptions` 来提供应用程序的名称或者禁用安全日志功能. 默认是**启用**状态. +通过配置 `AbpSecurityLogOptions` 来为日志提供应用程序的名称 (如果你有多个应用程序并且想要在日志中区分应用程序) 或者禁用安全日志功能. ```cs Configure(options => { - options.ApplicationName = "AbpSecurityTest"; + options.ApplicationName = "AbpSecurityTest"; }); ``` +## 选项 + +`IdentityOptions` 是由 Microsoft [Identity 库](https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/identity) 提供的标准 [选项类](../Options.md) . 所以, 你可以在 [模块](../Module-Development-Basics.md) 类的 `ConfigureServices` 方法中设置这些选项. + +**例如: 设置所需的密码最小长度** + +````csharp +Configure(options => +{ + options.Password.RequiredLength = 5; +}); +```` + +ABP 允许你在运行时通过使用 [设置系统](../Settings.md) 更进一步地更改这些选项. 你可以 [注入](../Dependency-Injection.md) `ISettingManager` 并使用 `Set...` 方法中的一种来更改用户, 租户或全局所有用户的选项值. + +**例如: 更改当前租户所需的密码最小长度** + +````csharp +public class MyService : ITransientDependency +{ + private readonly ISettingManager _settingManager; + + public MyService(ISettingManager settingManager) + { + _settingManager = settingManager; + } + + public async Task ChangeMinPasswordLength(int minLength) + { + await _settingManager.SetForCurrentTenantAsync( + IdentitySettingNames.Password.RequiredLength, + minLength.ToString() + ); + } +} +```` + +`IdentitySettingNames` 类 (在 `Volo.Abp.Identity.Settings` 命名空间中) 为设置名称定义了一些常量. + +## 分布式事件 + +此模块定义了如下 ETOs (事件传输对象) 以允许你去订阅模块中实体的改动. + +* `UserEto` 在 `IdentityUser` 实体更改完成时发布. +* `IdentityRoleEto` 在 `IdentityRole` 实体更改完成时发布. +* `IdentityClaimTypeEto` 在 `IdentityClaimType` 实体更改完成时发布. +* `OrganizationUnitEto` 在 `OrganizationUnit` 实体更改完成时发布. + +**例如: 当一个新用户被创建时接收通知** + +````csharp +public class MyHandler : + IDistributedEventHandler>, + ITransientDependency +{ + public async Task HandleEventAsync(EntityCreatedEto eventData) + { + UserEto user = eventData.Entity; + // TODO: ... + } +} +```` + +`UserEto` 和 `IdentityRoleEto` 默认自动发布事件. 你应该自己配置其他的 Eto. 请参阅 [分布式事件总线文档](../Distributed-Event-Bus.md) 了解预定义事件的详细信息. + +> 订阅分布式事件对于分布式场景 (如微服务架构) 特别有用. 如果你正在构建单体式应用程序, 或者在运行了身份模块的同一进程内监听事件, 那么订阅 [本地事件](../Local-Event-Bus.md) 更高效, 更简单. + +## 内部构件 + +本节包括模块的一些内部详细信息, 您不是那么需要, 但在一些情况下可能需要使用. + +### 领域层 + +#### 聚合 + +##### 用户 + +用户通常是一个登录并使用应用程序的人. + +* `IdentityUser` (聚合根): 表示系统中的一个用户. + * `IdentityUserRole` (集合): 分配给用户的角色. + * `IdentityUserClaim` (集合): 用户的自定义声明. + * `IdentityUserLogin` (集合): 用户的外部登录. + * `IdentityUserToken` (集合): 用户的令牌 (用于 Microsoft Identity 服务). + +##### 角色 + +角色通常是分配给用户的一组权限. + +* `IdentityRole` (聚合根): 表示系统中的角色. + * `IdentityRoleClaim` (集合): 角色的自定义声明. + +##### 声明类型 + +声明类型是可以被分配给系统中其他实体 (如角色和用户) 的自定义声明的定义. + +* `IdentityClaimType` (聚合根): 表示声明类型的定义. 它包括一些用于定义声明类型和验证规则的属性 (例如, 必须的, 正则表达式, 描述, 值类型) . + +##### 身份安全日志 + +`IdentitySecurityLog` 对象表示系统中与授权相关的操作 (如 *登录*) . + +* `IdentitySecurityLog` (聚合根): 表示系统中的安全日志. + +##### 组织单元 + +组织单元是一个有层级结构的实体. + +* ```OrganizationUnit``` (聚合根): 表示系统中的组织单元. + * ```Roles``` (集合): 组织单元的角色. + +#### 仓储 + +此模块定义了以下自定义仓储: + +* `IIdentityUserRepository` +* `IIdentityRoleRepository` +* `IIdentityClaimTypeRepository` +* ```IIdentitySecurityLogRepository``` +* ```IOrganizationUnitRepository``` + +#### 领域服务 + +##### 用户管理 + +`IdentityUserManager` 常用于管理用户, 他们的权限, 声明, 密码, 电子邮件等等. 它派生自 Microsoft Identity 的 `UserManager` 类, 其中 `T` 是 `IdentityUser`. + +##### 角色管理 + +`IdentityRoleManager` 常用于管理角色和他们的声明. 它派生自 Microsoft Identity 的 `RoleManager` 类, 其中 `T` 是 `IdentityRole`. + +##### 声明类型管理 + +`IdenityClaimTypeManager` 常用于对聚合根 `IdentityClaimType` 执行某些操作. + +##### 组织单元管理 + +```OrganizationUnitManager``` 常用于对聚合根 `OrganizationUnit` 执行某些操作. + +##### 安全日志管理 + +```IdentitySecurityLogManager``` 常用于保存安全日志. + +### 服务层 + +#### 应用服务 + +* `IdentityUserAppService` (实现 `IIdentityUserAppService`): 实现了用户管理 UI 的用例. +* `IdentityRoleAppService` (实现 `IIdentityRoleAppService`): 实现了角色管理 UI 的用例. +* `IdentityClaimTypeAppService` (实现 `IIdentityClaimTypeAppService`): 实现了声明类型管理 UI 的用例. +* `IdentitySettingsAppService` (实现 `IIdentitySettingsAppService`): 用于获取和更新身份模块的设置. +* `IdentityUserLookupAppService` (实现 `IIdentityUserLookupAppService`): 用于根据 `id` 或 `userName` 获取用户信息. 它旨在由ABP内部使用. +* `ProfileAppService` (实现 `IProfileAppService`): 用于更改用户的简介和密码. +* ```IdentitySecurityLogAppService``` (实现 ```IIdentitySecurityLogAppService```): 实现了安全日志 UI 的用例. +* ```OrganizationUnitAppService``` (实现 ```OrganizationUnitAppService```): 实现了组织单元管理 UI 的用例. + +### 数据库提供程序 + +此模块为数据库提供 [Entity Framework Core](../Entity-Framework-Core.md) 和 [MongoDB](../MongoDB.md) 两种选择. + +#### EF Core + +NuGet 包 [Volo.Abp.Identity.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Abp.Identity.EntityFrameworkCore) 实现了 EF Core 的集成. + +##### 数据库表 + +* **AbpRoles** + * AbpRoleClaims +* **AbpUsers** + * AbpUserClaims + * AbpUserLogins + * AbpUserRoles + * AbpUserTokens +* **AbpClaimTypes** +* **AbpOrganizationUnits** + * AbpOrganizationUnitRoles + * AbpUserOrganizationUnits +* **AbpSecurityLogs** + +#### MongoDB + +NuGet 包 [Volo.Abp.Identity.MongoDB](https://www.nuget.org/packages/Volo.Abp.Identity.MongoDB) 实现了 MongoDB 的集成. + +##### 数据库集合 + +* **AbpRoles** +* **AbpUsers** +* **AbpClaimTypes** +* **AbpOrganizationUnits** +* **AbpSecurityLogs** + +#### 常用数据库属性 + +你可以设置 `AbpIdentityDbProperties` 中的以下属性来更改数据库选项: + +* `DbTablePrefix` (`Abp` 作为默认值) 是表/集合名称的前缀. +* `DbSchema` (`null` 作为默认值) 是数据库架构. +* `ConnectionStringName` (`AbpIdentity` 作为默认值) 是此模块的 [连接字符串](../Connection-Strings.md) 名称. + +它们是静态属性. 你需要在开始运行应用程序前设置它们 (通常在 `Program.cs` 中). diff --git a/docs/zh-Hans/Modules/IdentityServer.md b/docs/zh-Hans/Modules/IdentityServer.md index cb82a640d5..1359c04e53 100644 --- a/docs/zh-Hans/Modules/IdentityServer.md +++ b/docs/zh-Hans/Modules/IdentityServer.md @@ -1 +1,173 @@ -TODO... \ No newline at end of file +# 身份服务器模块 + +身份服务器模块提供了一个 [IdentityServer](https://github.com/IdentityServer/IdentityServer4) (IDS) 的完全集成, 该框架提供高级身份验证功能, 如单点登录和API访问控制.此模块将客户端,资源以及其他 IDS 相关的对象保存到数据库中. + +## 如何安装 + +当你使用 ABP 框架 [创建一个新的解决方案](https://abp.io/get-started) 时, 此模块将被预安装(作为 NuGet/NPM 包).你可以继续用其作为包并轻松地获取更新, 也可以将其源代码包含在解决方案中(请参阅 `get-source` [CLI](../CLI.md))以开发自定义模块. + +### 源代码 + +可以 [在此处](https://github.com/abpframework/abp/tree/dev/modules/identityserver) 访问源代码.源代码使用 [MIT](https://choosealicense.com/licenses/mit/) 许可, 所以你可以免费使用和自定义它. + +## 用户界面 + +此模块使用了领域逻辑和数据库集成, 但没有提供任何 UI.如果你需要动态添加客户端和资源, 管理 UI 是非常有用的.在这种情况下, 你可以自己构建管理 UI, 或者考虑购买为此模块提供了管理 UI 的 [ABP 商业版](https://commercial.abp.io/). + +## 与其他模块的关系 + +此模块基于 [身份模块](Identity.md) 并且[账户模块](Account.md) 有一个 [集成包](https://www.nuget.org/packages/Volo.Abp.Account.Web.IdentityServer). + +## 选项 + +### AbpIdentityServerBuilderOptions + +`AbpIdentityServerBuilderOptions` 在你的身份服务器 [模块](https://docs.abp.io/zh-Hans/abp/latest/Module-Development-Basics) 中的 `PreConfigureServices` 方法中配置.例如: + +````csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(builder => + { + //Set options here... + }); +} +```` + +`AbpIdentityServerBuilderOptions` 属性: + +* `UpdateJwtSecurityTokenHandlerDefaultInboundClaimTypeMap` (默认值:true):更新 `JwtSecurityTokenHandler.DefaultInboundClaimTypeMap` 使其与身份服务器声明兼容. +* `UpdateAbpClaimTypes` (默认值:true):更新 `AbpClaimTypes` 与身份服务器声明兼容. +* `IntegrateToAspNetIdentity` (默认值:true):集成到 ASP.NET Identity. +* `AddDeveloperSigningCredential` (默认值:true):设置为 false 禁止调用 IIdentityServerBuilder 中的 `AddDeveloperSigningCredential()`. + +`IIdentityServerBuilder` 可以在你的身份服务器 [模块](https://docs.abp.io/zh-Hans/abp/latest/Module-Development-Basics) 中的 `PreConfigureServices` 方法中配置.例如: + +````csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(builder => + { + builder.AddSigningCredential(...); + }); +} +```` + +## 内部结构 + +### 领域层 + +#### 聚合 + +##### API 资源 + +需要 API 资源来允许客户端请求访问令牌. + +* `ApiResource` (聚合根):表示系统中的 API 资源. + * `ApiSecret` (集合):API 资源的密钥. + * `ApiScope` (集合):API 资源的作用域. + * `ApiResourceClaim` (集合):API 资源的声明. + +##### 客户端 + +客户端表示可以从你的身份服务器请求令牌的应用程序. + +* `Client` (聚合根):表示一个身份服务器的客户端应用程序. + * `ClientScope` (集合):客户端的作用域. + * `ClientSecret` (集合):客户端的密钥. + * `ClientGrantType` (集合):客户端的授权类型. + * `ClientCorsOrigin` (集合):客户端的 CORS 源. + * `ClientRedirectUri` (集合):客户端的重定向 URIs. + * `ClientPostLogoutRedirectUri` (集合):客户端的登出重定向 URIs. + * `ClientIdPRestriction` (集合):客户端的提供程序约束. + * `ClientClaim` (集合):客户端的声明. + * `ClientProperty` (集合):客户端的自定义属性. + +##### 持续化授权 + +持续化授权存储了授权码,刷新令牌和用户准许. + +* `PersistedGrant` (聚合根):表示为身份服务器持续化授权. + +##### 身份资源 + +身份资源是用户的用户 ID ,名称或邮件地址等数据. + +* `IdentityResource` (聚合根):表示与身份服务器的身份资源. + * `IdentityClaim` (集合):身份资源的声明. + +#### 仓储 + +为此模块定义了以下自定义仓储: + +* `IApiResourceRepository` +* `IClientRepository` +* `IPersistentGrantRepository` +* `IIdentityResourceRepository` + +#### 领域服务 + +此模块不包含任何领域服务, 但重写了下面的服务; + +* `AbpProfileService` (当 `AbpIdentityServerBuilderOptions.IntegrateToAspNetIdentity` 为 true 时使用) +* `AbpClaimsService` +* `AbpCorsPolicyService` + +### 设置 + +此模块未定义任何设置. + +### 应用层 + +#### 应用服务 + +* `ApiResourceAppService` (实现 `IApiResourceAppService`):实现了 API 资源管理 UI 的用例. +* `IdentityServerClaimTypeAppService` (实现 `IIdentityServerClaimTypeAppService`):用于获取声明列表. +* `ApiResourceAppService` (实现 `IApiResourceAppService`):实现了 API 管理资源 UI 的用例. +* `IdentityResourceAppService` (实现 `IIdentityResourceAppService`):实现了身份资源管理 UI 的用例. + +### 数据库提供程序 + +#### 公共 + +##### 表/集合 前缀 & 架构 + +所有表/集合都使用 `IdentityServer` 作为默认前缀.如果你需要改变表的前缀或设置一个架构名称(如果你的数据库提供程序支持), 请设置 `AbpIdentityServerDbProperties` 类的静态属性. + +##### 连接字符串 + +此模块使用 `AbpIdentityServer` 作为连接字符串的名称.如果你没有用这个名称定义连接字符串, 它将回退到 `Default` 连接字符串. + +有关详细信息, 请参阅 [连接字符串](https://docs.abp.io/zh-Hans/abp/latest/Connection-Strings) 文档. + +#### EF Core + +##### 表 + +* **IdentityServerApiResources** + * IdentityServerApiSecrets + * IdentityServerApiScopes + * IdentityServerApiScopeClaims + * IdentityServerApiClaims +* **IdentityServerClients** + * IdentityServerClientScopes + * IdentityServerClientSecrets + * IdentityServerClientGrantTypes + * IdentityServerClientCorsOrigins + * IdentityServerClientRedirectUris + * IdentityServerClientPostLogoutRedirectUris + * IdentityServerClientIdPRestrictions + * IdentityServerClientClaims + * IdentityServerClientProperties +* **IdentityServerPersistedGrants** +* **IdentityServerIdentityResources** + * IdentityServerIdentityClaims + +#### MongoDB + +##### 集合 + +* **IdentityServerApiResources** +* **IdentityServerClients** +* **IdentityServerPersistedGrants** +* **IdentityServerIdentityResources** diff --git a/docs/zh-Hans/Modules/Tenant-Management.md b/docs/zh-Hans/Modules/Tenant-Management.md new file mode 100644 index 0000000000..f8b0d60aa7 --- /dev/null +++ b/docs/zh-Hans/Modules/Tenant-Management.md @@ -0,0 +1,107 @@ +# 租户管理模块 + +[多租户](../Multi-Tenancy.md) 是 ABP 框架的核心功能之一. 它提供了构建 SaaS (软件及服务) 解决方案的基础架构. ABP 的多租户系统通过提供 `ITenantStore` 接口来抽象租户存储的位置. 您需要做的就是实现该接口. + +**租户管理模块是 `ITenantStore` 接口的实现. 它将租户存储在数据库中. 它还提供了 UI 来管理租户及其 [功能](../Features.md).** + +> 请参阅 [多租户](../Multi-Tenancy.md) 文档,以了解 ABP 框架的多租户系统. 本文重点介绍租户管理模块. + +### 关于商业版 SaaS 模块 + +[SaaS 模块](https://commercial.abp.io/modules/Volo.Saas) 是此模块的替代实现, 具有更多的功能和可能性. 它作为 [ABP 商业版](https://commercial.abp.io/) 订阅的一部分分发. + +## 如何安装 + +当你使用 ABP 框架 [创建一个新的解决方案](https://abp.io/get-started) 时, 此模块将被预安装(作为 NuGet/NPM 包). 你可以继续用其作为包并轻松地获取更新, 也可以将其源代码包含在解决方案中(请参阅 `get-source` [CLI](../CLI.md))以开发自定义模块. + +### 源代码 + +可以 [在此处](https://github.com/abpframework/abp/tree/dev/modules/identity) 访问源代码。源代码使用 [MIT](https://choosealicense.com/licenses/mit/) 许可, 所以你可以免费使用和自定义它. + +## 用户界面 + +此模块将 "管理 -> 租户管理 -> 租户" 菜单项添加到应用程序的主菜单, 这将打开如下页面: + +![租户管理页面](../images/module-tenant-management-page.png) + +在这个页面中, 你可以查看所有租户. 你可以按照如下所示创建一个新的租户: + +![租户管理新增租户](../images/module-tenant-management-new-tenant.png) + +在这个模态框中; + +* **租户名称**: 租户唯一的名称. 如果为租户使用二级域名, 这将是二级域名名称. +* **管理员电子邮件地址**: 租户管理员的电子邮件地址. +* **管理员密码**: 租户管理员的密码. + +当您点击租户附近的 *操作* 按钮时, 您将看见可以执行的操作: + +![租户管理操作](../images/module-tenant-management-actions.png) + +### 管理租户功能 + +功能操作会打开一个模态框, 来对相关租户进行启用/禁用/设置 [功能](../Features.md). 这有一个模态框的例子: + +![功能模态框](../images/features-modal.png) + +### 管理主机功能 + +如果您使用应用程序的功能也在主机端, *管理主机功能* 按钮则用于设置主机端的功能. + +## 内部结构 + +如果要 [自定义](../Customizing-Application-Modules-Guide.md) 此模块而不更改 [其源代码](https://github.com/abpframework/abp/tree/dev/modules/tenant-management), 则可以将此节作为参考. + +### 领域层 + +#### 聚合 + +* `Tenant` + +#### 仓储 + +* `ITenantRepository` + +#### 领域服务 + +* `TenantManager` + +### 应用层 + +#### 应用服务 + +* `TenantAppService` + +#### 权限 + +- `AbpTenantManagement.Tenants`: 租户管理. +- `AbpTenantManagement.Tenants.Create`: 创建一个新的租户. +- `AbpTenantManagement.Tenants.Update`: 编辑一个已存在的租户. +- `AbpTenantManagement.Tenants.Delete`: 删除一个已存在的租户. +- `AbpTenantManagement.Tenants.ManageFeatures`: 管理租户的功能. + +### EF Core 集成 + +* `TenantManagementDbContext` (实现了 `ITenantManagementDbContext`) + +**数据库表:** + +* `AbpTenants` +* `AbpTenantConnectionStrings` + +### MongoDB 集成 + +* `TenantManagementMongoDbContext` (实现了 `ITenantManagementMongoDbContext`) + +**数据库集合:** + +* `AbpTenants` (也包括连接字符串) + +## 注意事项 + +ABP 框架允许租户有专用的数据库, 以允许 *每个租户用一个数据库*. 此模块具有使该实现成为可能 (查阅其源代码) 的基础架构, 但是它没有实现应用程序层和 UI 功能以使其开箱即用. 您可以自己实现这些功能, 也可以考虑使用已完全实现这些功能并提供了更多业务功能的 [ABP 商业版 Saas 模块](https://docs.abp.io/en/commercial/latest/modules/saas). + +## 另请参见 + +* [多租户](../Multi-Tenancy.md) +* [ABP 商业版 SaaS 模块](https://docs.abp.io/en/commercial/latest/modules/saas) diff --git a/docs/zh-Hans/Modules/Tentant-Management.md b/docs/zh-Hans/Modules/Tentant-Management.md deleted file mode 100644 index fa999428dd..0000000000 --- a/docs/zh-Hans/Modules/Tentant-Management.md +++ /dev/null @@ -1,3 +0,0 @@ -# 租户管理模块 - -TODO \ No newline at end of file diff --git a/docs/zh-Hans/Tutorials/Part-1.md b/docs/zh-Hans/Tutorials/Part-1.md index 3ff216fb76..927463e100 100644 --- a/docs/zh-Hans/Tutorials/Part-1.md +++ b/docs/zh-Hans/Tutorials/Part-1.md @@ -2,32 +2,15 @@ ````json //[doc-params] { - "UI": ["MVC","NG"], + "UI": ["MVC","Blazor","BlazorServer","NG"], "DB": ["EF","Mongo"] } ```` -{{ -if UI == "MVC" - UI_Text="mvc" -else if UI == "NG" - UI_Text="angular" -else - UI_Text="?" -end -if DB == "EF" - DB_Text="Entity Framework Core" -else if DB == "Mongo" - DB_Text="MongoDB" -else - DB_Text="?" -end -}} - ## 关于本教程 在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: -* **{{DB_Text}}** 做为ORM提供程序. +* **{{DB_Text}}** 做为数据库提供程序. * **{{UI_Value}}** 做为UI框架. 本教程分为以下部分: @@ -40,16 +23,29 @@ end - [Part 6: 作者: 领域层](Part-6.md) - [Part 7: 作者: 数据库集成](Part-7.md) - [Part 8: 作者: 应用服务层](Part-8.md) -- [Part 9: 作者: 用户页面](Part-9.md) +- [Part 9: 作者: 用户界面](Part-9.md) - [Part 10: 图书到作者的关系](Part-10.md) ## 下载源码 -本教程根据你的**UI** 和 **Database**偏好有多个版,我们准备了两种可供下载的源码组合: +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: * [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) * [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +{{if UI == "MVC" && DB == "EF"}} + +### 视频教程 + +本章也被录制为视频教程 **发布在YouTube**. + +{{end}} ## 创建解决方案 在开始开发之前,请按照[入门教程](../Getting-Started.md)创建名为 `Acme.BookStore` 的新解决方案. @@ -58,12 +54,12 @@ end 启动模板中的**领域层**分为两个项目: - - `Acme.BookStore.Domain`包含你的[实体](https://docs.abp.io/zh-Hans/abp/latest/Entities), [领域服务](https://docs.abp.io/zh-Hans/abp/latest/Domain-Services)和其他核心域对象. + - `Acme.BookStore.Domain`包含你的[实体](../Entities.md), [领域服务](../Domain-Services.md)和其他核心域对象. - `Acme.BookStore.Domain.Shared`包含可与客户共享的常量,枚举或其他域相关对象. -在解决方案的**领域层**(`Acme.BookStore.Domain`项目)中定义你的实体. +在解决方案的**领域层**(`Acme.BookStore.Domain`项目)中定义你的实体. -该应用程序的主要实体是`Book`. 在`Acme.BookStore.Domain`项目中创建一个 `Books` 文件夹并在其中添加一个名为 `Book` 的类,如下所示: +该应用程序的主要实体是`Book`. 在`Acme.BookStore.Domain`项目中创建一个 `Books` 文件夹(命名空间),并在其中添加名为 `Book` 的类,如下所示: ````csharp using System; @@ -84,15 +80,15 @@ namespace Acme.BookStore.Books } ```` -* ABP为实体提供了两个基本的基类: `AggregateRoot`和`Entity`. **Aggregate Root**是[**领域驱动设计**](./Domain-Driven-Design.md) 概念之一. 可以视为直接查询和处理的根实体(请参阅[实体文档](../Entities.md)). -* `Book`实体继承了`AuditedAggregateRoot`,`AuditedAggregateRoot`类在`AggregateRoot`类的基础上添加了一些审计属性(`CreationTime`, `CreatorId`, `LastModificationTime` 等). ABP框架自动为你管理这些属性. -* `Guid`是`Book`实体的主键类型. +* ABP为实体提供了两个基本的基类: `AggregateRoot`和`Entity`. **Aggregate Root**是[**领域驱动设计**](../Domain-Driven-Design.md) 概念之一. 可以视为直接查询和处理的根实体(请参阅[实体文档](../Entities.md)). +* `Book`实体继承了`AuditedAggregateRoot`,`AuditedAggregateRoot`类在`AggregateRoot`类的基础上添加了一些基础[审计](../Audit-Logging.md)属性(例如`CreationTime`, `CreatorId`, `LastModificationTime` 等). ABP框架自动为你管理这些属性. +* `Guid`是`Book`实体的**主键类型**. > 为了保持简单,本教程将实体属性保留为 **public get/set** . 如果你想了解关于DDD最佳实践,请参阅[实体文档](../Entities.md). ### BookType枚举 -上面所用到了 `BookType` 枚举,在 `Acme.BookStore.Domain.Shared` 项目创建 `BookType`. +`Book`实体使用了`BookType`枚举. 在`Acme.BookStore.Domain.Shared`项目中创建`Books`文件夹(命名空间),并在其中添加`BookType`: ````csharp namespace Acme.BookStore.Books @@ -150,28 +146,37 @@ public class BookStoreMongoDbContext : AbpMongoDbContext ### 将Book实体映射到数据库表 -在 `Acme.BookStore.EntityFrameworkCore` 项目中打开 `BookStoreDbContextModelCreatingExtensions.cs` 文件,添加 `Book` 实体的映射代码. 最终类应为: + +打开`BookStoreDbContext`类的`OnModelCreating`方法,为`Book`实体添加映射代码: ````csharp using Acme.BookStore.Books; -using Microsoft.EntityFrameworkCore; -using Volo.Abp; -using Volo.Abp.EntityFrameworkCore.Modeling; +... namespace Acme.BookStore.EntityFrameworkCore { - public static class BookStoreDbContextModelCreatingExtensions + public class BookStoreDbContext : + AbpDbContext, + IIdentityDbContext, + ITenantManagementDbContext { - public static void ConfigureBookStore(this ModelBuilder builder) + ... + + protected override void OnModelCreating(ModelBuilder builder) { - Check.NotNull(builder, nameof(builder)); + base.OnModelCreating(builder); + + /* Include modules to your migration db context */ + + builder.ConfigurePermissionManagement(); + ... /* Configure your own tables/entities inside here */ builder.Entity(b => { b.ToTable(BookStoreConsts.DbTablePrefix + "Books", - BookStoreConsts.DbSchema); + BookStoreConsts.DbSchema); b.ConfigureByConvention(); //auto configure for the base class props b.Property(x => x.Name).IsRequired().HasMaxLength(128); }); @@ -180,14 +185,14 @@ namespace Acme.BookStore.EntityFrameworkCore } ```` -* `BookStoreConsts` 含有用于表的架构和表前缀的常量值. 你不必使用它,但建议在单点控制表前缀. -* `ConfigureByConvention()` 方法优雅的配置/映射继承的属性,应始终对你所有的实体使用它. +* `BookStoreConsts` 含有用于表的架构和表前缀的常量值. 使用它不是强制的,但建议在统一的地方控制表前缀. +* `ConfigureByConvention()` 方法优雅的配置/映射继承的属性,应对所有的实体使用它. ### 添加数据迁移 -启动模板使用[EF Core Code First Migrations](https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/)创建和维护数据库架构. 我们应该创建一个新的迁移并且应用到数据库. +本示例使用[EF Core Code First Migrations](https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/).因为我们修改了数据库映射配置,我们必须创建一个新的迁移并且应用到数据库. -在 `Acme.BookStore.EntityFrameworkCore.DbMigrations` 目录打开命令行终端输入以下命令: +在 `Acme.BookStore.EntityFrameworkCore` 目录打开命令行终端输入以下命令: ```bash dotnet ef migrations add Created_Book_Entity @@ -205,7 +210,7 @@ dotnet ef migrations add Created_Book_Entity > >在运行应用程序之前最好将初始数据添加到数据库中. 本节介绍ABP框架的[数据种子系统](../Data-Seeding.md). 如果你不想创建种子数据可以跳过本节,但是建议你遵循它来学习这个有用的ABP Framework功能。 -在 `*.Domain` 项目下创建派生 `IDataSeedContributor` 的类,并且拷贝以下代码: +在 `*.Domain` 项目下创建 `IDataSeedContributor` 的派生类,并且拷贝以下代码: ```csharp using System; @@ -258,7 +263,7 @@ namespace Acme.BookStore } ``` -* 如果数据库中当前没有图书,则此代码使用 `IRepository`(默认为[repository](../Repositories.md))将两本书插入数据库. +* 如果数据库中当前没有图书,则此代码使用 `IRepository`(默认[repository](../Repositories.md))将两本书插入数据库. ### 更新数据库 @@ -279,7 +284,7 @@ namespace Acme.BookStore ### BookDto -`CrudAppService` 基类需要定义实体的基本DTO. 在 `Acme.BookStore.Application.Contracts` 项目中创建一个名为 `BookDto` 的DTO类: +`CrudAppService` 基类需要定义实体的基本DTO. 在 `Acme.BookStore.Application.Contracts` 项目中创建 `Books` 文件夹(命名空间), 并在其中添加名为 `BookDto` 的DTO类: ````C# using System; @@ -300,9 +305,9 @@ namespace Acme.BookStore } ```` -* **DTO**类被用来在 **表示层** 和 **应用层** **传递数据**.查看[DTO文档](https://docs.abp.io/zh-Hans/abp/latest/Data-Transfer-Objects)查看更多信息. -* 为了在页面上展示书籍信息,`BookDto`被用来将书籍数据传递到表示层. -* `BookDto`继承自 `AuditedEntityDto`.跟上面定义的 `Book` 实体一样具有一些审计属性. +* **DTO**类被用来在 **表示层** 和 **应用层** **传递数据**.参阅[DTO文档](https://docs.abp.io/zh-Hans/abp/latest/Data-Transfer-Objects). +* 为了在用户界面上展示书籍信息,`BookDto`被用来将书籍数据传递到表示层. +* `BookDto`继承自 `AuditedEntityDto`.与上面定义的 `Book` 实体一样具有一些审计属性. 在将书籍返回到表示层时,需要将`Book`实体转换为`BookDto`对象. [AutoMapper](https://automapper.org)库可以在定义了正确的映射时自动执行此转换. 启动模板配置了AutoMapper,因此你只需在`Acme.BookStore.Application`项目的`BookStoreApplicationAutoMapperProfile`类中定义映射: @@ -322,12 +327,11 @@ namespace Acme.BookStore } ```` -> 参阅 [对象对对象映射](../Object-To-Object-Mapping.md) 文档了解详情. +> 参阅 [对象到对象映射](../Object-To-Object-Mapping.md) 文档了解详情. ### CreateUpdateBookDto -在`Acme.BookStore.Application.Contracts`项目中创建一个名为 `CreateUpdateBookDto` 的DTO类: - +在`Acme.BookStore.Application.Contracts`项目中创建 `Books` 文件夹(命名空间),并在其中添加名为 `CreateUpdateBookDto` 的DTO类: ````csharp using System; using System.ComponentModel.DataAnnotations; @@ -354,7 +358,7 @@ namespace Acme.BookStore.Books ```` * 这个DTO类被用于在创建或更新书籍的时候从用户界面获取图书信息. -* 它定义了数据注释属性(如`[Required]`)来定义属性的验证. DTO由ABP框架[自动验证](https://docs.abp.io/zh-Hans/abp/latest/Validation). +* 它定义了数据注释特性(如`[Required]`)来定义属性的验证规则. DTO由ABP框架[自动验证](https://docs.abp.io/zh-Hans/abp/latest/Validation). 就像上面的`BookDto`一样,创建一个从`CreateUpdateBookDto`对象到`Book`实体的映射,最终映射配置类如下: @@ -378,7 +382,7 @@ namespace Acme.BookStore ### IBookAppService -下一步是为应用程序定义接口,在`Acme.BookStore.Application.Contracts`项目中定义一个名为`IBookAppService`的接口: +下一步是为应用程序定义接口,在`Acme.BookStore.Application.Contracts`项目创建 `Books` 文件夹(命名空间),并在其中添加名为`IBookAppService`的接口: ````csharp using System; @@ -400,12 +404,12 @@ namespace Acme.BookStore.Books ```` * 框架定义应用程序服务的接口**不是必需的**. 但是,它被建议作为最佳实践. -* `ICrudAppService`定义了常见的**CRUD**方法:`GetAsync`,`GetListAsync`,`CreateAsync`,`UpdateAsync`和`DeleteAsync`. 你可以从空的`IApplicationService`接口继承并手动定义自己的方法(将在下一部分中完成). -* `ICrudAppService`有一些变体, 你可以在每个方法中使用单独的DTO,也可以分别单独指定(例如使用不同的DTO进行创建和更新). +* `ICrudAppService`定义了常见的**CRUD**方法:`GetAsync`,`GetListAsync`,`CreateAsync`,`UpdateAsync`和`DeleteAsync`. 从这个接口扩展不是必需的,你可以从空的`IApplicationService`接口继承并手动定义自己的方法(将在下一部分中完成). +* `ICrudAppService`有一些变体, 你可以在每个方法中使用单独的DTO(例如使用不同的DTO进行创建和更新). ### BookAppService -在`Acme.BookStore.Application`项目中创建名为 `BookAppService` 的 `IBookAppService` 实现: +是时候实现`IBookAppService`接口了.在`Acme.BookStore.Application`项目中创建 `Books` 文件夹(命名空间),并在其中添加名为 `BookAppService` 的类: ````csharp using System; @@ -439,19 +443,20 @@ namespace Acme.BookStore.Books ### 自动生成API Controllers -你通常创建**Controller**以将应用程序服务公开为**HTTP API**端点. 因此允许浏览器或第三方客户端通过AJAX调用它们. +在典型的ASP.NET Core应用程序中,你创建**API Controller**以将应用程序服务公开为**HTTP API**端点. 这将允许浏览器或第三方客户端通过HTTP调用它们. -ABP可以[**自动**](../API/Auto-API-Controllers.md)按照惯例将你的应用程序服务配置为MVC API控制器. +ABP可以[**自动**](../API/Auto-API-Controllers.md)按照约定将你的应用程序服务配置为MVC API控制器. ### Swagger UI 启动模板配置为使用[Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)运行[swagger UI](https://swagger.io/tools/swagger-ui/). 运行应用程序并在浏览器中输入`https://localhost:XXXX/swagger/`(用你自己的端口替换XXXX)作为URL. +使用`CTRL+F5`运行应用程序 ({{if UI=="MVC"}}`Acme.BookStore.Web`{{else}}`Acme.BookStore.HttpApi.Host`{{end}})并使用浏览器访问`https://localhost:/swagger/` on your browser. 使用你自己的端口号替换 ``. -你会看到一些内置的接口和`Book`的接口,它们都是REST风格的: +你会看到一些内置的服务端点和`Book`服务,它们都是REST风格的端点: -![bookstore-swagger](images/bookstore-swagger.png) +![bookstore-swagger](./images/bookstore-swagger.png) -Swagger有一个很好的UI来测试API. +Swagger有一个很好的UI来测试API. 你可以尝试执行`[GET] /api/app/book` API来获取书籍列表, 服务端会返回以下JSON结果: diff --git a/docs/zh-Hans/Tutorials/Part-2.md b/docs/zh-Hans/Tutorials/Part-2.md index 231ca226fa..fe0dd84c45 100644 --- a/docs/zh-Hans/Tutorials/Part-2.md +++ b/docs/zh-Hans/Tutorials/Part-2.md @@ -2,26 +2,10 @@ ````json //[doc-params] { - "UI": ["MVC","NG"], + "UI": ["MVC","Blazor","BlazorServer","NG"], "DB": ["EF","Mongo"] } ```` - -{{ -if UI == "MVC" - DB="ef" - DB_Text="Entity Framework Core" - UI_Text="mvc" -else if UI == "NG" - DB="mongodb" - DB_Text="MongoDB" - UI_Text="angular" -else - DB ="?" - UI_Text="?" -end -}} - ## 关于本教程 在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: @@ -44,16 +28,30 @@ end ## 下载源码 -本教程根据你的**UI** 和 **Database**偏好有多个版,我们准备了两种可供下载的源码组合: +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: * [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) * [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +{{if UI == "MVC" && DB == "EF"}} + +### 视频教程 + +本章也被录制为视频教程 **发布在YouTube**. + +{{end}} + {{if UI == "MVC"}} ## 动态JavaScript代理 -通常在 **JavaScript** 端通过AJAX调用HTTP API端点. 你可以使用 `$.ajax` 或其他工具来调用端点. 但是ABP提供了更好的方法. +在 **JavaScript** 端通过AJAX调用HTTP API端点是常见的做法. 你可以使用 `$.ajax` 或其他工具来调用端点. 但是ABP提供了更好的方法. ABP**动态**为所有API端点创建 **[JavaScript代理](../UI/AspNetCore/Dynamic-JavaScript-Proxies.md)**. 所以你可以像调用**Javascript本地方法**一样使用任何**端点**. @@ -79,21 +77,21 @@ acme.bookStore.books.book.getList({}).done(function (result) { console.log(resul ![bookstore-getlist-result-network](images/bookstore-getlist-result-network.png) -Let's **create a new book** using the `create` function: - 让我们使用 `create` 函数**创建一本书**: ````js -acme.bookStore.books.book.create({ - name: 'Foundation', - type: 7, - publishDate: '1951-05-24', - price: 21.5 - }).then(function (result) { - console.log('successfully created the book with id: ' + result.id); +acme.bookStore.books.book.create({ + name: 'Foundation', + type: 7, + publishDate: '1951-05-24', + price: 21.5 + }).then(function (result) { + console.log('successfully created the book with id: ' + result.id); }); ```` +> 如果你下载了本教程的源代码并按照示例中的步骤操作,你需要传递`authorId`参数给创建方法以**创建一本新书**. + 您应该在控制台中看到类似以下的消息: ````text @@ -102,7 +100,7 @@ successfully created the book with id: 439b0ea8-923e-8e1e-5d97-39f2c7ac4246 检查数据库中的 `Books` 表你会看到新的一行. 你可以自己尝试使用 `get`, `update` 和 `delete` 函数. -我们将利用这些动态代理功能在接下来的章节来与服务器通信. +在接下来的章节,我们将利用这些动态代理函数与服务器通信. {{end}} @@ -112,7 +110,7 @@ successfully created the book with id: 439b0ea8-923e-8e1e-5d97-39f2c7ac4246 本地化文本位于 `Acme.BookStore.Domain.Shared` 项目的 `Localization/BookStore` 文件夹下: -![bookstore-localization-files](./images/bookstore-localization-files-v2.png) +![bookstore-localization-files](images/bookstore-localization-files-v2.png) 打开 `en.json` (*英文翻译*)文件并更改内容,如下所示: @@ -154,7 +152,7 @@ successfully created the book with id: 439b0ea8-923e-8e1e-5d97-39f2c7ac4246 * 为按钮项添加 `Menu:` 前缀. * 使用 `Enum::` 命名约定来本地化枚举成员. 当您这样做时ABP可以在某些适当的情况下自动将枚举本地化. -如果未在本地化文件中定义文本,则文本将**回退**到本地化键(作为ASP.NET Core的标准行为). +如果未在本地化文件中定义文本,则文本将**回退**到本地化键(ASP.NET Core的标准行为). > ABP本地化系统建立在[ASP.NET Core标准本地化](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization)系统之上,并以多种方式进行了扩展. 有关详细信息请参见[本地化文档](../Localization.md). @@ -162,7 +160,7 @@ successfully created the book with id: 439b0ea8-923e-8e1e-5d97-39f2c7ac4246 ## 创建图书页面 -是时候创建可见的和可用的东西了! 代替经典的MVC,我们将使用微软推荐的[Razor Pages UI](https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/razor-pages/razor-pages-start). +是时候创建可见的和可用的东西了! 我们将使用微软推荐的[Razor Pages UI](https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/razor-pages/razor-pages-start),而不是经典的MVC. 在 `Acme.BookStore.Web` 项目的 `Pages` 文件夹下创建一个名为新的 `Books` 的文件夹. 然后在文件夹右键选择 **添加 > Razor Page** 菜单. 输入名称 `Index`: @@ -189,13 +187,13 @@ namespace Acme.BookStore.Web.Pages.Books { public void OnGet() { - + } } } ``` -### 将Book页面添加到主菜单 +### 将图书页面添加到主菜单 打开 `Menus` 文件夹中的 `BookStoreMenuContributor` 类,在 `ConfigureMainMenuAsync` 方法的底部添加如下代码: @@ -223,9 +221,7 @@ context.Menu.AddItem( ### 图书列表 -We will use the [Datatables.net](https://datatables.net/) jQuery library to show the book list. Datatables library completely work via AJAX, it is fast, popular and provides a good user experience. - -我们将使用[Datatables.net](https://datatables.net/)JQuery插件来显示页面上的表格列表. [Datatables](https://datatables.net/)可以完全通过AJAX工作,速度快,并提供良好的用户体验. +我们将使用[Datatables.net](https://datatables.net/)JQuery插件来显示图书列表. [Datatables](https://datatables.net/)可以完全通过AJAX工作,速度快,并提供良好的用户体验. > Datatables插件在启动模板中配置,因此你可以直接在任何页面中使用它,无需在页面中引用样式和脚本文件. @@ -261,7 +257,7 @@ We will use the [Datatables.net](https://datatables.net/) jQuery library to show 在 `Pages/Books/` 文件夹中创建 `index.js`文件 -![bookstore-index-js-file](./images/bookstore-index-js-file-v3.png) +![bookstore-index-js-file](images/bookstore-index-js-file-v3.png) `index.js` 的内容如下: @@ -321,8 +317,8 @@ $(function () { ```` * `abp.localization.getResource` 获取一个函数,该函数用于使用服务器端定义的相同JSON文件对文本进行本地化. 通过这种方式你可以与客户端共享本地化值. -* `abp.libs.datatables.normalizeConfiguration`是另一个辅助方法.不是必须的, 但是它通过为缺少的选项提供常规值来简化数据表配置. -* `abp.libs.datatables.createAjax`是帮助ABP的动态JavaScript API代理跟[Datatable](https://datatables.net/)的格式相适应的辅助方法. +* `abp.libs.datatables.normalizeConfiguration`是一个辅助方法.不是必须的, 但是它通过为缺省的选项提供约定的值来简化[Datatables](https://datatables.net/)配置. +* `abp.libs.datatables.createAjax`是另一个辅助方法,用来适配ABP的动态JavaScript API代理和[Datatable](https://datatables.net/)期望的参数格式. * `acme.bookStore.books.book.getList` 是动态JavaScript代理函数(上面已经介绍过了) * [luxon](https://moment.github.io/luxon/) 库也是该解决方案中预先配置的标准库,你可以轻松地执行日期/时间操作. @@ -334,15 +330,13 @@ $(function () { ![Book list](images/bookstore-book-list-3.png) -这是一个完全正常工作的服务端分页,排序和本地化的图书列表. - -{{end}} +这是一个可以正常工作的,服务端分页,排序和本地化的图书列表. {{if UI == "NG"}} ## 安装NPM包 ->注意: 本教程基于ABP Framework v3.0.3+. 如果你的项目版本较旧,请升级您的解决方案. 如果要升级现有的v2.x项目,请参阅[迁移指南](../UI/Angular/Migration-Guide-v3.md). +>注意: 本教程基于ABP Framework v3.1.0+. 如果你的项目版本较旧,请升级您的解决方案. 如果要升级现有的v2.x项目,请参阅[迁移指南](../UI/Angular/Migration-Guide-v3.md). 在 `angular` 目录下打开命令行窗口,选择 `yarn` 命令安装NPM包: @@ -357,9 +351,7 @@ yarn - [Ng Bootstrap](https://ng-bootstrap.github.io/#/home) 用做UI组件库. - [ngx-datatable](https://swimlane.gitbook.io/ngx-datatable/) 用做 datatable 类库. -### BookModule - -运行以下命令创建一个名为 `BookModule` 的新模块: +运行以下命令在angular应用程序根目录创建一个名为 `BookModule` 的新模块: ```bash yarn ng generate module book --module app --routing --route books @@ -417,7 +409,7 @@ const routes: Routes = [ ]; ```` -现在打开 `src/app/route.provider.ts` 以下替换 `configureRoutes` 函数: +现在打开 `src/app/route.provider.ts` 替换 `configureRoutes` 函数为以下代码: ```js function configureRoutes(routes: RoutesService) { @@ -450,27 +442,31 @@ function configureRoutes(routes: RoutesService) { `RoutesService` 是ABP框架提供的用于配置主菜单和路由的服务. -* `path` 路由的URL. +* `path` 路由的URL. * `name` 菜单项的名称(参阅[本地化文档](../UI/Angular/Localization.md)了解更多). -* `iconClass` 菜单项的图标(你可以使用默认的[Font Awesome](https://fontawesome.com/)图标). -* `order` 菜单项的排序.我们定义了101,它显示在 "Administration" 项的后面. -* `layout` BooksModule路由的布局. 可以定义 `eLayoutType.application`, `eLayoutType.account` 或 `eLayoutType.empty`. +* `iconClass` 菜单项的图标(你可以使用默认的[Font Awesome](https://fontawesome.com/)图标). +* `order` 菜单项的排序. +* `layout` BooksModule路由的布局. (有三个预定义的布局类型: `eLayoutType.application`, `eLayoutType.account` 或 `eLayoutType.empty`). + +更多信息请参阅[RoutesService 文档](../UI/Angular/Modifying-the-Menu.md#via-routesservice). -更多信息请参阅[RoutesService 文档](https://docs.abp.io/en/abp/latest/UI/Angular/Modifying-the-Menu.md#via-routesservice). +### 生成服务代理 -### 生成代理 +[ABP CLI](../CLI.md) 提供 `generate-proxy` 命令为HTTP APIs生成客户端代理.有了这些代理,在客户端使用HTTP APIs变得更加方便. 运行 `generate-proxy` 命令前, 你的 host 必须正在运行. -ABP CLI提供了 `generate-proxy` 命令为你的服务HTTP API生成客户端代理简化客户端使用服务的成本. 运行 `generate-proxy` 命令前你的host必须正在运行. 参阅 [CLI 文档](../CLI.md). +> **警告**: 使用IIS Express时有一个问题; 它不允许从另一个进程连接应用程序. 如果你使用Visual Studio, 在运行按钮的下拉框中选择`Acme.BookStore.HttpApi.Host`,不要选择IIS Express, 如下图: -在 `angular` 文件夹下运行以下命令: +![vs-run-without-iisexpress](images/vs-run-without-iisexpress.png) + +启动host应用程序后,在 `angular` 文件夹下运行以下命令: ```bash abp generate-proxy -t ng ``` -生成的文件如下: +这个命令将在`/src/app/proxy/books`文件夹下产生以下文件: -![Generated files](./images/generated-proxies-2.png) +![Generated files](images/generated-proxies-3.png) ### BookComponent @@ -479,8 +475,7 @@ abp generate-proxy -t ng ```js import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { BookDto } from './models'; -import { BookService } from './services'; +import { BookService, BookDto } from '@proxy/books'; @Component({ selector: 'app-book', @@ -494,7 +489,7 @@ export class BookComponent implements OnInit { constructor(public readonly list: ListService, private bookService: BookService) {} ngOnInit() { - const bookStreamCreator = (query) => this.bookService.getListByInput(query); + const bookStreamCreator = (query) => this.bookService.getList(query); this.list.hookToQuery(bookStreamCreator).subscribe((response) => { this.book = response; @@ -503,8 +498,8 @@ export class BookComponent implements OnInit { } ``` -* 我们注入了生成的 `BookService`. -* 我们实现了 [ListService](https://docs.abp.io/en/abp/latest/UI/Angular/List-Service),它是一个公用服务,提供了简单的分页,排序和搜索. +* 我们引入并注入了生成的 `BookService`. +* 我们使用 [ListService](../UI/Angular/List-Service.md),它是一个工具服务,提供了易用的分页,排序和搜索. 打开 `/src/app/book/book.component.html` 用以下内容替换它: @@ -545,9 +540,138 @@ export class BookComponent implements OnInit { 现在你可以在浏览器看到最终结果: -![Book list final result](./images/bookstore-book-list.png) +![图书列表最终结果](images/bookstore-book-list.png) -{{end}} +{{else if UI == "Blazor" || UI == "BlazorServer"}} + +## 创建图书页面 + +是时候创建可见和可用的东西了! 右击`Acme.BookStore.Blazor`项目下的`Pages`文件夹,新建一个名为`Books.razor`的**razor组件**. + +![blazor-add-books-component](images/blazor-add-books-component.png) + +用以下内容替换这个组件的内容: + +````html +@page "/books" + +

    Books

    + +@code { + +} +```` + +### 将图书页面添加到主菜单 + +打开`Blazor`项目中的`BookStoreMenuContributor`类,在 `ConfigureMainMenuAsync` 方法的底部添加如下代码: + +````csharp +context.Menu.AddItem( + new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" + ).AddItem( + new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/books" + ) + ) +); +```` + +运行项目,使用用户名 `admin` 和密码 `1q2w3E*` 登录到应用程序. 看到新菜单项已添加到顶部栏: + +![blazor-menu-bookstore](images/blazor-menu-bookstore.png) + +点击BookStore下的Books子菜单项就会跳转到空的图书页面. + +### 图书列表 + +我们将使用[Blazorise library](https://blazorise.com/)作为UI组件.它是一个强大的库,支持主要的HTML/CSS框架,包括Bootstrap. + +ABP提供了一个通用的基类,`AbpCrudPageBase<...>`,用来创建CRUD风格的页面.这个基类兼容用来构建`IBookAppService`的`ICrudAppService`.所以我们从`AbpCrudPageBase`继承,获得标准CRUD的默认实现. + +打开`Books.razor` 并把内容修改成下面这样: + +````xml +@page "/books" +@using Volo.Abp.Application.Dtos +@using Acme.BookStore.Books +@using Acme.BookStore.Localization +@using Microsoft.Extensions.Localization +@inject IStringLocalizer L +@inherits AbpCrudPageBase + + + +

    @L["Books"]

    +
    + + + + + + + @L[$"Enum:BookType:{(int)context.Type}"] + + + + + @context.PublishDate.ToShortDateString() + + + + + + + @context.CreationTime.ToLongDateString() + + + + + +
    +```` + +> 如果你可以编译并运行成功,但看到一些语法错误.你可以忽略这些错误,因为Visual Studio处理Blazor还有一些bug. + +* `AbpCrudPageBase`实现了所有的CRUD细节,我们从它继承. +* `Entities`, `TotalCount`, `PageSize`, `OnDataGridReadAsync`定义在基类中. +* 注入`IStringLocalizer` (作为`L`对象),用于本地化. + +虽然上面的代码非常容易理解,你仍然可以查看Blazorise [Card](https://blazorise.com/docs/components/card/)和[DataGrid](https://blazorise.com/docs/extensions/datagrid/)文档以更好地理解它们. + +#### 关于AbpCrudPageBase + +对于图书页面,我们将持续从`AbpCrudPageBase`获得益处. 你可以只注入`IBookAppService`并自己执行所有的服务端调用(感谢ABP的[动态C# HTTP API客户端代理](../API/Dynamic-CSharp-API-Clients.md)系统). + +## 运行最终应用程序 + +你可以运行应用程序!该部分的最终用户界面如下所示: + +![blazor-bookstore-book-list](images/blazor-bookstore-book-list.png) + +这是一个可以正常工作的,服务端分页,排序和本地化的图书列表. + +{{end # UI }} ## 下一章 diff --git a/docs/zh-Hans/Tutorials/Part-3.md b/docs/zh-Hans/Tutorials/Part-3.md index 90dd1326c3..6a9740730d 100644 --- a/docs/zh-Hans/Tutorials/Part-3.md +++ b/docs/zh-Hans/Tutorials/Part-3.md @@ -2,27 +2,10 @@ ````json //[doc-params] { - "UI": ["MVC","NG"], + "UI": ["MVC","Blazor","BlazorServer","NG"], "DB": ["EF","Mongo"] } ```` -{{ -if UI == "MVC" - UI_Text="mvc" -else if UI == "NG" - UI_Text="angular" -else - UI_Text="?" -end -if DB == "EF" - DB_Text="Entity Framework Core" -else if DB == "Mongo" - DB_Text="MongoDB" -else - DB_Text="?" -end -}} - ## 关于本教程 在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: @@ -45,16 +28,30 @@ end ## 下载源码 -本教程根据你的**UI** 和 **Database**偏好有多个版,我们准备了两种可供下载的源码组合: +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: * [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) * [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +{{if UI == "MVC" && DB == "EF"}} + +### 视频教程 + +本章也被录制为视频教程 **发布在YouTube**. + +{{end}} + {{if UI == "MVC"}} ## 创建新书籍 -通过本节, 你将会了解如何创建一个 modal form 来实现新增书籍的功能. 最终成果如下图所示: +通过本节, 你将会了解如何创建一个 modal form 实现新增书籍的功能. model dialog将如下图所示: ![bookstore-create-dialog](./images/bookstore-create-dialog-2.png) @@ -66,7 +63,7 @@ end #### CreateModal.cshtml.cs -打开 `CreateModal.cshtml.cs` 代码文件,用如下代码替换 `CreateModalModel` 类的实现: +打开 `CreateModal.cshtml.cs` 代码文件(`CreateModalModel` 类),替换成以下代码: ````C# using System.Threading.Tasks; @@ -101,10 +98,10 @@ namespace Acme.BookStore.Web.Pages.Books } ```` -* 该类派生于 `BookStorePageModel` 而非默认的 `PageModel`. `BookStorePageModel` 继承了 `PageModel` 并且添加了一些可以被你的page model类使用的通用属性和方法. +* 该类派生于 `BookStorePageModel` 而非默认的 `PageModel`. `BookStorePageModel` 间接继承了 `PageModel` 并且添加了一些可以被你的page model类使用的通用属性和方法. * `Book` 属性上的 `[BindProperty]` 特性将post请求提交上来的数据绑定到该属性上. * 该类通过构造函数注入了 `IBookAppService` 应用服务,并且在 `OnPostAsync` 处理程序中调用了服务的 `CreateAsync` 方法. -* 它在 `OnGet` 方法中创建一个新的 `CreateUpdateBookDto` 对象。 ASP.NET Core不需要像这样创建一个新实例就可以正常工作. 但是它不会为你创建实例,并且如果你的类在类构造函数中具有一些默认值分配或代码执行,它们将无法工作. 对于这种情况,我们为某些 `CreateUpdateBookDto` 属性设置了默认值. +* 它在 `OnGet` 方法中创建一个新的 `CreateUpdateBookDto` 对象。 ASP.NET Core不需要像这样创建一个新实例就可以正常工作. 但是它不会为你创建实例,并且如果你的类在类构造函数中赋值一些默认值或执行一些代码,它们将无法工作. 对于这种情况,我们为某些 `CreateUpdateBookDto` 属性设置了默认值. #### CreateModal.cshtml @@ -134,10 +131,9 @@ namespace Acme.BookStore.Web.Pages.Books * 这个 modal 使用 `abp-dynamic-form` [tag Helper](../UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md) 根据 `CreateBookViewModel` 类自动构建了表单. * `abp-model` 指定了 `Book` 属性为模型对象. -* `data-ajaxForm` 设置了表单通过AJAX提交,而不是经典的页面回发. * `abp-form-content` tag helper 作为表单控件渲染位置的占位符 (这是可选的,只有你在 `abp-dynamic-form` 中像本示例这样添加了其他内容才需要). -> 提示: 就像在本示例中一样,`Layout` 应该为 `null`,因为当通过AJAX加载模态时,我们不希望包括所有布局. +> 提示: 就像在本示例中一样,`Layout` 应该为 `null`,因为当通过AJAX加载模态窗口时,我们不希望包括所有布局. ### 添加 "New book" 按钮 @@ -195,9 +191,9 @@ namespace Acme.BookStore.Web.Pages.Books 如下图所示,只是在表格 **右上方** 添加了 **New book** 按钮: -![bookstore-new-book-button](./images/bookstore-new-book-button-2.png) +![bookstore-new-book-button](images/bookstore-new-book-button-2.png) -打开 `Pages/book/index.js` 在 `datatable` 配置代码后面添加如下代码: +打开 `Pages/Book/Index.js` 在 `datatable` 配置代码后面添加如下代码: ````js var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); @@ -212,9 +208,9 @@ $('#NewBookButton').click(function (e) { }); ```` -* `abp.ModalManager` 是一个在客户端打开和管理modal的辅助类.它基于Twitter Bootstrap的标准modal组件通过简化的API抽象隐藏了许多细节. +* `abp.ModalManager` 是一个在客户端管理modal的辅助类.它内部使用了Twitter Bootstrap的标准modal组件,但通过简化的API抽象了许多细节. * `createModal.onResult(...)` 用于在创建书籍后刷新数据表格. -* `createModal.open();` 用于打开模态创建新书籍. +* `createModal.open();` 用于打开modal创建新书籍. `Index.js` 的内容最终如下所示: @@ -335,8 +331,8 @@ namespace Acme.BookStore.Web.Pages.Books } ```` -* `[HiddenInput]` 和 `[BindProperty]` 是标准的 ASP.NET Core MVC 特性.这里启用 `SupportsGet` 从Http请求的查询字符串中获取Id的值. -* 在 `OnGetAsync` 方法中,将 `BookAppService.GetAsync` 方法返回的 `BookDto` 映射成 `CreateUpdateBookDto` 并赋值给Book属性. +* `[HiddenInput]` 和 `[BindProperty]` 是标准的 ASP.NET Core MVC 特性.这里启用 `SupportsGet` 从Http请求的查询字符串参数中获取Id的值. +* 在 `OnGetAsync` 方法中, 我们从 `BookAppService` 获得 `BookDto` ,并将它映射成DTO对象 `CreateUpdateBookDto`. * `OnPostAsync` 方法直接使用 `BookAppService.UpdateAsync` 来更新实体. ### BookDto 到 CreateUpdateBookDto 对象映射 @@ -391,10 +387,10 @@ namespace Acme.BookStore.Web 这个页面内容和 `CreateModal.cshtml` 非常相似,除了以下几点: -* 它包含`id`属性的`abp-input`, 用于存储编辑书的 `id` (它是隐藏的Input) +* 它包含`id`属性的`abp-input`, 用于存储被编辑书籍的 `id` (它是隐藏的Input) * 此页面指定的post地址是`Books/EditModal`. -### 为表格添加 "操作(Actions)" 下拉菜单 +### 为表格添加 "操作(Actions)" 下拉菜单 我们将为表格每行添加下拉按钮 ("Actions"): @@ -516,9 +512,9 @@ $(function () { } ```` -* `confirmMessage` 用来在实际执行 `action` 之前向用户进行确认. -* 通过javascript代理方法 `acme.bookStore.books.book.delete(...)` 执行一个AJAX请求来删除一个book实体. -* `abp.notify.info` 用来在执行删除操作后显示一个toastr通知信息. +* `confirmMessage` 执行 `action` 前向用户进行确认. +* `acme.bookStore.books.book.delete(...)` 执行一个AJAX请求删除一个book. +* `abp.notify.info` 执行删除操作后显示一个通知信息. 由于我们使用了两个新的本地化文本(`BookDeletionConfirmationMessage`和`SuccesslyDeleted`),因此你需要将它们添加到本地化文件(`Acme.BookStore.Domain.Shared`项目的`Localization/BookStore`文件夹下的`en.json`): @@ -640,7 +636,7 @@ $(function () { ## 创建新书籍 -下面的章节中,你将学习到如何创建一个新的模态对话框来新增书籍. +下面的章节中,你将学习到如何创建一个新的模态窗口新增书籍. ### BookComponent @@ -649,8 +645,7 @@ $(function () { ```js import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { BookDto } from './models'; -import { BookService } from './services'; +import { BookService, BookDto } from '@proxy/books'; @Component({ selector: 'app-book', @@ -666,7 +661,7 @@ export class BookComponent implements OnInit { constructor(public readonly list: ListService, private bookService: BookService) {} ngOnInit() { - const bookStreamCreator = (query) => this.bookService.getListByInput(query); + const bookStreamCreator = (query) => this.bookService.getList(query); this.list.hookToQuery(bookStreamCreator).subscribe((response) => { this.book = response; @@ -680,7 +675,8 @@ export class BookComponent implements OnInit { } ``` -* 我们定义了一个名为 `isModalOpen` 的变量和 `createBook` 方法. +* 我们定义了一个名为 `isModalOpen` 的属性和 `createBook` 方法. + 打开 `/src/app/book/book.component.html` 做以下更改: @@ -690,9 +686,9 @@ export class BookComponent implements OnInit {
    {%{{{ '::Menu:Books' | abpLocalization }}}%}
    -
    +
    - +
    - +
  • @@ -726,11 +722,11 @@ export class BookComponent implements OnInit { ``` * 添加了 `New book` 按钮到卡片头部. -* 添加了 `abp-modal` 渲染模态框,允许用户创建新书. `abp-modal` 是显示模态框的预构建组件. 你也可以使用其它方法显示模态框,但 `abp-modal` 提供了一些附加的好处. +* 添加了 `abp-modal` 渲染模态框,允许用户创建新书. `abp-modal` 是显示模态框的预构建组件. 你也可以使用其它方法显示模态框,但 `abp-modal` 提供了一些额外的好处. 你可以打开浏览器,点击**New book**按钮看到模态框. -![Empty modal for new book](./images/bookstore-empty-new-book-modal.png) +![Empty modal for new book](images/bookstore-empty-new-book-modal.png) ### 添加响应式表单 @@ -741,8 +737,7 @@ export class BookComponent implements OnInit { ```js import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { BookDto, BookType } from './models'; // add BookType -import { BookService } from './services'; +import { BookService, BookDto, bookTypeOptions } from '@proxy/books'; // add bookTypeOptions import { FormGroup, FormBuilder, Validators } from '@angular/forms'; // add this @Component({ @@ -756,12 +751,8 @@ export class BookComponent implements OnInit { form: FormGroup; // add this line - bookType = BookType; // add this line - // add bookTypes as a list of BookType enum members - bookTypes = Object.keys(this.bookType).filter( - (key) => typeof this.bookType[key] === 'number' - ); + bookTypes = bookTypeOptions; isModalOpen = false; @@ -772,7 +763,7 @@ export class BookComponent implements OnInit { ) {} ngOnInit() { - const bookStreamCreator = (query) => this.bookService.getListByInput(query); + const bookStreamCreator = (query) => this.bookService.getList(query); this.list.hookToQuery(bookStreamCreator).subscribe((response) => { this.book = response; @@ -800,7 +791,7 @@ export class BookComponent implements OnInit { return; } - this.bookService.createByInput(this.form.value).subscribe(() => { + this.bookService.create(this.form.value).subscribe(() => { this.isModalOpen = false; this.form.reset(); this.list.get(); @@ -809,12 +800,11 @@ export class BookComponent implements OnInit { } ``` -* 导入了 `FormGroup, FormBuilder and Validators`. +* 从` @angular/forms `导入了 `FormGroup, FormBuilder and Validators`. * 添加了 `form: FormGroup` 变量. -* 添加了 `bookType` 属性,你可以从模板中获取 `BookType` 枚举成员. * 添加了 `bookTypes` 属性作为 `BookType` 枚举成员列表. 将在表单选项中使用. -* 我们注入了 `fb: FormBuilder` 服务到构造函数. [FormBuilder](https://angular.io/api/forms/FormBuilder) 服务为生成控件提供了方便的方法. 它减少了构建复杂表单所需的样板文件的数量. -* 我们添加了 `buildForm` 方法到文件末尾, 在 `createBook` 方法调用 `buildForm()` 方法. 该方法创建一个响应式表单去创建新书. +* 我们注入了 `FormBuilder` 到构造函数. [FormBuilder](https://angular.io/api/forms/FormBuilder) 提供了简便的方法生成表单控件. 它减少了构建复杂表单所需的样板文件的数量. +* 我们添加了 `buildForm` 方法到文件末尾, 在 `createBook` 方法调用 `buildForm()` 方法. * 添加了`save` 方法. 打开 `/src/app/book/book.component.html`,使用以下内容替换 ` `: @@ -836,7 +826,7 @@ export class BookComponent implements OnInit { * @@ -897,13 +887,12 @@ export class BookModule { } * 我们导入了 `NgbDatepickerModule` 来使用日期选择器. -打开 `/src/app/book/book.component.ts` 使用以内内容替换: +打开 `/src/app/book/book.component.ts` 使用以下内容替换: ```js import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { BookDto, BookType } from './models'; -import { BookService } from './services'; +import { BookService, BookDto, bookTypeOptions } from '@proxy/books'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; // added this line @@ -923,11 +912,7 @@ export class BookComponent implements OnInit { form: FormGroup; - bookType = BookType; - - bookTypes = Object.keys(this.bookType).filter( - (key) => typeof this.bookType[key] === 'number' - ); + bookTypes = bookTypeOptions; isModalOpen = false; @@ -938,7 +923,7 @@ export class BookComponent implements OnInit { ) {} ngOnInit() { - const bookStreamCreator = (query) => this.bookService.getListByInput(query); + const bookStreamCreator = (query) => this.bookService.getList(query); this.list.hookToQuery(bookStreamCreator).subscribe((response) => { this.book = response; @@ -964,7 +949,7 @@ export class BookComponent implements OnInit { return; } - this.bookService.createByInput(this.form.value).subscribe(() => { + this.bookService.create(this.form.value).subscribe(() => { this.isModalOpen = false; this.form.reset(); this.list.get(); @@ -974,11 +959,11 @@ export class BookComponent implements OnInit { ``` * 导入了 `NgbDateNativeAdapter` 和 `NgbDateAdapter`. -* 我们添加了一个新的 `NgbDateAdapter` 提供程序,它将Datepicker值转换为 `Date` 类型. 有关更多详细信息,请参见[datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview). +* 我们添加了一个新的 `NgbDateAdapter` 提供程序,它将Datepicker值转换为 `Date` 类型. 更多详细信息,请参见[datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview). 现在你可以打开浏览器看到以下变化: -![Save button to the modal](./images/bookstore-new-book-form-v2.png) +![Save button to the modal](images/bookstore-new-book-form-v2.png) ## 更新书籍 @@ -987,8 +972,7 @@ export class BookComponent implements OnInit { ```js import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { BookDto, BookType, CreateUpdateBookDto } from './models'; -import { BookService } from './services'; +import { BookService, BookDto, bookTypeOptions } from '@proxy/books'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; @@ -1001,15 +985,11 @@ import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap export class BookComponent implements OnInit { book = { items: [], totalCount: 0 } as PagedResultDto; - selectedBook = new BookDto(); // declare selectedBook + selectedBook = {} as BookDto; // declare selectedBook form: FormGroup; - bookType = BookType; - - bookTypes = Object.keys(this.bookType).filter( - (key) => typeof this.bookType[key] === 'number' - ); + bookTypes = bookTypeOptions; isModalOpen = false; @@ -1020,7 +1000,7 @@ export class BookComponent implements OnInit { ) {} ngOnInit() { - const bookStreamCreator = (query) => this.bookService.getListByInput(query); + const bookStreamCreator = (query) => this.bookService.getList(query); this.list.hookToQuery(bookStreamCreator).subscribe((response) => { this.book = response; @@ -1028,14 +1008,14 @@ export class BookComponent implements OnInit { } createBook() { - this.selectedBook = new BookDto(); // reset the selected book + this.selectedBook = {} as BookDto; // reset the selected book this.buildForm(); this.isModalOpen = true; } // Add editBook method editBook(id: string) { - this.bookService.getById(id).subscribe((book) => { + this.bookService.get(id).subscribe((book) => { this.selectedBook = book; this.buildForm(); this.isModalOpen = true; @@ -1061,8 +1041,8 @@ export class BookComponent implements OnInit { } const request = this.selectedBook.id - ? this.bookService.updateByIdAndInput(this.form.value, this.selectedBook.id) - : this.bookService.createByInput(this.form.value); + ? this.bookService.update(this.selectedBook.id, this.form.value) + : this.bookService.create(this.form.value); request.subscribe(() => { this.isModalOpen = false; @@ -1074,10 +1054,10 @@ export class BookComponent implements OnInit { ``` * 我们声明了类型为 `BookDto` 的 `selectedBook` 变量. -* 我们添加了 `editBook` 方法, 根据给定图书 `Id` 设置 `selectedBook` 对象. +* 我们添加了 `editBook` 方法, 根据给定书籍 `Id` 设置 `selectedBook` 对象. * 我们替换了 `buildForm` 方法使用 `selectedBook` 数据创建表单. * 我们替换了 `createBook` 方法,设置 `selectedBook` 为空对象. -* 我们替换了 `save` 方法. +* 我们修改了 `save` 方法,同时处理新建和更新操作. ### 添加 "Actions" 下拉框到表格 @@ -1111,7 +1091,7 @@ export class BookComponent implements OnInit { 在表格的第一列添加了一个 "Actions" 下拉菜单,如下图所示: -![Action buttons](./images/bookstore-actions-buttons.png) +![Action buttons](images/bookstore-actions-buttons.png) 同时如下所示更改 `ng-template #abpHeader` 部分: @@ -1147,13 +1127,13 @@ constructor( delete(id: string) { this.confirmation.warn('::AreYouSureToDelete', '::AreYouSure').subscribe((status) => { if (status === Confirmation.Status.confirm) { - this.bookService.deleteById(id).subscribe(() => this.list.get()); + this.bookService.delete(id).subscribe(() => this.list.get()); } }); } ``` -* 我们注入了 `ConfirmationService`. +* 我们引入了 `ConfirmationService`. * 我们注入了 `ConfirmationService` 到构造函数. * 添加了 `delete` 方法. @@ -1161,6 +1141,7 @@ delete(id: string) { ### 添加删除按钮: + 打开 `/src/app/book/book.component.html` 修改 `ngbDropdownMenu` 添加删除按钮: ```html @@ -1174,14 +1155,445 @@ delete(id: string) { 最终操作下拉框UI看起来如下: -![bookstore-final-actions-dropdown](./images/bookstore-final-actions-dropdown.png) +![bookstore-final-actions-dropdown](images/bookstore-final-actions-dropdown.png) + +点击 `delete` 操作调用 `delete` 方法,然后显示一个确认弹层如下图所示. + +![bookstore-confirmation-popup](images/bookstore-confirmation-popup.png) + +{{end}} + +{{if UI == "Blazor" || UI == "BlazorServer"}} + +## 创建新书籍 + +通过本节, 你将会了解如何创建一个模态窗口实现新增书籍的功能. 因为我们已经从 `AbpCrudPageBase` 继承, 所以只需要开发视图部分. + +### 添加 "New Button" 按钮 + +打开 `Books.razor` 替换 `` 部分为以下代码: + +````xml + + + +

    @L["Books"]

    +
    + + + +
    +
    +```` + +如下图所示,卡片头 **右侧** 添加了 **New book** 按钮: + +![blazor-add-book-button](images/blazor-add-book-button.png) + +现在, 我们可以添加点击按钮后打开的模态窗口了. + +### 书籍创建模态窗口 + +打开 `Books.razor`, 添加以下代码到页面底部: + +````xml + + + +
    + + @L["NewBook"] + + + + + + + @L["Name"] + + + + + + + + + @L["Type"] + + + + @L["PublishDate"] + + + + @L["Price"] + + + + + + + + +
    +
    +
    +```` + +这段代码需要一个服务; 在文件顶部, `@inherits...` 行前, 注入 `AbpBlazorMessageLocalizerHelper`: + +````csharp +@inject AbpBlazorMessageLocalizerHelper LH +```` + +* 表单实现了验证功能, `AbpBlazorMessageLocalizerHelper` 用于本地化验证消息. +* `CreateModal` 对象, `CloseCreateModalAsync` 和 `CreateEntityAsync` 方法定义在基类中. 参阅 [Blazorise文档](https://blazorise.com/docs/) 以深入理解 `Modal` 和其它组件. + +这就是全部了. 运行应用程序, 尝试添加一本新书. + +![blazor-new-book-modal](images/blazor-new-book-modal.png) + +## 更新书籍 + +编辑书籍与新建书籍很类似. -点击 `delete` 动作调用 `delete` 方法,然后无法显示一个确认弹层如下图所示. +### 操作下拉菜单 -![bookstore-confirmation-popup](./images/bookstore-confirmation-popup.png) +打开 `Books.razor` , 在 `DataGridColumns` 中添加以下 `DataGridEntityActionsColumn` 作为第一项: + +````xml + + + + + + + +```` + +* `OpenEditModalAsync` 定义在基类中, 它接收实体(书籍)参数, 编辑这个实体. + +`DataGridEntityActionsColumn` 组件用于显示 `DataGrid` 每一行中的"操作" 下拉菜单. 如果其中只有唯一的操作, `DataGridEntityActionsColumn` 显示 **唯一按钮**, 而不是下拉菜单. + +![blazor-edit-book-action](images/blazor-edit-book-action-2.png) + +### 编辑模态窗口 + +我们现在可以定义一个模态窗口编辑书籍. 加入下面的代码到 `Books.razor` 页面的底部: + +````xml + + + +
    + + @EditingEntity.Name + + + + + + + @L["Name"] + + + + + + + + + @L["Type"] + + + + @L["PublishDate"] + + + + @L["Price"] + + + + + + + + +
    +
    +
    +```` + +### AutoMapper 配置 + +基类 `AbpCrudPageBase` 使用 [对象到对象映射](../Object-To-Object-Mapping.md) 系统将 `BookDto` 对象转化为`CreateUpdateBookDto` 对象. 因此, 我们需要定义映射. + +打开 `Acme.BookStore.Blazor` 项目中的 `BookStoreBlazorAutoMapperProfile `, 替换成以下内容: + +````csharp +using Acme.BookStore.Books; +using AutoMapper; + +namespace Acme.BookStore.Blazor +{ + public class BookStoreBlazorAutoMapperProfile : Profile + { + public BookStoreBlazorAutoMapperProfile() + { + CreateMap(); + } + } +} +```` + +* `CreateMap();` 行用于定义映射. + +### 测试编辑模态窗口 + +你可以运行程序并尝试编辑一本书. + +![blazor-edit-book-modal](images/blazor-edit-book-modal.png) + +> 提示: 尝试保留 *Name* 字段为空并提交表单, 将显示验证错误消息. + +## 删除书籍 + +打开 `Books.razor` 页面, 在 `EntityActions` 中的"编辑" 操作下面加入以下的 `EntityAction`: + +````xml + +```` + +* `DeleteEntityAsync` 定义在基类中. 通过向服务器发起请求删除实体. +* `ConfirmationMessage` 执行操作前显示确认消息的回调函数. +* `GetDeleteConfirmationMessage` 定义在基类中. 你可以覆写这个方法 (或传递其它值给 `ConfirmationMessage` 参数) 以定制本地化消息. + +因为"操作" 按钮现在有了两个操作, 变成了下拉菜单: + +![blazor-edit-book-action](images/blazor-delete-book-action.png) + +运行程序并尝试删除一本书. + +## 完整的 CRUD UI 代码 + +下面是完整的创建图书管理CRUD页面的代码, 这些代码在上面是分成两部分开发的: + +````xml +@page "/books" +@using Volo.Abp.Application.Dtos +@using Acme.BookStore.Books +@using Acme.BookStore.Localization +@using Microsoft.Extensions.Localization +@using Volo.Abp.AspNetCore.Components.Web +@inject IStringLocalizer L +@inject AbpBlazorMessageLocalizerHelper LH +@inherits AbpCrudPageBase + + + + + +

    @L["Books"]

    +
    + + + +
    +
    + + + + + + + + + + + + + + + @L[$"Enum:BookType:{(int) context.Type}"] + + + + + @context.PublishDate.ToShortDateString() + + + + + + + @context.CreationTime.ToLongDateString() + + + + + +
    + + + + +
    + + @L["NewBook"] + + + + + + + @L["Name"] + + + + + + + + + @L["Type"] + + + + @L["PublishDate"] + + + + @L["Price"] + + + + + + + + +
    +
    +
    + + + + +
    + + @EditingEntity.Name + + + + + + + @L["Name"] + + + + + + + + + @L["Type"] + + + + @L["PublishDate"] + + + + @L["Price"] + + + + + + + + +
    +
    +
    +```` {{end}} ## 下一章 -查看本教程的[下一章](Part-4.md). +查看本教程的[下一章](Part-4.md). \ No newline at end of file diff --git a/docs/zh-Hans/Tutorials/Part-4.md b/docs/zh-Hans/Tutorials/Part-4.md index 151c7aab90..e570880a22 100644 --- a/docs/zh-Hans/Tutorials/Part-4.md +++ b/docs/zh-Hans/Tutorials/Part-4.md @@ -2,27 +2,10 @@ ````json //[doc-params] { - "UI": ["MVC","NG"], + "UI": ["MVC","Blazor","BlazorServer","NG"], "DB": ["EF","Mongo"] } ```` -{{ -if UI == "MVC" - UI_Text="mvc" -else if UI == "NG" - UI_Text="angular" -else - UI_Text="?" -end -if DB == "EF" - DB_Text="Entity Framework Core" -else if DB == "Mongo" - DB_Text="MongoDB" -else - DB_Text="?" -end -}} - ## 关于本教程 在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: @@ -45,16 +28,32 @@ end ## 下载源码 -本教程根据你的**UI** 和 **Database**偏好有多个版,我们准备了两种可供下载的源码组合: +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: * [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) * [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +{{if UI == "MVC" && DB == "EF"}} + +### 视频教程 + +本章也被录制为视频教程 **发布在YouTube**. + +{{end}} + ## 解决方案中的测试项目 这一部分涵盖了 **服务器端** 测试. 解决方案中有多个测试项目: -![bookstore-test-projects-v2](./images/bookstore-test-projects-{{UI_Text}}.png) +![bookstore-test-projects-v2](./images/bookstore-test-projects-mvc.png) + +> 根据你选择的UI和数据库, 测试项目略微有所不同. 例如, 如果选择MongoDB, 那么 `Acme.BookStore.EntityFrameworkCore.Tests` 会变为 `Acme.BookStore.MongoDB.Tests`. 每个项目用于测试相关的应用程序项目.测试项目使用以下库进行测试: @@ -64,11 +63,11 @@ end {{if DB=="EF"}} -> 测试项目配置为使用 **SQLite内存** 作为数据库. 创建一个单独的数据库实例并使用数据种子系统进行初始化种子数据,为每个测试准备一个新的数据库. +> 测试项目配置为使用 **SQLite内存** 作为数据库. 创建一个单独的数据库实例并使用[数据种子系统](../Data-Seeding.md)初始化种子数据,为每个测试准备一个新的数据库. {{else if DB=="Mongo"}} -> **[Mongo2Go](https://github.com/Mongo2Go/Mongo2Go)**库用于模拟MongoDB数据库. 创建一个单独的数据库实例并使用数据种子系统进行初始化种子数据,为每个测试准备一个新的数据库. +> **[Mongo2Go](https://github.com/Mongo2Go/Mongo2Go)**库用于模拟MongoDB数据库. 创建一个单独的数据库实例并使用[数据种子系统](../Data-Seeding.md)初始化种子数据,为每个测试准备一个新的数据库. {{end}} @@ -78,12 +77,15 @@ end ## 测试 BookAppService -在 `Acme.BookStore.Application.Tests` 项目中创建一个名叫 `BookAppService_Tests` 的测试类: +在 `Acme.BookStore.Application.Tests` 项目的 `Books` 命名空间(文件夹)中创建一个名叫 `BookAppService_Tests` 的测试类: ````csharp +using System; +using System.Linq; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Application.Dtos; +using Volo.Abp.Validation; using Xunit; namespace Acme.BookStore.Books @@ -129,7 +131,7 @@ public async Task Should_Create_A_Valid_Book() { Name = "New test book 42", Price = 10, - PublishDate = System.DateTime.Now, + PublishDate = DateTime.Now, Type = BookType.ScienceFiction } ); @@ -201,7 +203,7 @@ namespace Acme.BookStore.Books result.TotalCount.ShouldBeGreaterThan(0); result.Items.ShouldContain(b => b.Name == "1984"); } - + [Fact] public async Task Should_Create_A_Valid_Book() { @@ -211,7 +213,7 @@ namespace Acme.BookStore.Books { Name = "New test book 42", Price = 10, - PublishDate = System.DateTime.Now, + PublishDate = DateTime.Now, Type = BookType.ScienceFiction } ); @@ -220,7 +222,7 @@ namespace Acme.BookStore.Books result.Id.ShouldNotBe(Guid.Empty); result.Name.ShouldBe("New test book 42"); } - + [Fact] public async Task Should_Not_Create_A_Book_Without_Name() { @@ -244,7 +246,7 @@ namespace Acme.BookStore.Books } ```` -打开**测试资源管理器**(测试 -> Windows -> 测试资源管理器)并**执行**所有测试: +打开**测试资源管理器**(测试 -> Windows -> 测试资源管理器)并**执行所有**测试: ![bookstore-appservice-tests](./images/bookstore-appservice-tests.png) @@ -252,4 +254,4 @@ namespace Acme.BookStore.Books ## 下一章 -查看本教程的[下一章](Part-5.md). \ No newline at end of file +查看本教程的[下一章](Part-5.md). diff --git a/docs/zh-Hans/Tutorials/Part-5.md b/docs/zh-Hans/Tutorials/Part-5.md index 602306c484..be687a6cb9 100644 --- a/docs/zh-Hans/Tutorials/Part-5.md +++ b/docs/zh-Hans/Tutorials/Part-5.md @@ -1 +1,635 @@ -TODO.. \ No newline at end of file +# Web应用程序开发教程 - 第五章: 授权 +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer","NG"], + "DB": ["EF","Mongo"] +} +```` +## 关于本教程 + +在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: + +* **{{DB_Text}}** 做为ORM提供程序. +* **{{UI_Value}}** 做为UI框架. + +本教程分为以下部分: + +- [Part 1: 创建服务端](Part-1.md) +- [Part 2: 图书列表页面](Part-2.md) +- [Part 3: 创建,更新和删除图书](Part-2.md) +- [Part 4: 集成测试](Part-4.md) +- **Part 5: 授权**(本章) +- [Part 6: 作者: 领域层](Part-6.md) +- [Part 7: 作者: 数据库集成](Part-7.md) +- [Part 8: 作者: 应用服务层](Part-8.md) +- [Part 9: 作者: 用户页面](Part-9.md) +- [Part 10: 图书到作者的关系](Part-10.md) + +## 下载源码 + +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: + +* [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) +* [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +{{if UI == "MVC" && DB == "EF"}} + +### 视频教程 + +本章也被录制为视频教程 **发布在YouTube**. + +{{end}} + +## 权限 + +ABP框架提供了一个基于ASP.NET Core[授权基础架构](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction)的[授权系统](../Authorization.md). 基于标准授权基础架构的一个主要功能是添加了 **权限系统**, 这个系统允许定义权限并且根据角色, 用户或客户端启用/禁用权限. + +### 权限名称 + +权限必须有唯一的名称 (一个 `字符串`). 最好的方法是把它定义为一个 `常量`, 这样我们就可以重用这个权限名称了. + +打开 `Acme.BookStore.Application.Contracts` 项目中的 `BookStorePermissions` 类 (位于 `Permissions` 文件夹) 并替换为以下代码: + +````csharp +namespace Acme.BookStore.Permissions +{ + public static class BookStorePermissions + { + public const string GroupName = "BookStore"; + + public static class Books + { + public const string Default = GroupName + ".Books"; + public const string Create = Default + ".Create"; + public const string Edit = Default + ".Edit"; + public const string Delete = Default + ".Delete"; + } + } +} +```` + +权限名称具有层次结构. 例如, "创建图书" 权限被定义为 `BookStore.Books.Create`. ABP不强制必须如此, 但这是一种有益的做法. + +### 权限定义 + +在使用权限前必须定义它们. + +打开 `Acme.BookStore.Application.Contracts` 项目中的 `BookStorePermissionDefinitionProvider` 类 (位于 `Permissions` 文件夹) 并替换为以下代码: + +````csharp +using Acme.BookStore.Localization; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; + +namespace Acme.BookStore.Permissions +{ + public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider + { + public override void Define(IPermissionDefinitionContext context) + { + var bookStoreGroup = context.AddGroup(BookStorePermissions.GroupName, L("Permission:BookStore")); + + var booksPermission = bookStoreGroup.AddPermission(BookStorePermissions.Books.Default, L("Permission:Books")); + booksPermission.AddChild(BookStorePermissions.Books.Create, L("Permission:Books.Create")); + booksPermission.AddChild(BookStorePermissions.Books.Edit, L("Permission:Books.Edit")); + booksPermission.AddChild(BookStorePermissions.Books.Delete, L("Permission:Books.Delete")); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } + } +} +```` + +这个类定义了一个 **权限组** (在UI上分组权限, 下文会看到) 和 权限组中的**4个权限**. 而且, **创建**, **编辑** 和 **删除** 是 `BookStorePermissions.Books.Default` 权限的子权限. **仅当父权限被选择**时, 子权限才能被选择. + +最后, 编辑本地化文件 (`Acme.BookStore.Domain.Shared` 项目的 `Localization/BookStore` 文件夹中的 `en.json`) 定义上面使用的本地化键: + +````json +"Permission:BookStore": "Book Store", +"Permission:Books": "Book Management", +"Permission:Books.Create": "Creating new books", +"Permission:Books.Edit": "Editing the books", +"Permission:Books.Delete": "Deleting the books" +```` + +> 本地化键名可以是任意的, 并没有强制的规则. 但我们推荐上面使用的约定. + +### 权限管理界面 + +完成权限定义后, 可以在**权限管理模态窗口**看到它们. + +在*管理 -> Identity -> 角色* 页面, 选择admin角色的 *权限* 操作, 打开权限管理模态窗口: + +![bookstore-permissions-ui](images/bookstore-permissions-ui.png) + +授予你希望的权限并保存. + +> **提示**: 如果运行 `Acme.BookStore.DbMigrator` 应用程序, 新权限会被自动授予admin. + +## 授权 + +现在, 你可以使用权限授权图书管理. + +### 应用层 和 HTTP API + +打开 the `BookAppService` 类, 设置策略名称为上面定义的权限名称. + +````csharp +using System; +using Acme.BookStore.Permissions; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore.Books +{ + public class BookAppService : + CrudAppService< + Book, //The Book entity + BookDto, //Used to show books + Guid, //Primary key of the book entity + PagedAndSortedResultRequestDto, //Used for paging/sorting + CreateUpdateBookDto>, //Used to create/update a book + IBookAppService //implement the IBookAppService + { + public BookAppService(IRepository repository) + : base(repository) + { + GetPolicyName = BookStorePermissions.Books.Default; + GetListPolicyName = BookStorePermissions.Books.Default; + CreatePolicyName = BookStorePermissions.Books.Create; + UpdatePolicyName = BookStorePermissions.Books.Edit; + DeletePolicyName = BookStorePermissions.Books.Delete; + } + } +} +```` + +加入代码到构造器. 基类中的 `CrudAppService` 自动在CRUD操作中使用这些权限. 这不仅实现了 **应用服务** 的安全性, 也实现了 **HTTP API** 安全性, 因为如前解释的, HTTP API 自动使用这些服务. (参阅 [自动 API controllers](../API/Auto-API-Controllers.md)). + +> 在稍后开发作者管理功能时, 你将会看到声明式授权, 使用 `[Authorize(...)]` 特性. + +{{if UI == "MVC"}} + +### Razor 页面 + +虽然安全的 HTTP API和应用服务阻止未授权用户使用服务, 但他们依然可以导航到图书管理页面. 虽然当页面发起第一个访问服务器的AJAX请求时会收到授权异常, 但为了更好的用户体验和安全性, 我们应该对页面进行授权. + +打开 `BookStoreWebModule` 在 `ConfigureServices` 方法中加入以下代码: + +````csharp +Configure(options => +{ + options.Conventions.AuthorizePage("/Books/Index", BookStorePermissions.Books.Default); + options.Conventions.AuthorizePage("/Books/CreateModal", BookStorePermissions.Books.Create); + options.Conventions.AuthorizePage("/Books/EditModal", BookStorePermissions.Books.Edit); +}); +```` + +现在未授权用户会被重定向至**登录页面**. + +#### 隐藏新建图书按钮 + +图书管理页面有一个 *新建图书* 按钮, 当用户没有 *图书新建* 权限时就不可见的. + +![bookstore-new-book-button-small](images/bookstore-new-book-button-small.png) + +打开 the `Pages/Books/Index.cshtml` 文件, 替换内容为以下代码: + +````html +@page +@using Acme.BookStore.Localization +@using Acme.BookStore.Permissions +@using Acme.BookStore.Web.Pages.Books +@using Microsoft.AspNetCore.Authorization +@using Microsoft.Extensions.Localization +@model IndexModel +@inject IStringLocalizer L +@inject IAuthorizationService AuthorizationService +@section scripts +{ + +} + + + + + + @L["Books"] + + + @if (await AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Create)) + { + + } + + + + + + + +```` + +* 加入 `@inject IAuthorizationService AuthorizationService` 以访问授权服务. +* 使用 `@if (await AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Create))` 检查图书创建权限, 条件显示 *新建图书* 按钮. + +### JavaScript端 + +图书管理页面中的图书表格每行都有操作按钮. 操作按钮包括 *编辑* 和 *删除* 操作: + +![bookstore-edit-delete-actions](images/bookstore-edit-delete-actions.png) + +如果用户没有权限, 应该隐藏相关的操作. 表格行中的操作有一个 `visible` 属性, 可以设置为 `false` 隐藏操作项. + +打开 `Acme.BookStore.Web` 项目中的 `Pages/Books/Index.js`, 为 `编辑` 操作加入 `visible` 属性: + +````js +{ + text: l('Edit'), + visible: abp.auth.isGranted('BookStore.Books.Edit'), //CHECK for the PERMISSION + action: function (data) { + editModal.open({ id: data.record.id }); + } +} +```` + +对 `Delete` 操作进行同样的操作: + +````js +visible: abp.auth.isGranted('BookStore.Books.Delete') +```` + +* `abp.auth.isGranted(...)` 检查前面定义的权限. +* `visible` 也可以是一个返回 `bool` 值的函数. 这个函数可以稍后根据某些条件计算. + +### 菜单项 + +即使我们在图书管理页面的所有层都控制了权限, 应用程序的主菜单依然会显示. 我们应该隐藏用户没有权限的菜单项. + +打开 `BookStoreMenuContributor` 类, 找到下面的代码: + +````csharp +context.Menu.AddItem( + new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" + ).AddItem( + new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/Books" + ) + ) +); +```` + +替换为以下代码: + +````csharp +var bookStoreMenu = new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" +); + +context.Menu.AddItem(bookStoreMenu); + +//CHECK the PERMISSION +if (await context.IsGrantedAsync(BookStorePermissions.Books.Default)) +{ + bookStoreMenu.AddItem(new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/Books" + )); +} +```` + +你需要为 `ConfigureMenuAsync` 方法加入 `async` 关键字, 并重新组织返回值. 最终的 `BookStoreMenuContributor` 类应该如下: + +````csharp +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using Acme.BookStore.Localization; +using Acme.BookStore.MultiTenancy; +using Acme.BookStore.Permissions; +using Volo.Abp.TenantManagement.Web.Navigation; +using Volo.Abp.UI.Navigation; + +namespace Acme.BookStore.Web.Menus +{ + public class BookStoreMenuContributor : IMenuContributor + { + public async Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name == StandardMenus.Main) + { + await ConfigureMainMenuAsync(context); + } + } + + private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + { + if (!MultiTenancyConsts.IsEnabled) + { + var administration = context.Menu.GetAdministration(); + administration.TryRemoveMenuItem(TenantManagementMenuNames.GroupName); + } + + var l = context.GetLocalizer(); + + context.Menu.Items.Insert(0, new ApplicationMenuItem("BookStore.Home", l["Menu:Home"], "~/")); + + var bookStoreMenu = new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" + ); + + context.Menu.AddItem(bookStoreMenu); + + //CHECK the PERMISSION + if (await context.IsGrantedAsync(BookStorePermissions.Books.Default)) + { + bookStoreMenu.AddItem(new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/Books" + )); + } + } + } +} +```` + +{{else if UI == "NG"}} + +### Angular Guard 配置 + +UI的第一步是防止未认证用户看见"图书"菜单项并进入图书管理页面. + +打开 `/src/app/book/book-routing.module.ts` 替换为以下代码: + +````js +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { AuthGuard, PermissionGuard } from '@abp/ng.core'; +import { BookComponent } from './book.component'; + +const routes: Routes = [ + { path: '', component: BookComponent, canActivate: [AuthGuard, PermissionGuard] }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class BookRoutingModule {} +```` + +* 从 `@abp/ng.core` 引入 `AuthGuard` 和 `PermissionGuard`. +* 在路由定义中添加 `canActivate: [AuthGuard, PermissionGuard]`. + +打开 `/src/app/route.provider.ts`, 在 `/books` 路由中添加 `requiredPolicy: 'BookStore.Books'`. `/books` 路由应该如以下配置: + +````js +{ + path: '/books', + name: '::Menu:Books', + parentName: '::Menu:BookStore', + layout: eLayoutType.application, + requiredPolicy: 'BookStore.Books', +} +```` + +### 隐藏新建图书按钮 + +当用户没有 *图书新建* 权限时, 图书管理页面上的 *新建图书* 按钮应该不可见. + +![bookstore-new-book-button-small](images/bookstore-new-book-button-small.png) + +打开 `/src/app/book/book.component.html` 文件, 替换创建按钮的HTML内容如下: + +````html + + +```` + +* 加入 `*abpPermission="'BookStore.Books.Create'"`, 当用户没有权限时隐藏按钮. + +### 隐藏编辑和删除操作 + +图书管理页面中的图书表格每行都有操作按钮. 操作按钮包括 *编辑* 和 *删除* 操作: + +![bookstore-edit-delete-actions](images/bookstore-edit-delete-actions.png) + +如果用户没有权限, 应该隐藏相关的操作. + +打开 `/src/app/book/book.component.html` 文件, 替换编辑和删除按钮的内容如下: + +````html + + + + + +```` + +* 加入 `*abpPermission="'BookStore.Books.Create'"`, 当用户没有编辑权限时隐藏按钮. +* 加入 `*abpPermission="'BookStore.Books.Delete'"`, 当用户没有删除权限时隐藏按钮. + +{{else if UI == "Blazor"}} + +### Razor验证组件 + +打开 `Acme.BookStore.Blazor` 项目中的 `/Pages/Books.razor` 文件, 在` @page` 指令和命名空间引入(`@using` 行)后添加 `Authorize` 特性, 如下所示: + +````html +@page "/books" +@attribute [Authorize(BookStorePermissions.Books.Default)] +@using Acme.BookStore.Permissions +@using Microsoft.AspNetCore.Authorization +... +```` + +添加这个特性阻止未登录用户或未授权用户访问这个页面. 用户重试后, 会被重定向到登录页面. + +### 显示/隐藏操作 + +图书管理页面上的每一种图书都有 *新建* 按钮和 *编辑*, *删除* 操作. 如果用户没有相关权限, 这些按钮/操作应该被隐藏. + +基类 `AbpCrudPageBase` 已经具有这些操作需要的功能. + +#### 设置策略 (权限) 名称 + +加入以下代码到 `Books.razor` 文件结尾: + +````csharp +@code +{ + public Books() // Constructor + { + CreatePolicyName = BookStorePermissions.Books.Create; + UpdatePolicyName = BookStorePermissions.Books.Edit; + DeletePolicyName = BookStorePermissions.Books.Delete; + } +} +```` + +基类 `AbpCrudPageBase` 自动检查相关操作的权限. 如果需要手动检查, 它也定义了相应的属性. + +* `HasCreatePermission`: True, 如果用户具有新建实体的权限. +* `HasUpdatePermission`: True, 如果用户具有编辑/更新实体的权限. +* `HasDeletePermission`: True, 如果用户具有删除实体的权限. + +> **Blazor 提示**: 当添加少量代码到 `@code` 是没有问题的. 当添加的代码变长时, 建议使用代码后置方法以便于维护. 我们将在作者部分使用这个方法. + +#### 隐藏新建图书按钮 + +检查 *新建图书* 按钮权限: + +````xml +@if (HasCreatePermission) +{ + +} +```` + +#### 隐藏编辑/删除操作 + +`EntityAction` 组件定义了 `Visible` 属性 (参数) 以条件显示操作. + +更新 `EntityActions` 部分: + +````xml + + + + +```` + +#### 关于权限缓存 + +你可以运行和测试权限. 从admin角色中移除一个图书相关权限, 观察到相关按钮/操作从UI上消失. + +在客户端, **ABP框架缓存当前用户的权限** . 所以, 当你修改了你的权限, 你需要手工 **刷新页面**. 如果不刷新并试图使用被禁的操作, 你会从服务器收到一个HTTP 403 (forbidden) 响应. + +> 修改角色或用户的权限在服务端立即生效. 所以, 缓存系统不会导致安全问题. + +### 菜单项 + +即使我们在图书管理页面的所有层都控制了权限, 应用程序的主菜单依然会显示. 我们应该隐藏用户没有权限的菜单项. + +打开 `Acme.BookStore.Blazor` 项目中的 `BookStoreMenuContributor` 类, 找到以下代码: + +````csharp +context.Menu.AddItem( + new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" + ).AddItem( + new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/books" + ) + ) +); +```` + +替换为以下代码: + +````csharp +var bookStoreMenu = new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" +); + +context.Menu.AddItem(bookStoreMenu); + +//CHECK the PERMISSION +if (await context.IsGrantedAsync(BookStorePermissions.Books.Default)) +{ + bookStoreMenu.AddItem(new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/books" + )); +} +```` + +你需要为 `ConfigureMenuAsync` 方法加入 `async` 关键字并重新整理返回值. 最终的 `ConfigureMainMenuAsync` 方法如下: + +````csharp +private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) +{ + var l = context.GetLocalizer(); + + context.Menu.Items.Insert( + 0, + new ApplicationMenuItem( + "BookStore.Home", + l["Menu:Home"], + "/", + icon: "fas fa-home" + ) + ); + + var bookStoreMenu = new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" + ); + + context.Menu.AddItem(bookStoreMenu); + + //CHECK the PERMISSION + if (await context.IsGrantedAsync(BookStorePermissions.Books.Default)) + { + bookStoreMenu.AddItem(new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/books" + )); + } +} +```` + +{{end}} + +## 下一章 + +查看本教程的[下一章](Part-6.md). diff --git a/docs/zh-Hans/Tutorials/Part-6.md b/docs/zh-Hans/Tutorials/Part-6.md index 602306c484..6030c8f3b9 100644 --- a/docs/zh-Hans/Tutorials/Part-6.md +++ b/docs/zh-Hans/Tutorials/Part-6.md @@ -1 +1,278 @@ -TODO.. \ No newline at end of file +# Web应用程序开发教程 - 第六章: 作者: 领域层 +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer","NG"], + "DB": ["EF","Mongo"] +} +```` +## 关于本教程 + +在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: + +* **{{DB_Text}}** 做为ORM提供程序. +* **{{UI_Value}}** 做为UI框架. + +本教程分为以下部分: + +- [Part 1: 创建服务端](Part-1.md) +- [Part 2: 图书列表页面](Part-2.md) +- [Part 3: 创建,更新和删除图书](Part-2.md) +- [Part 4: 集成测试](Part-4.md) +- [Part 5: 授权](Part-5.md) +- **Part 6: 作者: 领域层**(本章) +- [Part 7: 作者: 数据库集成](Part-7.md) +- [Part 8: 作者: 应用服务层](Part-8.md) +- [Part 9: 作者: 用户页面](Part-9.md) +- [Part 10: 图书到作者的关系](Part-10.md) + +## 下载源码 + +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: + +* [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) +* [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +## 简介 + +在前面的章节中, 我们使用 ABP 框架轻松地构建了一些服务; + +* 使用 [CrudAppService](../Application-Services.md) 基类, 而不是为标准的增删改查操作手工开发应用服务. +* 使用 [generic repositories](../Repositories.md) 自动完成数据层功能. + +对于 "作者" 部分; + +* 我们将要展示在需要的情况下, 如何 **手工做一些事情**. +* 我们将要实现一些 **领域驱动设计 (DDD) 最佳实践**. + +> **开发将会逐层完成, 一次聚焦一层. 在真实项目中, 你会逐个功能(垂直)开发, 如同前面的教程. 通过这种方式, 你可以体验这两种方式** + +## 作者实体 + +在 `Acme.BookStore.Domain` 项目中创建 `Authors` 文件夹 (命名空间), 在其中加入 `Author` 类: + +````csharp +using System; +using JetBrains.Annotations; +using Volo.Abp; +using Volo.Abp.Domain.Entities.Auditing; + +namespace Acme.BookStore.Authors +{ + public class Author : FullAuditedAggregateRoot + { + public string Name { get; private set; } + public DateTime BirthDate { get; set; } + public string ShortBio { get; set; } + + private Author() + { + /* This constructor is for deserialization / ORM purpose */ + } + + internal Author( + Guid id, + [NotNull] string name, + DateTime birthDate, + [CanBeNull] string shortBio = null) + : base(id) + { + SetName(name); + BirthDate = birthDate; + ShortBio = shortBio; + } + + internal Author ChangeName([NotNull] string name) + { + SetName(name); + return this; + } + + private void SetName([NotNull] string name) + { + Name = Check.NotNullOrWhiteSpace( + name, + nameof(name), + maxLength: AuthorConsts.MaxNameLength + ); + } + } +} +```` + +* 由 `FullAuditedAggregateRoot` 继承使得实体支持[软删除](../Data-Filtering.md) (指实体被删除时, 它并没有从数据库中被删除, 而只是被标记删除), 实体也具有了 [审计](../Entities.md) 属性. +* `Name` 属性的 `private set` 限制从类的外部设置这个属性. 有两种方法设置名字 (两种都进行了验证): + * 当新建一个作者时, 通过构造器. + * 使用 `ChangeName` 方法更新名字. +* `构造器` 和 `ChangeName` 方法的访问级别是 `internal`, 强制这些方法只能在领域层由 `AuthorManager` 使用. 稍后将对此进行解释. +* `Check` 类是一个ABP框架工具类, 用于检查方法参数 (如果参数非法会抛出 `ArgumentException`). + +`AuthorConsts` 是一个简单的类, 它位于 `Acme.BookStore.Domain.Shared` 项目的 `Authors` 命名空间 (文件夹)中: + +````csharp +namespace Acme.BookStore.Authors +{ + public static class AuthorConsts + { + public const int MaxNameLength = 64; + } +} +```` + +在 `Acme.BookStore.Domain.Shared` 项目中创建这个类, 因为[数据传输类](../Data-Transfer-Objects.md) (DTOs) 稍后会再一次用到它. + +## AuthorManager: 领域服务 + +`Author` 构造器和 `ChangeName` 方法的访问级别是 `internal`, 所以它们只能在领域层使用. 在 `Acme.BookStore.Domain` 项目中的 `Authors` 文件夹 (命名空间)创建 `AuthorManager` 类: + +````csharp +using System; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp; +using Volo.Abp.Domain.Services; + +namespace Acme.BookStore.Authors +{ + public class AuthorManager : DomainService + { + private readonly IAuthorRepository _authorRepository; + + public AuthorManager(IAuthorRepository authorRepository) + { + _authorRepository = authorRepository; + } + + public async Task CreateAsync( + [NotNull] string name, + DateTime birthDate, + [CanBeNull] string shortBio = null) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + + var existingAuthor = await _authorRepository.FindByNameAsync(name); + if (existingAuthor != null) + { + throw new AuthorAlreadyExistsException(name); + } + + return new Author( + GuidGenerator.Create(), + name, + birthDate, + shortBio + ); + } + + public async Task ChangeNameAsync( + [NotNull] Author author, + [NotNull] string newName) + { + Check.NotNull(author, nameof(author)); + Check.NotNullOrWhiteSpace(newName, nameof(newName)); + + var existingAuthor = await _authorRepository.FindByNameAsync(newName); + if (existingAuthor != null && existingAuthor.Id != author.Id) + { + throw new AuthorAlreadyExistsException(newName); + } + + author.ChangeName(newName); + } + } +} +```` + +* `AuthorManager` 强制使用一种可控的方式创建作者和修改作者的名字. 应用层 (后面会介绍) 将会使用这些方法. + +> **DDD 提示**: 如非必须并且用于执行核心业务规则, 不要引入领域服务方法. 对于这个场景, 我们使用这个服务保证名字的唯一性. + +两个方法都检查是否存在同名用户, 如果存在, 抛出业务异常 `AuthorAlreadyExistsException`, 这个异常定义在 `Acme.BookStore.Domain` 项目 (`Authors` 文件夹中): + +````csharp +using Volo.Abp; + +namespace Acme.BookStore.Authors +{ + public class AuthorAlreadyExistsException : BusinessException + { + public AuthorAlreadyExistsException(string name) + : base(BookStoreDomainErrorCodes.AuthorAlreadyExists) + { + WithData("name", name); + } + } +} +```` + +`BusinessException` 是一个特殊的异常类型. 在需要时抛出领域相关异常是一个好的实践. ABP框架会自动处理它, 并且它也容易本地化. `WithData(...)` 方法提供额外的数据给异常对象, 这些数据将会在本地化中或出于其它一些目的被使用. + +打开 `Acme.BookStore.Domain.Shared` 项目中的 `BookStoreDomainErrorCodes` 并修改为: + +````csharp +namespace Acme.BookStore +{ + public static class BookStoreDomainErrorCodes + { + public const string AuthorAlreadyExists = "BookStore:00001"; + } +} +```` + +这里定义了一个字符串, 表示应用程序抛出的错误码, 这个错误码可以被客户端应用程序处理. 为了用户, 你可能希望本地化它. 打开 `Acme.BookStore.Domain.Shared` 项目中的 `Localization/BookStore/en.json` , 加入以下项: + +````json +"BookStore:00001": "There is already an author with the same name: {name}" +```` + +当 `AuthorAlreadyExistsException` 被抛出, 终端用户将会在UI上看到组织好的错误消息. + +## IAuthorRepository + +`AuthorManager` 注入了 `IAuthorRepository`, 所以我们需要定义它. 在 `Acme.BookStore.Domain` 项目的 `Authors` 文件夹 (命名空间) 中创建这个新接口: + +````csharp +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore.Authors +{ + public interface IAuthorRepository : IRepository + { + Task FindByNameAsync(string name); + + Task> GetListAsync( + int skipCount, + int maxResultCount, + string sorting, + string filter = null + ); + } +} +```` + +* `IAuthorRepository` 扩展了标准 `IRepository` 接口, 所以所有的标准 [repository](../Repositories.md) 方法对于 `IAuthorRepository` 都是可用的. +* `FindByNameAsync` 在 `AuthorManager` 中用来根据姓名查询用户. +* `GetListAsync` 用于应用层以获得一个排序的, 经过过滤的作者列表, 显示在UI上. + +我们会在下一章实现这个repository. + +> 这两个方法似乎 **看上去没有必要**, 因为标准repositories已经是 `IQueryable`, 你可以直接使用它们, 而不是自定义方法. 在实际应用程序中, 这么做是没问题的. 但在这个 **学习指南**中, 解释如何在需要时创建自定义repository方法是有价值的. + +## 结论 + +这一章覆盖了图书管理程序作者相关功能的领域层. 在这一章中创建/更新的文件在下图中被高亮: + +![bookstore-author-domain-layer](images/bookstore-author-domain-layer.png) + +## 下一章 + +查看本教程的[下一章](Part-7.md). diff --git a/docs/zh-Hans/Tutorials/Part-7.md b/docs/zh-Hans/Tutorials/Part-7.md index 602306c484..95b016e451 100644 --- a/docs/zh-Hans/Tutorials/Part-7.md +++ b/docs/zh-Hans/Tutorials/Part-7.md @@ -1 +1,238 @@ -TODO.. \ No newline at end of file +# Web应用程序开发教程 - 第七章: 数据库集成 +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer","NG"], + "DB": ["EF","Mongo"] +} +```` +## 关于本教程 + +在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: + +* **{{DB_Text}}** 做为ORM提供程序. +* **{{UI_Value}}** 做为UI框架. + +本教程分为以下部分: + +- [Part 1: 创建服务端](Part-1.md) +- [Part 2: 图书列表页面](Part-2.md) +- [Part 3: 创建,更新和删除图书](Part-2.md) +- [Part 4: 集成测试](Part-4.md) +- [Part 5: 授权](Part-5.md) +- [Part 6: 作者: 领域层](Part-6.md) +- **Part 7: 数据库集成**(本章) +- [Part 8: 作者: 应用服务层](Part-8.md) +- [Part 9: 作者: 用户页面](Part-9.md) +- [Part 10: 图书到作者的关系](Part-10.md) + +## 下载源码 + +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: + +* [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) +* [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +## 简介 + +这章阐述如何为前一章介绍的 `作者` 实体配置数据库集成. + +{{if DB=="EF"}} + +## DB Context + +打开 `Acme.BookStore.EntityFrameworkCore` 项目中的 `BookStoreDbContext` 加入 `DbSet` 属性: + +````csharp +public DbSet Authors { get; set; } +```` + +定位到相同项目中的 `BookStoreDbContext` 类中的 `OnModelCreating` 方法, 加入以下代码到方法的结尾: + +````csharp +builder.Entity(b => +{ + b.ToTable(BookStoreConsts.DbTablePrefix + "Authors", + BookStoreConsts.DbSchema); + + b.ConfigureByConvention(); + + b.Property(x => x.Name) + .IsRequired() + .HasMaxLength(AuthorConsts.MaxNameLength); + + b.HasIndex(x => x.Name); +}); +```` + +这和前面的 `Book` 实体做的一样, 所以不再赘述. + +## 创建数据库迁移 + +配置启动解决方案为使用 [Entity Framework Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/). 因为我们还没有修改数据库映射配置,所以需要创建一个新的迁移并对数据库应用变更. + +打开命令行终端, 切换当前目录为 `Acme.BookStore.EntityFrameworkCore` 项目目录, 输入以下命令: + +````bash +dotnet ef migrations add Added_Authors +```` + +这会在项目中添加一个迁移类: + +![bookstore-efcore-migration-authors](./images/bookstore-efcore-migration-authors.png) + +你可以在同一个命令行终端中使用以下命令对数据库应用更改: + +````bash +dotnet ef database update +```` + +> 如果你使用 Visual Studio, 可能希望在 *Package Manager Console (PMC)* 使用 `Add-Migration Added_Authors -c BookStoreMigrationsDbContext` 和 `Update-Database -c BookStoreMigrationsDbContext` 命令. 如果这样, 保证 {{if UI=="MVC"}}`Acme.BookStore.Web`{{else if UI=="BlazorServer"}}`Acme.BookStore.Blazor`{{else if UI=="Blazor" || UI=="NG"}}`Acme.BookStore.HttpApi.Host`{{end}} 是启动项目并且在PMC中 `Acme.BookStore.EntityFrameworkCore` 是 *默认项目* . + +{{else if DB=="Mongo"}} + +## DB Context + +打开 `Acme.BookStore.MongoDB` 项目 `MongoDb 文件夹`中的 `BookStoreMongoDbContext`, 在类中加入以下属性: + +````csharp +public IMongoCollection Authors => Collection(); +```` + +{{end}} + +## 实现 IAuthorRepository + +{{if DB=="EF"}} + +在 `Acme.BookStore.EntityFrameworkCore` 项目 (`Authors` 文件夹)中创建一个新类 `EfCoreAuthorRepository`, 粘贴以下代码: + +````csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; +using Acme.BookStore.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace Acme.BookStore.Authors +{ + public class EfCoreAuthorRepository + : EfCoreRepository, + IAuthorRepository + { + public EfCoreAuthorRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async Task FindByNameAsync(string name) + { + var dbSet = await GetDbSetAsync(); + return await dbSet.FirstOrDefaultAsync(author => author.Name == name); + } + + public async Task> GetListAsync( + int skipCount, + int maxResultCount, + string sorting, + string filter = null) + { + var dbSet = await GetDbSetAsync(); + return await dbSet + .WhereIf( + !filter.IsNullOrWhiteSpace(), + author => author.Name.Contains(filter) + ) + .OrderBy(sorting) + .Skip(skipCount) + .Take(maxResultCount) + .ToListAsync(); + } + } +} +```` + +* 继承自 `EfCoreRepository`, 所以继承了标准repository的方法实现. +* `WhereIf` 是ABP 框架的快捷扩展方法. 它仅当第一个条件满足时, 执行 `Where` 查询. (根据名字查询, 仅当 filter 不为空). 你可以不使用这个方法, 但这些快捷方法可以提高效率. +* `sorting` 可以是一个字符串, 如 `Name`, `Name ASC` 或 `Name DESC`. 通过使用 [System.Linq.Dynamic.Core](https://www.nuget.org/packages/System.Linq.Dynamic.Core) NuGet 包是可能的. + +> 参阅 [EF Core 集成文档](../Entity-Framework-Core.md) 获得基于EF Core的repositories的更多信息. + +{{else if DB=="Mongo"}} + +在 `Acme.BookStore.MongoDB` 项目 (`Authors` 文件夹)中创建一个新类 `MongoDbAuthorRepository`, 粘贴以下代码: + +```csharp +using System; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Collections.Generic; +using System.Threading.Tasks; +using Acme.BookStore.MongoDB; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace Acme.BookStore.Authors +{ + public class MongoDbAuthorRepository + : MongoDbRepository, + IAuthorRepository + { + public MongoDbAuthorRepository( + IMongoDbContextProvider dbContextProvider + ) : base(dbContextProvider) + { + } + + public async Task FindByNameAsync(string name) + { + var queryable = await GetMongoQueryableAsync(); + return await queryable.FirstOrDefaultAsync(author => author.Name == name); + } + + public async Task> GetListAsync( + int skipCount, + int maxResultCount, + string sorting, + string filter = null) + { + var queryable = await GetMongoQueryableAsync(); + return await queryable + .WhereIf>( + !filter.IsNullOrWhiteSpace(), + author => author.Name.Contains(filter) + ) + .OrderBy(sorting) + .As>() + .Skip(skipCount) + .Take(maxResultCount) + .ToListAsync(); + } + } +} +``` + +* 继承自 `MongoDbRepository`, 所以继承了标准repository的方法实现. +* `WhereIf` 是ABP 框架的快捷扩展方法. 它仅当第一个条件满足时, 执行 `Where` 查询. (根据名字查询, 仅当 filter 不为空). 你可以不使用这个方法, 但这些快捷方法可以提高效率. +* `sorting` 可以是一个字符串, 如 `Name`, `Name ASC` 或 `Name DESC`. 通过使用 [System.Linq.Dynamic.Core](https://www.nuget.org/packages/System.Linq.Dynamic.Core) NuGet 包是可能的. + +> 参阅 [MongoDB 集成文档](../MongoDB.md) 获得基于MongoDB的repositories的更多信息. + +{{end}} + +## 下一章 + +查看本教程的[下一章](Part-8.md). diff --git a/docs/zh-Hans/Tutorials/Part-8.md b/docs/zh-Hans/Tutorials/Part-8.md index 602306c484..4727b8392e 100644 --- a/docs/zh-Hans/Tutorials/Part-8.md +++ b/docs/zh-Hans/Tutorials/Part-8.md @@ -1 +1,574 @@ -TODO.. \ No newline at end of file +# Web应用程序开发教程 - 第八章: 作者: 应用服务层 +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer","NG"], + "DB": ["EF","Mongo"] +} +```` +## 关于本教程 + +在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: + +* **{{DB_Text}}** 做为ORM提供程序. +* **{{UI_Value}}** 做为UI框架. + +本教程分为以下部分: + +- [Part 1: 创建服务端](Part-1.md) +- [Part 2: 图书列表页面](Part-2.md) +- [Part 3: 创建,更新和删除图书](Part-2.md) +- [Part 4: 集成测试](Part-4.md) +- [Part 5: 授权](Part-5.md) +- [Part 6: 作者: 领域层](Part-6.md) +- [Part 7: 作者: 数据库集成](Part-7.md) +- [Part 8: 作者: 应用服务层](本章) +- [Part 9: 作者: 用户页面](Part-9.md) +- [Part 10: 图书到作者的关系](Part-10.md) + +## 下载源码 + +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: + +* [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) +* [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +## 简介 + +这章阐述如何为前一章介绍的 `作者` 实体创建应用服务层. + +## IAuthorAppService + +我们首先创建 [应用服务](../Application-Services.md) 接口和相关的 [DTO](../Data-Transfer-Objects.md)s. 在 `Acme.BookStore.Application.Contracts` 项目的 `Authors` 命名空间 (文件夹) 创建一个新接口 `IAuthorAppService`: + +````csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Acme.BookStore.Authors +{ + public interface IAuthorAppService : IApplicationService + { + Task GetAsync(Guid id); + + Task> GetListAsync(GetAuthorListDto input); + + Task CreateAsync(CreateAuthorDto input); + + Task UpdateAsync(Guid id, UpdateAuthorDto input); + + Task DeleteAsync(Guid id); + } +} +```` + +* `IApplicationService` 是一个常规接口, 所有应用服务都继承自它, 所以 ABP 框架可以识别它们. +* 在 `Author` 实体中定义标准方法用于CRUD操作. +* `PagedResultDto` 是一个ABP框架中预定义的 DTO 类. 它拥有一个 `Items` 集合 和一个 `TotalCount` 属性, 用于返回分页结果. +* 优先从 `CreateAsync` 方法返回 `AuthorDto` (新创建的作者), 虽然在这个程序中没有这么做 - 这里只是展示一种不同用法. + +这个类使用下面定义的DTOs (为你的项目创建它们). + +### AuthorDto + +````csharp +using System; +using Volo.Abp.Application.Dtos; + +namespace Acme.BookStore.Authors +{ + public class AuthorDto : EntityDto + { + public string Name { get; set; } + + public DateTime BirthDate { get; set; } + + public string ShortBio { get; set; } + } +} +```` + +* `EntityDto` 只有一个类型为指定泛型参数的 `Id` 属性. 你可以自己创建 `Id` 属性, 而不是继承自 `EntityDto`. + +### GetAuthorListDto + +````csharp +using Volo.Abp.Application.Dtos; + +namespace Acme.BookStore.Authors +{ + public class GetAuthorListDto : PagedAndSortedResultRequestDto + { + public string Filter { get; set; } + } +} +```` + +* `Filter` 用于搜索作者. 它可以是 `null` (或空字符串) 以获得所有用户. +* `PagedAndSortedResultRequestDto` 具有标准分页和排序属性: `int MaxResultCount`, `int SkipCount` 和 `string Sorting`. + +> ABP 框架拥有这些基本的DTO类以简化并标准化你的DTOs. 参阅 [DTO 文档](../Data-Transfer-Objects.md) 获得所有DTO类的详细信息. + +### CreateAuthorDto + +````csharp +using System; +using System.ComponentModel.DataAnnotations; + +namespace Acme.BookStore.Authors +{ + public class CreateAuthorDto + { + [Required] + [StringLength(AuthorConsts.MaxNameLength)] + public string Name { get; set; } + + [Required] + public DateTime BirthDate { get; set; } + + public string ShortBio { get; set; } + } +} +```` + +数据标记特性可以用来验证DTO. 参阅 [验证文档](../Validation.md) 获得详细信息. + +### UpdateAuthorDto + +````csharp +using System; +using System.ComponentModel.DataAnnotations; + +namespace Acme.BookStore.Authors +{ + public class UpdateAuthorDto + { + [Required] + [StringLength(AuthorConsts.MaxNameLength)] + public string Name { get; set; } + + [Required] + public DateTime BirthDate { get; set; } + + public string ShortBio { get; set; } + } +} +```` + +> 我们可以在创建和更新操作间分享 (重用) 相同的DTO. 虽然可以这么做, 但我们推荐为这些操作创建不同的DTOs, 因为我们发现随着时间的推移, 它们通常会变得有差异. 所以, 与紧耦合相比, 代码重复也是合理的. + +## AuthorAppService + +是时候实现 `IAuthorAppService` 接口了. 在 `Acme.BookStore.Application` 项目的 `Authors` 命名空间 (文件夹) 中创建一个新类 `AuthorAppService` : + +````csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Acme.BookStore.Permissions; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore.Authors +{ + [Authorize(BookStorePermissions.Authors.Default)] + public class AuthorAppService : BookStoreAppService, IAuthorAppService + { + private readonly IAuthorRepository _authorRepository; + private readonly AuthorManager _authorManager; + + public AuthorAppService( + IAuthorRepository authorRepository, + AuthorManager authorManager) + { + _authorRepository = authorRepository; + _authorManager = authorManager; + } + + //...SERVICE METHODS WILL COME HERE... + } +} +```` + +* `[Authorize(BookStorePermissions.Authors.Default)]` 是一个检查权限(策略)的声明式方法, 用来给当前用户授权. 参阅 [授权文档](../Authorization.md) 获得详细信息. `BookStorePermissions` 类在后文会被更新, 现在不需要担心编译错误. +* 由 `BookStoreAppService` 派生, 这个类是一个简单基类, 可以做为模板. 它继承自标准的 `ApplicationService` 类. +* 实现上面定义的 `IAuthorAppService` . +* 注入 `IAuthorRepository` 和 `AuthorManager` 以使用服务方法. + +现在, 我们逐个介绍服务方法. 复制这些方法到 `AuthorAppService` 类. + +### GetAsync + +````csharp +public async Task GetAsync(Guid id) +{ + var author = await _authorRepository.GetAsync(id); + return ObjectMapper.Map(author); +} +```` + +这个方法根据 `Id` 获得 `Author` 实体, 使用 [对象到对象映射](../Object-To-Object-Mapping.md) 转换为 `AuthorDto`. 这需要配置AutoMapper, 后面会介绍. + +### GetListAsync + +````csharp +public async Task> GetListAsync(GetAuthorListDto input) +{ + if (input.Sorting.IsNullOrWhiteSpace()) + { + input.Sorting = nameof(Author.Name); + } + + var authors = await _authorRepository.GetListAsync( + input.SkipCount, + input.MaxResultCount, + input.Sorting, + input.Filter + ); + + var totalCount = input.Filter == null + ? await _authorRepository.CountAsync() + : await _authorRepository.CountAsync( + author => author.Name.Contains(input.Filter)); + + return new PagedResultDto( + totalCount, + ObjectMapper.Map, List>(authors) + ); +} +```` + +* 为处理客户端没有设置的情况, 在方法的开头设置默认排序是 "根据作者名". +* 使用 `IAuthorRepository.GetListAsync` 从数据库中获得分页的, 排序的和过滤的作者列表. 我们已经在教程的前一章中实现了它. 再一次强调, 实际上不需要创建这个方法, 因为我们可以从数据库中直接查询, 这里只是演示如何创建自定义repository方法. +* 直接查询 `AuthorRepository` , 得到作者的数量. 如果客户端发送了过滤条件, 会得到过滤后的作者数量. +* 最后, 通过映射 `Author` 列表到 `AuthorDto` 列表, 返回分页后的结果. + +### CreateAsync + +````csharp +[Authorize(BookStorePermissions.Authors.Create)] +public async Task CreateAsync(CreateAuthorDto input) +{ + var author = await _authorManager.CreateAsync( + input.Name, + input.BirthDate, + input.ShortBio + ); + + await _authorRepository.InsertAsync(author); + + return ObjectMapper.Map(author); +} +```` + +* `CreateAsync` 需要 `BookStorePermissions.Authors.Create` 权限 (另外包括 `AuthorAppService` 类声明的 `BookStorePermissions.Authors.Default` 权限). +* 使用 `AuthorManager` (领域服务) 创建新作者. +* 使用 `IAuthorRepository.InsertAsync` 插入新作者到数据库. +* 使用 `ObjectMapper` 返回 `AuthorDto` , 代表新创建的作者. + +> **DDD提示**: 一些开发者可能会发现可以在 `_authorManager.CreateAsync` 插入新实体. 我们认为把它留给应用层是更好的设计, 因为应用层更了解应该何时插入实体到数据库(在插入实体前可能需要额外的工作. 如果在领域层插入, 可能需要额外的更新操作). 但是, 你拥有最终的决定权. + +### UpdateAsync + +````csharp +[Authorize(BookStorePermissions.Authors.Edit)] +public async Task UpdateAsync(Guid id, UpdateAuthorDto input) +{ + var author = await _authorRepository.GetAsync(id); + + if (author.Name != input.Name) + { + await _authorManager.ChangeNameAsync(author, input.Name); + } + + author.BirthDate = input.BirthDate; + author.ShortBio = input.ShortBio; + + await _authorRepository.UpdateAsync(author); +} +```` + +* `UpdateAsync` 需要额外的 `BookStorePermissions.Authors.Edit` 权限. +* 使用 `IAuthorRepository.GetAsync` 从数据库中获得作者实体. 如果给定的id没有找到作者, `GetAsync` 抛出 `EntityNotFoundException`, 这在web应用程序中导致一个 `404` HTTP 状态码. 在更新操作中先获取实体再更新它, 是一个好的实践. +* 如果客户端请求, 使用 `AuthorManager.ChangeNameAsync` (领域服务方法) 修改作者姓名. +* 因为没有任何业务逻辑, 直接更新 `BirthDate` 和 `ShortBio`, 它们可以接受任何值. +* 最后, 调用 `IAuthorRepository.UpdateAsync` 更新实体到数据库. + +{{if DB == "EF"}} + +> **EF Core 提示**: Entity Framework Core 拥有 **change tracking** 系统并在unit of work 结束时 **自动保存** 任何修改到实体 (你可以简单地认为APB框架在方法结束时自动调用 `SaveChanges`). 所以, 即使你在方法结束时没有调用 `_authorRepository.UpdateAsync(...)` , 它依然可以工作. 如果你不考虑以后修改EF Core, 你可以移除这一行. + +{{end}} + +### DeleteAsync + +````csharp +[Authorize(BookStorePermissions.Authors.Delete)] +public async Task DeleteAsync(Guid id) +{ + await _authorRepository.DeleteAsync(id); +} +```` + +* `DeleteAsync` 需要额外的 `BookStorePermissions.Authors.Delete` 权限. +* 直接使用repository的 `DeleteAsync` 方法. + +## 权限定义 + +你还不能编译代码, 因为它需要 `BookStorePermissions` 类定义中一些常数. + +打开 `Acme.BookStore.Application.Contracts` 项目中的 `BookStorePermissions` 类 (在 `Permissions` 文件夹中), 修改为如下代码: + +````csharp +namespace Acme.BookStore.Permissions +{ + public static class BookStorePermissions + { + public const string GroupName = "BookStore"; + + public static class Books + { + public const string Default = GroupName + ".Books"; + public const string Create = Default + ".Create"; + public const string Edit = Default + ".Edit"; + public const string Delete = Default + ".Delete"; + } + + // *** ADDED a NEW NESTED CLASS *** + public static class Authors + { + public const string Default = GroupName + ".Authors"; + public const string Create = Default + ".Create"; + public const string Edit = Default + ".Edit"; + public const string Delete = Default + ".Delete"; + } + } +} +```` + +然后打开同一项目中的 `BookStorePermissionDefinitionProvider`, 在 `Define` 方法的结尾加入以下行: + +````csharp +var authorsPermission = bookStoreGroup.AddPermission( + BookStorePermissions.Authors.Default, L("Permission:Authors")); + +authorsPermission.AddChild( + BookStorePermissions.Authors.Create, L("Permission:Authors.Create")); + +authorsPermission.AddChild( + BookStorePermissions.Authors.Edit, L("Permission:Authors.Edit")); + +authorsPermission.AddChild( + BookStorePermissions.Authors.Delete, L("Permission:Authors.Delete")); +```` + +最后, 在 `Acme.BookStore.Domain.Shared` 项目中的 `Localization/BookStore/en.json` 加入以下项, 用以本地化权限名称: + +````csharp +"Permission:Authors": "Author Management", +"Permission:Authors.Create": "Creating new authors", +"Permission:Authors.Edit": "Editing the authors", +"Permission:Authors.Delete": "Deleting the authors" +```` + +## 对象到对象映射 + +`AuthorAppService` 使用 `ObjectMapper` 将 `Author` 对象 转换为 `AuthorDto` 对象. 所以, 我们需要在 AutoMapper 配置中定义映射. + +打开 `Acme.BookStore.Application` 项目中的 `BookStoreApplicationAutoMapperProfile` 类, 加入以下行到构造函数: + +````csharp +CreateMap(); +```` + +## 种子数据 + +如同图书管理部分所做的, 在数据库中生成一些初始作者实体. 不仅当第一次运行应用程序时是有用的, 对自动化测试也是很有用的. + +打开 `Acme.BookStore.Domain` 项目中的 `BookStoreDataSeederContributor`, 修改文件内容如下: + +````csharp +using System; +using System.Threading.Tasks; +using Acme.BookStore.Authors; +using Acme.BookStore.Books; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore +{ + public class BookStoreDataSeederContributor + : IDataSeedContributor, ITransientDependency + { + private readonly IRepository _bookRepository; + private readonly IAuthorRepository _authorRepository; + private readonly AuthorManager _authorManager; + + public BookStoreDataSeederContributor( + IRepository bookRepository, + IAuthorRepository authorRepository, + AuthorManager authorManager) + { + _bookRepository = bookRepository; + _authorRepository = authorRepository; + _authorManager = authorManager; + } + + public async Task SeedAsync(DataSeedContext context) + { + if (await _bookRepository.GetCountAsync() <= 0) + { + await _bookRepository.InsertAsync( + new Book + { + Name = "1984", + Type = BookType.Dystopia, + PublishDate = new DateTime(1949, 6, 8), + Price = 19.84f + }, + autoSave: true + ); + + await _bookRepository.InsertAsync( + new Book + { + Name = "The Hitchhiker's Guide to the Galaxy", + Type = BookType.ScienceFiction, + PublishDate = new DateTime(1995, 9, 27), + Price = 42.0f + }, + autoSave: true + ); + } + + // ADDED SEED DATA FOR AUTHORS + + if (await _authorRepository.GetCountAsync() <= 0) + { + await _authorRepository.InsertAsync( + await _authorManager.CreateAsync( + "George Orwell", + new DateTime(1903, 06, 25), + "Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)." + ) + ); + + await _authorRepository.InsertAsync( + await _authorManager.CreateAsync( + "Douglas Adams", + new DateTime(1952, 03, 11), + "Douglas Adams was an English author, screenwriter, essayist, humorist, satirist and dramatist. Adams was an advocate for environmentalism and conservation, a lover of fast cars, technological innovation and the Apple Macintosh, and a self-proclaimed 'radical atheist'." + ) + ); + } + } + } +} +```` + +{{if DB=="EF"}} + +你现在可以运行 `.DbMigrator` 控制台应用程序, **迁移** **数据库 schema** 并生成 **种子** 初始数据. + +{{else if DB=="Mongo"}} + +你现在可以运行 `.DbMigrator` 控制台应用程序, **迁移** **数据库 schema** 并生成 **种子** 初始数据. + +{{end}} + +## 测试作者应用服务 + +最后, 你可以为 `IAuthorAppService` 写一些测试. 在 `Acme.BookStore.Application.Tests` 项目的 `Authors` 命名空间(文件夹)中加入一个名为 `AuthorAppService_Tests` 新类: + +````csharp +using System; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Acme.BookStore.Authors +{ {{if DB=="Mongo"}} + [Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}} + public class AuthorAppService_Tests : BookStoreApplicationTestBase + { + private readonly IAuthorAppService _authorAppService; + + public AuthorAppService_Tests() + { + _authorAppService = GetRequiredService(); + } + + [Fact] + public async Task Should_Get_All_Authors_Without_Any_Filter() + { + var result = await _authorAppService.GetListAsync(new GetAuthorListDto()); + + result.TotalCount.ShouldBeGreaterThanOrEqualTo(2); + result.Items.ShouldContain(author => author.Name == "George Orwell"); + result.Items.ShouldContain(author => author.Name == "Douglas Adams"); + } + + [Fact] + public async Task Should_Get_Filtered_Authors() + { + var result = await _authorAppService.GetListAsync( + new GetAuthorListDto {Filter = "George"}); + + result.TotalCount.ShouldBeGreaterThanOrEqualTo(1); + result.Items.ShouldContain(author => author.Name == "George Orwell"); + result.Items.ShouldNotContain(author => author.Name == "Douglas Adams"); + } + + [Fact] + public async Task Should_Create_A_New_Author() + { + var authorDto = await _authorAppService.CreateAsync( + new CreateAuthorDto + { + Name = "Edward Bellamy", + BirthDate = new DateTime(1850, 05, 22), + ShortBio = "Edward Bellamy was an American author..." + } + ); + + authorDto.Id.ShouldNotBe(Guid.Empty); + authorDto.Name.ShouldBe("Edward Bellamy"); + } + + [Fact] + public async Task Should_Not_Allow_To_Create_Duplicate_Author() + { + await Assert.ThrowsAsync(async () => + { + await _authorAppService.CreateAsync( + new CreateAuthorDto + { + Name = "Douglas Adams", + BirthDate = DateTime.Now, + ShortBio = "..." + } + ); + }); + } + + //TODO: Test other methods... + } +} +```` + +完成应用服务方法的测试, 它们应该很容易理解. + +## 下一章 + +查看本教程的[下一章](Part-9.md). diff --git a/docs/zh-Hans/Tutorials/Part-9.md b/docs/zh-Hans/Tutorials/Part-9.md index 602306c484..2fbd4dcd55 100644 --- a/docs/zh-Hans/Tutorials/Part-9.md +++ b/docs/zh-Hans/Tutorials/Part-9.md @@ -1 +1,1249 @@ -TODO.. \ No newline at end of file +# Web应用程序开发教程 - 第九章: 作者: 用户页面 +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer","NG"], + "DB": ["EF","Mongo"] +} +```` +## 关于本教程 + +在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: + +* **{{DB_Text}}** 做为ORM提供程序. +* **{{UI_Value}}** 做为UI框架. + +本教程分为以下部分: + +- [Part 1: 创建服务端](Part-1.md) +- [Part 2: 图书列表页面](Part-2.md) +- [Part 3: 创建,更新和删除图书](Part-2.md) +- [Part 4: 集成测试](Part-4.md) +- [Part 5: 授权](Part-5.md) +- [Part 6: 作者: 领域层](Part-6.md) +- [Part 7: 作者: 数据库集成](Part-7.md) +- [Part 8: 作者: 应用服务层](Part-8.md) +- [Part 9: 作者: 用户页面](本章) +- [Part 10: 图书到作者的关系](Part-10.md) + +## 下载源码 + +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: + +* [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) +* [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +## 简介 + +这章阐述如何为前一章介绍的 `作者` 实体创建CRUD页面. + +{{if UI == "MVC"}} + +## 作者列表页面 + +在 `Acme.BookStore.Web` 项目的 `Pages/Authors` 文件夹下创建一个新的razor页面, `Index.cshtml`, 修改文件内容如下. + +### Index.cshtml + +````html +@page +@using Acme.BookStore.Localization +@using Acme.BookStore.Permissions +@using Acme.BookStore.Web.Pages.Authors +@using Microsoft.AspNetCore.Authorization +@using Microsoft.Extensions.Localization +@inject IStringLocalizer L +@inject IAuthorizationService AuthorizationService +@model IndexModel + +@section scripts +{ + +} + + + + + + @L["Authors"] + + + @if (await AuthorizationService + .IsGrantedAsync(BookStorePermissions.Authors.Create)) + { + + } + + + + + + + +```` + +这是一个简单的页面, 和我们以前创建的图书页面一样. 它导入了一个JavaScript文件, 我们后面会进行介绍这个文件. + +### IndexModel.cshtml.cs + +````csharp +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Acme.BookStore.Web.Pages.Authors +{ + public class IndexModel : PageModel + { + public void OnGet() + { + + } + } +} +```` + +### Index.js + +````js +$(function () { + var l = abp.localization.getResource('BookStore'); + var createModal = new abp.ModalManager(abp.appPath + 'Authors/CreateModal'); + var editModal = new abp.ModalManager(abp.appPath + 'Authors/EditModal'); + + var dataTable = $('#AuthorsTable').DataTable( + abp.libs.datatables.normalizeConfiguration({ + serverSide: true, + paging: true, + order: [[1, "asc"]], + searching: false, + scrollX: true, + ajax: abp.libs.datatables.createAjax(acme.bookStore.authors.author.getList), + columnDefs: [ + { + title: l('Actions'), + rowAction: { + items: + [ + { + text: l('Edit'), + visible: + abp.auth.isGranted('BookStore.Authors.Edit'), + action: function (data) { + editModal.open({ id: data.record.id }); + } + }, + { + text: l('Delete'), + visible: + abp.auth.isGranted('BookStore.Authors.Delete'), + confirmMessage: function (data) { + return l( + 'AuthorDeletionConfirmationMessage', + data.record.name + ); + }, + action: function (data) { + acme.bookStore.authors.author + .delete(data.record.id) + .then(function() { + abp.notify.info( + l('SuccessfullyDeleted') + ); + dataTable.ajax.reload(); + }); + } + } + ] + } + }, + { + title: l('Name'), + data: "name" + }, + { + title: l('BirthDate'), + data: "birthDate", + render: function (data) { + return luxon + .DateTime + .fromISO(data, { + locale: abp.localization.currentCulture.name + }).toLocaleString(); + } + } + ] + }) + ); + + createModal.onResult(function () { + dataTable.ajax.reload(); + }); + + editModal.onResult(function () { + dataTable.ajax.reload(); + }); + + $('#NewAuthorButton').click(function (e) { + e.preventDefault(); + createModal.open(); + }); +}); +```` + +简单来说, 这个JavaScript页面: + +* 创建了一个具有 `操作`, `姓名` 和 `生日` 列的数据表格. + * `Actions` 列用来添加 *编辑* 和 *删除* 操作. + * `生日` 提供了一个 `render` 函数, 使用 [luxon](https://moment.github.io/luxon/) 库格式化 `DateTime` 值. +* 使用 `abp.ModalManager` 打开 *新建* 和 *编辑* 模态表单. + +这块代码与以前创建的图书页面非常相似, 所以我们不再赘述. + +### 本地化 + +这个页面使用了一些需要声明的本地化键. 打开 `Acme.BookStore.Domain.Shared` 项目中 `Localization/BookStore` 文件夹下的 `en.json` 文件, 加入以下条目: + +````json +"Menu:Authors": "Authors", +"Authors": "Authors", +"AuthorDeletionConfirmationMessage": "Are you sure to delete the author '{0}'?", +"BirthDate": "Birth date", +"NewAuthor": "New author" +```` + +注意我们加入了额外的键. 它们会在下面的小节中被使用. + +### 加入主菜单 + +打开 `Acme.BookStore.Web` 项目的 `Menus` 文件夹中的 `BookStoreMenuContributor.cs` , 在 `ConfigureMainMenuAsync` 方法的结尾加入以下代码: + +````csharp +if (await context.IsGrantedAsync(BookStorePermissions.Authors.Default)) +{ + bookStoreMenu.AddItem(new ApplicationMenuItem( + "BooksStore.Authors", + l["Menu:Authors"], + url: "/Authors" + )); +} +```` + +### 运行应用程序 + +运行并登录应用程序. **因为你还没有权限, 所以不能看见菜单项**. 转到 `Identity/Roles` 页面, 点击 *操作* 按钮并选择**管理员角色**的*权限*操作: + +![bookstore-author-permissions](images/bookstore-author-permissions.png) + +如你所见, 管理员角色还没有*作者管理*权限. 单击复选框并保存, 赋予权限. **刷新页面**后, 你会在主菜单中的*图书商店*下看到*作者*菜单项: + +![bookstore-authors-page](images/bookstore-authors-page.png) + +页面是完全可以工作的, 除了 *新建作者* 和 *操作/编辑*, 因为它们还没有实现 . + +> **提示**: 如果你在定义一个新权限后运行 `.DbMigrator` 控制台程序, 它会自动将这些权限赋予管理员角色, 你不需要手工赋予权限. + +## 新建模态窗口 + +在 `Acme.BookStore.Web` 项目的 `Pages/Authors` 文件夹下创建一个 razor 页面 `CreateModal.cshtml`, 修改它的内容如下: + +### CreateModal.cshtml + +```html +@page +@using Acme.BookStore.Localization +@using Acme.BookStore.Web.Pages.Authors +@using Microsoft.Extensions.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model CreateModalModel +@inject IStringLocalizer L +@{ + Layout = null; +} +
    + + + + + + + + + +
    +``` + +之前我们已经使用ABP框架的 [动态表单](../UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md)开发了图书页面. 这里可以使用相同的方法, 但我们希望展示如何手工完成它. 实际上, 没有那么手工化, 因为在这个例子中我们使用了 `abp-input` 标签简化了表单元素的创建. + +你当然可以使用标准Bootstrap HTML结构, 但是这需要写很多代码. `abp-input` 自动添加验证, 本地化和根据数据类型生成标准元素. + +### CreateModal.cshtml.cs + +```csharp +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Acme.BookStore.Authors; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +namespace Acme.BookStore.Web.Pages.Authors +{ + public class CreateModalModel : BookStorePageModel + { + [BindProperty] + public CreateAuthorViewModel Author { get; set; } + + private readonly IAuthorAppService _authorAppService; + + public CreateModalModel(IAuthorAppService authorAppService) + { + _authorAppService = authorAppService; + } + + public void OnGet() + { + Author = new CreateAuthorViewModel(); + } + + public async Task OnPostAsync() + { + var dto = ObjectMapper.Map(Author); + await _authorAppService.CreateAsync(dto); + return NoContent(); + } + + public class CreateAuthorViewModel + { + [Required] + [StringLength(AuthorConsts.MaxNameLength)] + public string Name { get; set; } + + [Required] + [DataType(DataType.Date)] + public DateTime BirthDate { get; set; } + + [TextArea] + public string ShortBio { get; set; } + } + } +} +``` + +这个页面模型类注入和使用 `IAuthorAppService` 创建新作者. 它和图书创建模型类之间主要的区别是这个模型类为视图模型声明了一个新类 `CreateAuthorViewModel`, 而不是重用 `CreateAuthorDto`. + +这么做的主要原因是展示在页面中如何使用不同的模型. 但还有一个好处: 我们为类成员添加了两个不存在于 `CreateAuthorDto` 中的特性: + +* 为 `BirthDate` 添加 `[DataType(DataType.Date)]` 特性, 这会在UI为这个属性显示一个日期选择控件. +* 为 `ShortBio` 添加 `[TextArea]` 特性, 这会显示一个多行文本框, 而不是标准文本框. + +通过这种方式, 可以根据UI需求定制视图模型类, 而无需修改DTO. 这么做的一个结果是: 使用 `ObjectMapper` 将 `CreateAuthorViewModel` 映射到 `CreateAuthorDto`. 为了完成映射, 需要在 `BookStoreWebAutoMapperProfile` 构造函数中加入新的映射代码: + +````csharp +using Acme.BookStore.Authors; // ADDED NAMESPACE IMPORT +using Acme.BookStore.Books; +using AutoMapper; + +namespace Acme.BookStore.Web +{ + public class BookStoreWebAutoMapperProfile : Profile + { + public BookStoreWebAutoMapperProfile() + { + CreateMap(); + + // ADD a NEW MAPPING + CreateMap(); + } + } +} +```` + +当你重新运行应用程序后, 点击"新建作者" 按钮会打开一个新的模态窗口. + +![bookstore-new-author-modal](images/bookstore-new-author-modal.png) + +## 编辑模态窗口 + +在 `Acme.BookStore.Web` 项目的 `Pages/Authors` 文件夹下创建一个 razor 页面 `EditModal.cshtml`, 修改它的内容如下: + +### EditModal.cshtml + +````html +@page +@using Acme.BookStore.Localization +@using Acme.BookStore.Web.Pages.Authors +@using Microsoft.Extensions.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model EditModalModel +@inject IStringLocalizer L +@{ + Layout = null; +} +
    + + + + + + + + + + +
    +```` + +### EditModal.cshtml.cs + +```csharp +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Acme.BookStore.Authors; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +namespace Acme.BookStore.Web.Pages.Authors +{ + public class EditModalModel : BookStorePageModel + { + [BindProperty] + public EditAuthorViewModel Author { get; set; } + + private readonly IAuthorAppService _authorAppService; + + public EditModalModel(IAuthorAppService authorAppService) + { + _authorAppService = authorAppService; + } + + public async Task OnGetAsync(Guid id) + { + var authorDto = await _authorAppService.GetAsync(id); + Author = ObjectMapper.Map(authorDto); + } + + public async Task OnPostAsync() + { + await _authorAppService.UpdateAsync( + Author.Id, + ObjectMapper.Map(Author) + ); + + return NoContent(); + } + + public class EditAuthorViewModel + { + [HiddenInput] + public Guid Id { get; set; } + + [Required] + [StringLength(AuthorConsts.MaxNameLength)] + public string Name { get; set; } + + [Required] + [DataType(DataType.Date)] + public DateTime BirthDate { get; set; } + + [TextArea] + public string ShortBio { get; set; } + } + } +} +``` + +这个类与 `CreateModal.cshtml.cs` 类似, 主要不同是: + +* 使用 `IAuthorAppService.GetAsync(...)` 方法从应用层获取正在编辑的作者. +* `EditAuthorViewModel` 拥有一个额外的 `Id` 属性, 它被 `[HiddenInput]` 特性标记, 会为这个属性在页面上创建一个隐藏输入框. + +这个类要求在 `BookStoreWebAutoMapperProfile` 类中添加两个对象映射声明: + +```csharp +using Acme.BookStore.Authors; +using Acme.BookStore.Books; +using AutoMapper; + +namespace Acme.BookStore.Web +{ + public class BookStoreWebAutoMapperProfile : Profile + { + public BookStoreWebAutoMapperProfile() + { + CreateMap(); + + CreateMap(); + + // ADD THESE NEW MAPPINGS + CreateMap(); + CreateMap(); + } + } +} +``` + +这就是全部了! 你可以运行应用程序并尝试编辑一个作者. + +{{else if UI == "NG"}} + +## 作者管理页面 + +运行下面的命令行在angular应用程序的根目录创建一个名为 `AuthorModule` 新模块: + +```bash +yarn ng generate module author --module app --routing --route authors +``` + +这上命令应该产生以下输出: + +```bash +> yarn ng generate module author --module app --routing --route authors + +yarn run v1.19.1 +$ ng generate module author --module app --routing --route authors +CREATE src/app/author/author-routing.module.ts (344 bytes) +CREATE src/app/author/author.module.ts (349 bytes) +CREATE src/app/author/author.component.html (21 bytes) +CREATE src/app/author/author.component.spec.ts (628 bytes) +CREATE src/app/author/author.component.ts (276 bytes) +CREATE src/app/author/author.component.scss (0 bytes) +UPDATE src/app/app-routing.module.ts (1396 bytes) +Done in 2.22s. +``` + +### AuthorModule + +打开 `/src/app/author/author.module.ts,` 替换成以下内容: + +```js +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; +import { AuthorRoutingModule } from './author-routing.module'; +import { AuthorComponent } from './author.component'; +import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap'; + +@NgModule({ + declarations: [AuthorComponent], + imports: [SharedModule, AuthorRoutingModule, NgbDatepickerModule], +}) +export class AuthorModule {} +``` + +- 添加 `SharedModule`. `SharedModule` 导出一些创建用户页面需要的通用模块. +- `SharedModule` 已经导出 `CommonModule`, 所以移除 `CommonModule`. +- 添加 `NgbDatepickerModule`, 后面用于作者创建和编辑表单. + +### 菜单定义 + +打开 `src/app/route.provider.ts` 文件, 加入以下的菜单定义: + +````js +{ + path: '/authors', + name: '::Menu:Authors', + parentName: '::Menu:BookStore', + layout: eLayoutType.application, + requiredPolicy: 'BookStore.Authors', +} +```` + +最终的 `configureRoutes` 函数声明应该如下: + +```js +function configureRoutes(routes: RoutesService) { + return () => { + routes.add([ + { + path: '/', + name: '::Menu:Home', + iconClass: 'fas fa-home', + order: 1, + layout: eLayoutType.application, + }, + { + path: '/book-store', + name: '::Menu:BookStore', + iconClass: 'fas fa-book', + order: 2, + layout: eLayoutType.application, + }, + { + path: '/books', + name: '::Menu:Books', + parentName: '::Menu:BookStore', + layout: eLayoutType.application, + requiredPolicy: 'BookStore.Books', + }, + { + path: '/authors', + name: '::Menu:Authors', + parentName: '::Menu:BookStore', + layout: eLayoutType.application, + requiredPolicy: 'BookStore.Authors', + }, + ]); + }; +} +``` + +### 生成服务代理 + +[ABP CLI](https://docs.abp.io/en/abp/latest/CLI) 提供 `generate-proxy` 命令为HTTP APIs生成客户端代理, 使得使用HTTP APIs更容易. `generate-proxy` 命令运行前, 应用程序必须启动运行. + +在 `angular` 文件夹运行以下命令: + +```bash +abp generate-proxy -t ng +``` + +这个命令为作者服务和相关模型(DTO)类生成服务代理: + +![bookstore-angular-service-proxy-author](images/bookstore-angular-service-proxy-author-2.png) + +### AuthorComponent + +打开 `/src/app/author/author.component.ts` 文件, 替换成以下内容: + +```js +import { Component, OnInit } from '@angular/core'; +import { ListService, PagedResultDto } from '@abp/ng.core'; +import { AuthorService, AuthorDto } from '@proxy/authors'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; +import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; + +@Component({ + selector: 'app-author', + templateUrl: './author.component.html', + styleUrls: ['./author.component.scss'], + providers: [ListService, { provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], +}) +export class AuthorComponent implements OnInit { + author = { items: [], totalCount: 0 } as PagedResultDto; + + isModalOpen = false; + + form: FormGroup; + + selectedAuthor = {} as AuthorDto; + + constructor( + public readonly list: ListService, + private authorService: AuthorService, + private fb: FormBuilder, + private confirmation: ConfirmationService + ) {} + + ngOnInit(): void { + const authorStreamCreator = (query) => this.authorService.getList(query); + + this.list.hookToQuery(authorStreamCreator).subscribe((response) => { + this.author = response; + }); + } + + createAuthor() { + this.selectedAuthor = {} as AuthorDto; + this.buildForm(); + this.isModalOpen = true; + } + + editAuthor(id: string) { + this.authorService.get(id).subscribe((author) => { + this.selectedAuthor = author; + this.buildForm(); + this.isModalOpen = true; + }); + } + + buildForm() { + this.form = this.fb.group({ + name: [this.selectedAuthor.name || '', Validators.required], + birthDate: [ + this.selectedAuthor.birthDate ? new Date(this.selectedAuthor.birthDate) : null, + Validators.required, + ], + }); + } + + save() { + if (this.form.invalid) { + return; + } + + if (this.selectedAuthor.id) { + this.authorService + .update(this.selectedAuthor.id, this.form.value) + .subscribe(() => { + this.isModalOpen = false; + this.form.reset(); + this.list.get(); + }); + } else { + this.authorService.create(this.form.value).subscribe(() => { + this.isModalOpen = false; + this.form.reset(); + this.list.get(); + }); + } + } + + delete(id: string) { + this.confirmation.warn('::AreYouSureToDelete', '::AreYouSure') + .subscribe((status) => { + if (status === Confirmation.Status.confirm) { + this.authorService.delete(id).subscribe(() => this.list.get()); + } + }); + } +} +``` + +打开 `/src/app/author/author.component.html` 文件, 替换成以下内容: + +````html +
    +
    +
    +
    +
    + {%{{{ '::Menu:Authors' | abpLocalization }}}%} +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + +
    + +
    + + +
    +
    +
    +
    + + + + {%{{{ row.birthDate | date }}}%} + + +
    +
    +
    + + + +

    {%{{{ (selectedAuthor.id ? '::Edit' : '::NewAuthor') | abpLocalization }}}%}

    +
    + + +
    +
    + * + +
    + +
    + * + +
    +
    +
    + + + + + + +
    +```` + +### 本地化 + +这个页面使用了一些需要声明的本地化键. 打开 `Acme.BookStore.Domain.Shared` 项目中 `Localization/BookStore` 文件夹下的 `en.json` 文件, 加入以下条目: + +````json +"Menu:Authors": "Authors", +"Authors": "Authors", +"AuthorDeletionConfirmationMessage": "Are you sure to delete the author '{0}'?", +"BirthDate": "Birth date", +"NewAuthor": "New author" +```` + +### 运行应用程序 + +运行并登录应用程序. **因为你还没有权限, 所以不能看见菜单项**. 转到 `Identity/Roles` 页面, 点击 *操作* 按钮并选择**管理员角色**的*权限*操作: + +![bookstore-author-permissions](images/bookstore-author-permissions.png) + +如你所见, 管理员角色还没有*作者管理*权限. 单击复选框并保存, 赋予权限. **刷新页面**后, 你会在主菜单中的*图书商店*下看到*作者*菜单项: + +![bookstore-authors-page](images/bookstore-angular-authors-page.png) + +这就是全部了! 这是一个完整的, 可以工作的页面. 你可以新建, 编辑和删除作者. + +> **提示**: 如果你在定义一个新权限后运行 `.DbMigrator` 控制台程序, 它会自动将这些权限赋予管理员角色, 你不需要手工赋予权限. + +{{end}} + +{{if UI == "Blazor" || UI == "BlazorServer"}} + +## 作者管理页面 + +### 作者Razor组件 + +在 `Acme.BookStore.Blazor` 项目中新建一个 Razor组件页面 `/Pages/Authors.razor`, 替换成以下内容: + +````xml +@page "/authors" +@using Acme.BookStore.Authors +@using Acme.BookStore.Localization +@using Volo.Abp.AspNetCore.Components.Web +@inherits BookStoreComponentBase +@inject IAuthorAppService AuthorAppService +@inject AbpBlazorMessageLocalizerHelper LH + + + + +

    @L["Authors"]

    +
    + + + @if (CanCreateAuthor) + { + + } + + +
    +
    + + + + + + + + @L["Actions"] + + + @if (CanEditAuthor) + { + + @L["Edit"] + + } + @if (CanDeleteAuthor) + { + + @L["Delete"] + + } + + + + + + + + @context.BirthDate.ToShortDateString() + + + + + +
    + + + + +
    + + @L["NewAuthor"] + + + + + + + @L["Name"] + + + + + + + + + @L["BirthDate"] + + + + + @L["ShortBio"] + + + + + + + + + + + + + +
    +
    +
    + + + + +
    + + @EditingAuthor.Name + + + + + + + @L["Name"] + + + + + + + + + @L["BirthDate"] + + + + + @L["ShortBio"] + + + + + + + + + + + + + +
    +
    +
    +```` + +* 这些代码类似 `Books.razor`, 除了不继承自 `AbpCrudPageBase`, 它使用自己的实现. +* 注入 `IAuthorAppService` , 从UI使用服务器端的HTTP APIs . 我们可以直接注入应用服务接口并在 [动态 C# HTTP API 客户端代理系统](../API/Dynamic-CSharp-API-Clients.md)的帮助下像使用普通的方法一样使用它们, 动态 C# HTTP API 客户端代理系统会为我们调用REST API. 参考下面的 `Authors` 类获得使用方法. +* 注入 `IAuthorizationService` 检查 [权限](../Authorization.md). +* 注入 `IObjectMapper` 进行 [对象到对象映射](../Object-To-Object-Mapping.md). + +在 `Pages` 文件夹下新建一个代码后置文件 `Authors.razor.cs`, 使用以下代码: + +````csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Acme.BookStore.Authors; +using Acme.BookStore.Permissions; +using Blazorise; +using Blazorise.DataGrid; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Application.Dtos; + +namespace Acme.BookStore.Blazor.Pages +{ + public partial class Authors + { + private IReadOnlyList AuthorList { get; set; } + + private int PageSize { get; } = LimitedResultRequestDto.DefaultMaxResultCount; + private int CurrentPage { get; set; } + private string CurrentSorting { get; set; } + private int TotalCount { get; set; } + + private bool CanCreateAuthor { get; set; } + private bool CanEditAuthor { get; set; } + private bool CanDeleteAuthor { get; set; } + + private CreateAuthorDto NewAuthor { get; set; } + + private Guid EditingAuthorId { get; set; } + private UpdateAuthorDto EditingAuthor { get; set; } + + private Modal CreateAuthorModal { get; set; } + private Modal EditAuthorModal { get; set; } + + private Validations CreateValidationsRef; + + private Validations EditValidationsRef; + + public Authors() + { + NewAuthor = new CreateAuthorDto(); + EditingAuthor = new UpdateAuthorDto(); + } + + protected override async Task OnInitializedAsync() + { + await SetPermissionsAsync(); + await GetAuthorsAsync(); + } + + private async Task SetPermissionsAsync() + { + CanCreateAuthor = await AuthorizationService + .IsGrantedAsync(BookStorePermissions.Authors.Create); + + CanEditAuthor = await AuthorizationService + .IsGrantedAsync(BookStorePermissions.Authors.Edit); + + CanDeleteAuthor = await AuthorizationService + .IsGrantedAsync(BookStorePermissions.Authors.Delete); + } + + private async Task GetAuthorsAsync() + { + var result = await AuthorAppService.GetListAsync( + new GetAuthorListDto + { + MaxResultCount = PageSize, + SkipCount = CurrentPage * PageSize, + Sorting = CurrentSorting + } + ); + + AuthorList = result.Items; + TotalCount = (int)result.TotalCount; + } + + private async Task OnDataGridReadAsync(DataGridReadDataEventArgs e) + { + CurrentSorting = e.Columns + .Where(c => c.Direction != SortDirection.None) + .Select(c => c.Field + (c.Direction == SortDirection.Descending ? " DESC" : "")) + .JoinAsString(","); + CurrentPage = e.Page - 1; + + await GetAuthorsAsync(); + + await InvokeAsync(StateHasChanged); + } + + private void OpenCreateAuthorModal() + { + CreateValidationsRef.ClearAll(); + + NewAuthor = new CreateAuthorDto(); + CreateAuthorModal.Show(); + } + + private void CloseCreateAuthorModal() + { + CreateAuthorModal.Hide(); + } + + private void OpenEditAuthorModal(AuthorDto author) + { + EditValidationsRef.ClearAll(); + + EditingAuthorId = author.Id; + EditingAuthor = ObjectMapper.Map(author); + EditAuthorModal.Show(); + } + + private async Task DeleteAuthorAsync(AuthorDto author) + { + var confirmMessage = L["AuthorDeletionConfirmationMessage", author.Name]; + if (!await Message.Confirm(confirmMessage)) + { + return; + } + + await AuthorAppService.DeleteAsync(author.Id); + await GetAuthorsAsync(); + } + + private void CloseEditAuthorModal() + { + EditAuthorModal.Hide(); + } + + private async Task CreateAuthorAsync() + { + if (CreateValidationsRef.ValidateAll()) + { + await AuthorAppService.CreateAsync(NewAuthor); + await GetAuthorsAsync(); + CreateAuthorModal.Hide(); + } + } + + private async Task UpdateAuthorAsync() + { + if (EditValidationsRef.ValidateAll()) + { + await AuthorAppService.UpdateAsync(EditingAuthorId, EditingAuthor); + await GetAuthorsAsync(); + EditAuthorModal.Hide(); + } + } + } +} +```` + +这个类定义了 `Authors.razor` 页面使用的属性和方法. + +### 对象映射 + +`Authors` 类使用 `OpenEditAuthorModal` 方法中的 `IObjectMapper`. 所以需要定义这个映射. + +打开 `Acme.BookStore.Blazor` 项目中的 `BookStoreBlazorAutoMapperProfile.cs`, 在构造函数中加入一个映射: + +````csharp +CreateMap(); +```` + +你在文件开头需要 `using Acme.BookStore.Authors;` 声明语句. + +### 加入主菜单 + +打开 `Acme.BookStore.Blazor` 项目的 `Menus` 文件夹中的 `BookStoreMenuContributor.cs` , 在 `ConfigureMainMenuAsync` 方法的结尾加入以下代码: + +````csharp +if (await context.IsGrantedAsync(BookStorePermissions.Authors.Default)) +{ + bookStoreMenu.AddItem(new ApplicationMenuItem( + "BooksStore.Authors", + l["Menu:Authors"], + url: "/authors" + )); +} +```` + +### 本地化 + +我们需要本地化上面的代码. 打开 `Acme.BookStore.Domain.Shared` 项目中 `Localization/BookStore` 文件夹下的 `en.json` 文件, 加入以下条目: + +````json +"Menu:Authors": "Authors", +"Authors": "Authors", +"AuthorDeletionConfirmationMessage": "Are you sure to delete the author '{0}'?", +"BirthDate": "Birth date", +"NewAuthor": "New author" +```` + +### 运行应用程序 + +运行并登录应用程序. **因为你还没有权限, 所以不能看见菜单项**. 转到 `Identity/Roles` 页面, 点击 *操作* 按钮并选择**管理员角色**的*权限*操作: + +![bookstore-author-permissions](images/bookstore-author-permissions.png) + +如你所见, 管理员角色还没有*作者管理*权限. 单击复选框并保存, 赋予权限. **刷新页面**后, 你会在主菜单中的*图书商店*下看到*作者*菜单项: + +![bookstore-authors-page](images/bookstore-authors-blazor-ui.png) + +这就是全部了! 这是一个完整的, 可以工作的页面. 你可以新建, 编辑和删除作者. + +> **提示**: 如果你在定义一个新权限后运行 `.DbMigrator` 控制台程序, 它会自动将这些权限赋予管理员角色, 你不需要手工赋予权限. + +{{end}} + +## 下一章 + +查看本教程的[下一章](Part-10.md). diff --git a/docs/zh-Hans/Tutorials/images/blazor-add-book-button.png b/docs/zh-Hans/Tutorials/images/blazor-add-book-button.png new file mode 100644 index 0000000000..58a607d3b1 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/blazor-add-book-button.png differ diff --git a/docs/zh-Hans/Tutorials/images/blazor-add-books-component.png b/docs/zh-Hans/Tutorials/images/blazor-add-books-component.png new file mode 100644 index 0000000000..ec0412d31e Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/blazor-add-books-component.png differ diff --git a/docs/zh-Hans/Tutorials/images/blazor-bookstore-book-list.png b/docs/zh-Hans/Tutorials/images/blazor-bookstore-book-list.png new file mode 100644 index 0000000000..18bb26ccb1 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/blazor-bookstore-book-list.png differ diff --git a/docs/zh-Hans/Tutorials/images/blazor-delete-book-action.png b/docs/zh-Hans/Tutorials/images/blazor-delete-book-action.png new file mode 100644 index 0000000000..f2b0b83f30 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/blazor-delete-book-action.png differ diff --git a/docs/zh-Hans/Tutorials/images/blazor-edit-book-action-2.png b/docs/zh-Hans/Tutorials/images/blazor-edit-book-action-2.png new file mode 100644 index 0000000000..c3e2b1e0e1 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/blazor-edit-book-action-2.png differ diff --git a/docs/zh-Hans/Tutorials/images/blazor-edit-book-modal.png b/docs/zh-Hans/Tutorials/images/blazor-edit-book-modal.png new file mode 100644 index 0000000000..e63e796d34 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/blazor-edit-book-modal.png differ diff --git a/docs/zh-Hans/Tutorials/images/blazor-menu-bookstore.png b/docs/zh-Hans/Tutorials/images/blazor-menu-bookstore.png new file mode 100644 index 0000000000..f9f334aa1d Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/blazor-menu-bookstore.png differ diff --git a/docs/zh-Hans/Tutorials/images/blazor-new-book-modal.png b/docs/zh-Hans/Tutorials/images/blazor-new-book-modal.png new file mode 100644 index 0000000000..dd284a0ffd Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/blazor-new-book-modal.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-efcore-migration-authors.png b/docs/zh-Hans/Tutorials/images/bookstore-efcore-migration-authors.png new file mode 100644 index 0000000000..2cfb29f86f Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-efcore-migration-authors.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-efcore-migration.png b/docs/zh-Hans/Tutorials/images/bookstore-efcore-migration.png index 9714e795e2..5c034f6e80 100644 Binary files a/docs/zh-Hans/Tutorials/images/bookstore-efcore-migration.png and b/docs/zh-Hans/Tutorials/images/bookstore-efcore-migration.png differ diff --git a/docs/zh-Hans/Tutorials/images/generated-proxies-3.png b/docs/zh-Hans/Tutorials/images/generated-proxies-3.png new file mode 100644 index 0000000000..2a94f4e389 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/generated-proxies-3.png differ diff --git a/docs/zh-Hans/Tutorials/images/vs-run-without-iisexpress.png b/docs/zh-Hans/Tutorials/images/vs-run-without-iisexpress.png new file mode 100644 index 0000000000..03c30e2b48 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/vs-run-without-iisexpress.png differ diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index 37f57388c0..f63354c194 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -5,11 +5,25 @@ "path": "Tutorials/Todo/Index.md" }, { - "text": "入门", + "text": "入门教程", "items": [ { "text": "Web应用程序", - "path": "Getting-Started.md" + "path": "Getting-Started.md", + "items": [ + { + "text": "1: 设置你的开发环境", + "path": "Getting-Started-Setup-Environment.md" + }, + { + "text": "2: 创建新的解决方案", + "path": "Getting-Started-Create-Solution.md" + }, + { + "text": "3: 运行解决方案", + "path": "Getting-Started-Running-Solution.md" + } + ] }, { "text": "控制台应用程序", @@ -678,7 +692,7 @@ }, { "text": "CMS Kit", - "path": "Modules/Cms-Kit.md" + "path": "Modules/Cms-Kit/Index.md" }, { "text": "文档", diff --git a/docs/zh-Hans/images/create-aspnet-core-application.png b/docs/zh-Hans/images/create-aspnet-core-application.png index 5813cf38e9..b8b98f5c32 100644 Binary files a/docs/zh-Hans/images/create-aspnet-core-application.png and b/docs/zh-Hans/images/create-aspnet-core-application.png differ diff --git a/docs/zh-Hans/images/features-modal.png b/docs/zh-Hans/images/features-modal.png index 74ee8aabc3..0fce7ecf5c 100644 Binary files a/docs/zh-Hans/images/features-modal.png and b/docs/zh-Hans/images/features-modal.png differ diff --git a/docs/zh-Hans/images/identity-module-menu.png b/docs/zh-Hans/images/identity-module-menu.png new file mode 100644 index 0000000000..ce2ee7e3e9 Binary files /dev/null and b/docs/zh-Hans/images/identity-module-menu.png differ diff --git a/docs/zh-Hans/images/identity-module-permissions.png b/docs/zh-Hans/images/identity-module-permissions.png new file mode 100644 index 0000000000..dfe0a9a022 Binary files /dev/null and b/docs/zh-Hans/images/identity-module-permissions.png differ diff --git a/docs/zh-Hans/images/identity-module-roles.png b/docs/zh-Hans/images/identity-module-roles.png new file mode 100644 index 0000000000..53df1cdc6d Binary files /dev/null and b/docs/zh-Hans/images/identity-module-roles.png differ diff --git a/docs/zh-Hans/images/identity-module-users.png b/docs/zh-Hans/images/identity-module-users.png new file mode 100644 index 0000000000..33edb4cb78 Binary files /dev/null and b/docs/zh-Hans/images/identity-module-users.png differ diff --git a/docs/zh-Hans/images/module-tenant-management-actions.png b/docs/zh-Hans/images/module-tenant-management-actions.png new file mode 100644 index 0000000000..899e731f8c Binary files /dev/null and b/docs/zh-Hans/images/module-tenant-management-actions.png differ diff --git a/docs/zh-Hans/images/module-tenant-management-new-tenant.png b/docs/zh-Hans/images/module-tenant-management-new-tenant.png new file mode 100644 index 0000000000..7f0f50b42a Binary files /dev/null and b/docs/zh-Hans/images/module-tenant-management-new-tenant.png differ diff --git a/docs/zh-Hans/images/module-tenant-management-page.png b/docs/zh-Hans/images/module-tenant-management-page.png new file mode 100644 index 0000000000..5ed6cc63bd Binary files /dev/null and b/docs/zh-Hans/images/module-tenant-management-page.png differ diff --git a/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/AbpApiVersioningAbstractionsModule.cs b/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/AbpApiVersioningAbstractionsModule.cs index b4f679c927..70d960c8d5 100644 --- a/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/AbpApiVersioningAbstractionsModule.cs +++ b/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/AbpApiVersioningAbstractionsModule.cs @@ -1,13 +1,12 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; -namespace Volo.Abp.ApiVersioning +namespace Volo.Abp.ApiVersioning; + +public class AbpApiVersioningAbstractionsModule : AbpModule { - public class AbpApiVersioningAbstractionsModule : AbpModule + public override void ConfigureServices(ServiceConfigurationContext context) { - public override void ConfigureServices(ServiceConfigurationContext context) - { - context.Services.AddSingleton(NullRequestedApiVersion.Instance); - } + context.Services.AddSingleton(NullRequestedApiVersion.Instance); } } diff --git a/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/IRequestedApiVersion.cs b/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/IRequestedApiVersion.cs index 3d8a31c694..96c2ef33cd 100644 --- a/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/IRequestedApiVersion.cs +++ b/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/IRequestedApiVersion.cs @@ -1,7 +1,6 @@ -namespace Volo.Abp.ApiVersioning +namespace Volo.Abp.ApiVersioning; + +public interface IRequestedApiVersion { - public interface IRequestedApiVersion - { - string Current { get; } - } + string Current { get; } } diff --git a/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/NullRequestedApiVersion.cs b/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/NullRequestedApiVersion.cs index 721fa91815..0589c662ef 100644 --- a/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/NullRequestedApiVersion.cs +++ b/framework/src/Volo.Abp.ApiVersioning.Abstractions/Volo/Abp/ApiVersioning/NullRequestedApiVersion.cs @@ -1,14 +1,13 @@ -namespace Volo.Abp.ApiVersioning +namespace Volo.Abp.ApiVersioning; + +public class NullRequestedApiVersion : IRequestedApiVersion { - public class NullRequestedApiVersion : IRequestedApiVersion - { - public static NullRequestedApiVersion Instance = new NullRequestedApiVersion(); + public static NullRequestedApiVersion Instance = new NullRequestedApiVersion(); - public string Current => null; + public string Current => null; + + private NullRequestedApiVersion() + { - private NullRequestedApiVersion() - { - - } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Microsoft/AspNetCore/Builder/ApplicationBuilderAbpJwtTokenMiddlewareExtension.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Microsoft/AspNetCore/Builder/ApplicationBuilderAbpJwtTokenMiddlewareExtension.cs index 4ff54f39bf..8171013854 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Microsoft/AspNetCore/Builder/ApplicationBuilderAbpJwtTokenMiddlewareExtension.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Microsoft/AspNetCore/Builder/ApplicationBuilderAbpJwtTokenMiddlewareExtension.cs @@ -1,25 +1,24 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; -namespace Microsoft.AspNetCore.Builder +namespace Microsoft.AspNetCore.Builder; + +public static class ApplicationBuilderAbpJwtTokenMiddlewareExtension { - public static class ApplicationBuilderAbpJwtTokenMiddlewareExtension + public static IApplicationBuilder UseJwtTokenMiddleware(this IApplicationBuilder app, string schema = JwtBearerDefaults.AuthenticationScheme) { - public static IApplicationBuilder UseJwtTokenMiddleware(this IApplicationBuilder app, string schema = JwtBearerDefaults.AuthenticationScheme) + return app.Use(async (ctx, next) => { - return app.Use(async (ctx, next) => + if (ctx.User.Identity?.IsAuthenticated != true) { - if (ctx.User.Identity?.IsAuthenticated != true) + var result = await ctx.AuthenticateAsync(schema); + if (result.Succeeded && result.Principal != null) { - var result = await ctx.AuthenticateAsync(schema); - if (result.Succeeded && result.Principal != null) - { - ctx.User = result.Principal; - } + ctx.User = result.Principal; } + } - await next(); - }); - } + await next(); + }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo/Abp/AspNetCore/Authentication/JwtBearer/AbpAspNetCoreAuthenticationJwtBearerModule.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo/Abp/AspNetCore/Authentication/JwtBearer/AbpAspNetCoreAuthenticationJwtBearerModule.cs index 8698e2283d..1fa5077a91 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo/Abp/AspNetCore/Authentication/JwtBearer/AbpAspNetCoreAuthenticationJwtBearerModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo/Abp/AspNetCore/Authentication/JwtBearer/AbpAspNetCoreAuthenticationJwtBearerModule.cs @@ -1,11 +1,10 @@ using Volo.Abp.Modularity; using Volo.Abp.Security; -namespace Volo.Abp.AspNetCore.Authentication.JwtBearer +namespace Volo.Abp.AspNetCore.Authentication.JwtBearer; + +[DependsOn(typeof(AbpSecurityModule))] +public class AbpAspNetCoreAuthenticationJwtBearerModule : AbpModule { - [DependsOn(typeof(AbpSecurityModule))] - public class AbpAspNetCoreAuthenticationJwtBearerModule : AbpModule - { - } } diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Microsoft/AspNetCore/Authentication/OAuth/Claims/AbpClaimActionCollectionExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Microsoft/AspNetCore/Authentication/OAuth/Claims/AbpClaimActionCollectionExtensions.cs index 2ad72955af..01dc489c54 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Microsoft/AspNetCore/Authentication/OAuth/Claims/AbpClaimActionCollectionExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Microsoft/AspNetCore/Authentication/OAuth/Claims/AbpClaimActionCollectionExtensions.cs @@ -1,57 +1,56 @@ using Volo.Abp.AspNetCore.Authentication.OAuth.Claims; using Volo.Abp.Security.Claims; -namespace Microsoft.AspNetCore.Authentication.OAuth.Claims +namespace Microsoft.AspNetCore.Authentication.OAuth.Claims; + +public static class AbpClaimActionCollectionExtensions { - public static class AbpClaimActionCollectionExtensions + public static void MapAbpClaimTypes(this ClaimActionCollection claimActions) { - public static void MapAbpClaimTypes(this ClaimActionCollection claimActions) + if (AbpClaimTypes.UserName != "name") + { + claimActions.MapJsonKey(AbpClaimTypes.UserName, "name"); + claimActions.DeleteClaim("name"); + claimActions.RemoveDuplicate(AbpClaimTypes.UserName); + } + + if (AbpClaimTypes.Email != "email") + { + claimActions.MapJsonKey(AbpClaimTypes.Email, "email"); + claimActions.DeleteClaim("email"); + claimActions.RemoveDuplicate(AbpClaimTypes.Email); + } + + if (AbpClaimTypes.EmailVerified != "email_verified") + { + claimActions.MapJsonKey(AbpClaimTypes.EmailVerified, "email_verified"); + } + + if (AbpClaimTypes.PhoneNumber != "phone_number") { - if (AbpClaimTypes.UserName != "name") - { - claimActions.MapJsonKey(AbpClaimTypes.UserName, "name"); - claimActions.DeleteClaim("name"); - claimActions.RemoveDuplicate(AbpClaimTypes.UserName); - } - - if (AbpClaimTypes.Email != "email") - { - claimActions.MapJsonKey(AbpClaimTypes.Email, "email"); - claimActions.DeleteClaim("email"); - claimActions.RemoveDuplicate(AbpClaimTypes.Email); - } - - if (AbpClaimTypes.EmailVerified != "email_verified") - { - claimActions.MapJsonKey(AbpClaimTypes.EmailVerified, "email_verified"); - } - - if (AbpClaimTypes.PhoneNumber != "phone_number") - { - claimActions.MapJsonKey(AbpClaimTypes.PhoneNumber, "phone_number"); - } - - if (AbpClaimTypes.PhoneNumberVerified != "phone_number_verified") - { - claimActions.MapJsonKey(AbpClaimTypes.PhoneNumberVerified, "phone_number_verified"); - } - - if (AbpClaimTypes.Role != "role") - { - claimActions.MapJsonKeyMultiple(AbpClaimTypes.Role, "role"); - } - - claimActions.RemoveDuplicate(AbpClaimTypes.Name); + claimActions.MapJsonKey(AbpClaimTypes.PhoneNumber, "phone_number"); } - public static void MapJsonKeyMultiple(this ClaimActionCollection claimActions, string claimType, string jsonKey) + if (AbpClaimTypes.PhoneNumberVerified != "phone_number_verified") { - claimActions.Add(new MultipleClaimAction(claimType, jsonKey)); + claimActions.MapJsonKey(AbpClaimTypes.PhoneNumberVerified, "phone_number_verified"); } - - public static void RemoveDuplicate(this ClaimActionCollection claimActions, string claimType) + + if (AbpClaimTypes.Role != "role") { - claimActions.Add(new RemoveDuplicateClaimAction(claimType)); + claimActions.MapJsonKeyMultiple(AbpClaimTypes.Role, "role"); } + + claimActions.RemoveDuplicate(AbpClaimTypes.Name); + } + + public static void MapJsonKeyMultiple(this ClaimActionCollection claimActions, string claimType, string jsonKey) + { + claimActions.Add(new MultipleClaimAction(claimType, jsonKey)); + } + + public static void RemoveDuplicate(this ClaimActionCollection claimActions, string claimType) + { + claimActions.Add(new RemoveDuplicateClaimAction(claimType)); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/AbpAspNetCoreAuthenticationOAuthModule.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/AbpAspNetCoreAuthenticationOAuthModule.cs index cff3f437b7..b73c78782e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/AbpAspNetCoreAuthenticationOAuthModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/AbpAspNetCoreAuthenticationOAuthModule.cs @@ -1,11 +1,10 @@ using Volo.Abp.Modularity; using Volo.Abp.Security; -namespace Volo.Abp.AspNetCore.Authentication.OAuth +namespace Volo.Abp.AspNetCore.Authentication.OAuth; + +[DependsOn(typeof(AbpSecurityModule))] +public class AbpAspNetCoreAuthenticationOAuthModule : AbpModule { - [DependsOn(typeof(AbpSecurityModule))] - public class AbpAspNetCoreAuthenticationOAuthModule : AbpModule - { - } } diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/Claims/MultipleClaimAction.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/Claims/MultipleClaimAction.cs index 66ca78026d..1910af63d5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/Claims/MultipleClaimAction.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/Claims/MultipleClaimAction.cs @@ -3,51 +3,50 @@ using System.Security.Claims; using System.Text.Json; using Microsoft.AspNetCore.Authentication.OAuth.Claims; -namespace Volo.Abp.AspNetCore.Authentication.OAuth.Claims +namespace Volo.Abp.AspNetCore.Authentication.OAuth.Claims; + +public class MultipleClaimAction : ClaimAction { - public class MultipleClaimAction : ClaimAction + public MultipleClaimAction(string claimType, string jsonKey) + : base(claimType, jsonKey) { - public MultipleClaimAction(string claimType, string jsonKey) - : base(claimType, jsonKey) - { - } + } - public override void Run(JsonElement userData, ClaimsIdentity identity, string issuer) - { - JsonElement prop; + public override void Run(JsonElement userData, ClaimsIdentity identity, string issuer) + { + JsonElement prop; - if (!userData.TryGetProperty(ValueType, out prop)) - return; - - if (prop.ValueKind == JsonValueKind.Null) - { - return; - } + if (!userData.TryGetProperty(ValueType, out prop)) + return; - Claim claim; - switch (prop.ValueKind) - { - case JsonValueKind.String: - claim = new Claim(ClaimType, prop.GetString(), ValueType, issuer); + if (prop.ValueKind == JsonValueKind.Null) + { + return; + } + + Claim claim; + switch (prop.ValueKind) + { + case JsonValueKind.String: + claim = new Claim(ClaimType, prop.GetString(), ValueType, issuer); + if (!identity.Claims.Any(c => c.Type == claim.Type && c.Value == claim.Value)) + { + identity.AddClaim(claim); + } + break; + case JsonValueKind.Array: + foreach (var arramItem in prop.EnumerateArray()) + { + claim = new Claim(ClaimType, arramItem.GetString(), ValueType, issuer); if (!identity.Claims.Any(c => c.Type == claim.Type && c.Value == claim.Value)) { identity.AddClaim(claim); } - break; - case JsonValueKind.Array: - foreach (var arramItem in prop.EnumerateArray()) - { - claim = new Claim(ClaimType, arramItem.GetString(), ValueType, issuer); - if (!identity.Claims.Any(c => c.Type == claim.Type && c.Value == claim.Value)) - { - identity.AddClaim(claim); - } - } - break; - default: - throw new AbpException("Unhandled JsonValueKind: " + prop.ValueKind); - } + } + break; + default: + throw new AbpException("Unhandled JsonValueKind: " + prop.ValueKind); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/Claims/RemoveDuplicateClaimAction.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/Claims/RemoveDuplicateClaimAction.cs index ab3e4027c2..80773ed481 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/Claims/RemoveDuplicateClaimAction.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo/Abp/AspNetCore/Authentication/OAuth/Claims/RemoveDuplicateClaimAction.cs @@ -5,36 +5,35 @@ using System.Security.Claims; using System.Text.Json; using Microsoft.AspNetCore.Authentication.OAuth.Claims; -namespace Volo.Abp.AspNetCore.Authentication.OAuth.Claims +namespace Volo.Abp.AspNetCore.Authentication.OAuth.Claims; + +public class RemoveDuplicateClaimAction : ClaimAction { - public class RemoveDuplicateClaimAction : ClaimAction + public RemoveDuplicateClaimAction(string claimType) + : base(claimType, ClaimValueTypes.String) { - public RemoveDuplicateClaimAction(string claimType) - : base(claimType, ClaimValueTypes.String) + } + + /// + public override void Run(JsonElement userData, ClaimsIdentity identity, string issuer) + { + var claims = identity.Claims.Where(c => c.Type == ClaimType).ToArray(); + if (claims.Length < 2) { + return; } - /// - public override void Run(JsonElement userData, ClaimsIdentity identity, string issuer) + var previousValues = new List(); + foreach (var claim in claims) { - var claims = identity.Claims.Where(c => c.Type == ClaimType).ToArray(); - if (claims.Length < 2) + if (claim.Value.IsIn(previousValues)) { - return; + identity.RemoveClaim(claim); } - - var previousValues = new List(); - foreach (var claim in claims) + else { - if (claim.Value.IsIn(previousValues)) - { - identity.RemoveClaim(claim); - } - else - { - previousValues.Add(claim.Value); - } + previousValues.Add(claim.Value); } } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Microsoft/Extensions/DependencyInjection/AbpOpenIdConnectExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Microsoft/Extensions/DependencyInjection/AbpOpenIdConnectExtensions.cs index acd4233512..02b292a984 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Microsoft/Extensions/DependencyInjection/AbpOpenIdConnectExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Microsoft/Extensions/DependencyInjection/AbpOpenIdConnectExtensions.cs @@ -7,59 +7,58 @@ using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Volo.Abp.AspNetCore.MultiTenancy; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class AbpOpenIdConnectExtensions { - public static class AbpOpenIdConnectExtensions - { - public static AuthenticationBuilder AddAbpOpenIdConnect(this AuthenticationBuilder builder) - => builder.AddAbpOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, _ => { }); + public static AuthenticationBuilder AddAbpOpenIdConnect(this AuthenticationBuilder builder) + => builder.AddAbpOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, _ => { }); - public static AuthenticationBuilder AddAbpOpenIdConnect(this AuthenticationBuilder builder, Action configureOptions) - => builder.AddAbpOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, configureOptions); + public static AuthenticationBuilder AddAbpOpenIdConnect(this AuthenticationBuilder builder, Action configureOptions) + => builder.AddAbpOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, configureOptions); - public static AuthenticationBuilder AddAbpOpenIdConnect(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) - => builder.AddAbpOpenIdConnect(authenticationScheme, OpenIdConnectDefaults.DisplayName, configureOptions); + public static AuthenticationBuilder AddAbpOpenIdConnect(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) + => builder.AddAbpOpenIdConnect(authenticationScheme, OpenIdConnectDefaults.DisplayName, configureOptions); - public static AuthenticationBuilder AddAbpOpenIdConnect(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) + public static AuthenticationBuilder AddAbpOpenIdConnect(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) + { + return builder.AddOpenIdConnect(authenticationScheme, displayName, options => { - return builder.AddOpenIdConnect(authenticationScheme, displayName, options => - { - options.ClaimActions.MapAbpClaimTypes(); + options.ClaimActions.MapAbpClaimTypes(); - configureOptions?.Invoke(options); + configureOptions?.Invoke(options); - options.Events ??= new OpenIdConnectEvents(); - var authorizationCodeReceived = options.Events.OnAuthorizationCodeReceived ?? (_ => Task.CompletedTask); + options.Events ??= new OpenIdConnectEvents(); + var authorizationCodeReceived = options.Events.OnAuthorizationCodeReceived ?? (_ => Task.CompletedTask); - options.Events.OnAuthorizationCodeReceived = receivedContext => - { - SetAbpTenantId(receivedContext); - return authorizationCodeReceived.Invoke(receivedContext); - }; + options.Events.OnAuthorizationCodeReceived = receivedContext => + { + SetAbpTenantId(receivedContext); + return authorizationCodeReceived.Invoke(receivedContext); + }; - options.Events.OnRemoteFailure = remoteFailureContext => + options.Events.OnRemoteFailure = remoteFailureContext => + { + if (remoteFailureContext.Failure is OpenIdConnectProtocolException && + remoteFailureContext.Failure.Message.Contains("access_denied")) { - if (remoteFailureContext.Failure is OpenIdConnectProtocolException && - remoteFailureContext.Failure.Message.Contains("access_denied")) - { - remoteFailureContext.HandleResponse(); - remoteFailureContext.Response.Redirect($"{remoteFailureContext.Request.PathBase}/"); - } - return Task.CompletedTask; - }; - }); - } + remoteFailureContext.HandleResponse(); + remoteFailureContext.Response.Redirect($"{remoteFailureContext.Request.PathBase}/"); + } + return Task.CompletedTask; + }; + }); + } - private static void SetAbpTenantId(AuthorizationCodeReceivedContext receivedContext) - { - var tenantKey = receivedContext.HttpContext.RequestServices - .GetRequiredService>().Value.TenantKey; + private static void SetAbpTenantId(AuthorizationCodeReceivedContext receivedContext) + { + var tenantKey = receivedContext.HttpContext.RequestServices + .GetRequiredService>().Value.TenantKey; - if (receivedContext.Request.Cookies.ContainsKey(tenantKey)) - { - receivedContext.TokenEndpointRequest.SetParameter(tenantKey, - receivedContext.Request.Cookies[tenantKey]); - } + if (receivedContext.Request.Cookies.ContainsKey(tenantKey)) + { + receivedContext.TokenEndpointRequest.SetParameter(tenantKey, + receivedContext.Request.Cookies[tenantKey]); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs index 13a8fd5c04..8670fbea17 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs @@ -2,13 +2,12 @@ using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.Authentication.OpenIdConnect +namespace Volo.Abp.AspNetCore.Authentication.OpenIdConnect; + +[DependsOn( + typeof(AbpMultiTenancyModule), + typeof(AbpAspNetCoreAuthenticationOAuthModule))] +public class AbpAspNetCoreAuthenticationOpenIdConnectModule : AbpModule { - [DependsOn( - typeof(AbpMultiTenancyModule), - typeof(AbpAspNetCoreAuthenticationOAuthModule))] - public class AbpAspNetCoreAuthenticationOpenIdConnectModule : AbpModule - { - } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/AbpAspNetCoreComponentsServerThemingModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/AbpAspNetCoreComponentsServerThemingModule.cs index 72462b97d6..dab511b595 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/AbpAspNetCoreComponentsServerThemingModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/AbpAspNetCoreComponentsServerThemingModule.cs @@ -4,34 +4,33 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Packages; using Volo.Abp.Modularity; -namespace Volo.Abp.AspNetCore.Components.Server.Theming +namespace Volo.Abp.AspNetCore.Components.Server.Theming; + +[DependsOn( + typeof(AbpAspNetCoreComponentsServerModule), + typeof(AbpAspNetCoreMvcUiPackagesModule), + typeof(AbpAspNetCoreComponentsWebThemingModule), + typeof(AbpAspNetCoreMvcUiBundlingModule) + )] +public class AbpAspNetCoreComponentsServerThemingModule : AbpModule { - [DependsOn( - typeof(AbpAspNetCoreComponentsServerModule), - typeof(AbpAspNetCoreMvcUiPackagesModule), - typeof(AbpAspNetCoreComponentsWebThemingModule), - typeof(AbpAspNetCoreMvcUiBundlingModule) - )] - public class AbpAspNetCoreComponentsServerThemingModule : AbpModule + public override void ConfigureServices(ServiceConfigurationContext context) { - public override void ConfigureServices(ServiceConfigurationContext context) + Configure(options => { - Configure(options => - { - options - .StyleBundles - .Add(BlazorStandardBundles.Styles.Global, bundle => - { - bundle.AddContributors(typeof(BlazorGlobalStyleContributor)); - }); - - options - .ScriptBundles - .Add(BlazorStandardBundles.Scripts.Global, bundle => - { - bundle.AddContributors(typeof(BlazorGlobalScriptContributor)); - }); - }); - } + options + .StyleBundles + .Add(BlazorStandardBundles.Styles.Global, bundle => + { + bundle.AddContributors(typeof(BlazorGlobalStyleContributor)); + }); + + options + .ScriptBundles + .Add(BlazorStandardBundles.Scripts.Global, bundle => + { + bundle.AddContributors(typeof(BlazorGlobalScriptContributor)); + }); + }); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalBundles.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalBundles.cs index 7112fd342d..d65efa0c3f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalBundles.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalBundles.cs @@ -1,15 +1,14 @@ -namespace Volo.Abp.AspNetCore.Components.Server.Theming.Bundling +namespace Volo.Abp.AspNetCore.Components.Server.Theming.Bundling; + +public class BlazorStandardBundles { - public class BlazorStandardBundles + public static class Styles { - public static class Styles - { - public static string Global = "Blazor.Global"; - } + public static string Global = "Blazor.Global"; + } - public static class Scripts - { - public static string Global = "Blazor.Global"; - } + public static class Scripts + { + public static string Global = "Blazor.Global"; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalScriptContributor.cs index 6a0542f717..8977acc869 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalScriptContributor.cs @@ -1,14 +1,13 @@ using System.Collections.Generic; using Volo.Abp.AspNetCore.Mvc.UI.Bundling; -namespace Volo.Abp.AspNetCore.Components.Server.Theming.Bundling +namespace Volo.Abp.AspNetCore.Components.Server.Theming.Bundling; + +public class BlazorGlobalScriptContributor : BundleContributor { - public class BlazorGlobalScriptContributor : BundleContributor + public override void ConfigureBundle(BundleConfigurationContext context) { - public override void ConfigureBundle(BundleConfigurationContext context) - { - context.Files.AddIfNotContains("/_framework/blazor.server.js"); - context.Files.AddIfNotContains("/_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/abp.js"); - } + context.Files.AddIfNotContains("/_framework/blazor.server.js"); + context.Files.AddIfNotContains("/_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/abp.js"); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalStyleContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalStyleContributor.cs index 9e712ffba7..3223be417c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalStyleContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalStyleContributor.cs @@ -4,19 +4,18 @@ using Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Packages.FontAwesome; using Volo.Abp.Modularity; -namespace Volo.Abp.AspNetCore.Components.Server.Theming.Bundling +namespace Volo.Abp.AspNetCore.Components.Server.Theming.Bundling; + +[DependsOn( + typeof(BootstrapStyleContributor), + typeof(FontAwesomeStyleContributor) +)] +public class BlazorGlobalStyleContributor : BundleContributor { - [DependsOn( - typeof(BootstrapStyleContributor), - typeof(FontAwesomeStyleContributor) - )] - public class BlazorGlobalStyleContributor : BundleContributor + public override void ConfigureBundle(BundleConfigurationContext context) { - public override void ConfigureBundle(BundleConfigurationContext context) - { - context.Files.AddIfNotContains("/_content/Blazorise/blazorise.css"); - context.Files.AddIfNotContains("/_content/Blazorise.Bootstrap5/blazorise.bootstrap5.css"); - context.Files.AddIfNotContains("/_content/Blazorise.Snackbar/blazorise.snackbar.css"); - } + context.Files.AddIfNotContains("/_content/Blazorise/blazorise.css"); + context.Files.AddIfNotContains("/_content/Blazorise.Bootstrap5/blazorise.bootstrap5.css"); + context.Files.AddIfNotContains("/_content/Blazorise.Snackbar/blazorise.snackbar.css"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/AbpAspNetCoreComponentsServerModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/AbpAspNetCoreComponentsServerModule.cs index 91b672b073..dfa515c1b8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/AbpAspNetCoreComponentsServerModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/AbpAspNetCoreComponentsServerModule.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Hosting; using Volo.Abp.AspNetCore.Auditing; using Volo.Abp.AspNetCore.Components.Web; using Volo.Abp.AspNetCore.Mvc; @@ -12,49 +13,54 @@ using Volo.Abp.EventBus; using Volo.Abp.Http.Client; using Volo.Abp.Modularity; -namespace Volo.Abp.AspNetCore.Components.Server +namespace Volo.Abp.AspNetCore.Components.Server; + +[DependsOn( + typeof(AbpHttpClientModule), + typeof(AbpAspNetCoreComponentsWebModule), + typeof(AbpAspNetCoreSignalRModule), + typeof(AbpEventBusModule), + typeof(AbpAspNetCoreMvcContractsModule) + )] +public class AbpAspNetCoreComponentsServerModule : AbpModule { - [DependsOn( - typeof(AbpHttpClientModule), - typeof(AbpAspNetCoreComponentsWebModule), - typeof(AbpAspNetCoreSignalRModule), - typeof(AbpEventBusModule), - typeof(AbpAspNetCoreMvcContractsModule) - )] - public class AbpAspNetCoreComponentsServerModule : AbpModule + public override void ConfigureServices(ServiceConfigurationContext context) { - public override void ConfigureServices(ServiceConfigurationContext context) + var serverSideBlazorBuilder = context.Services.AddServerSideBlazor(options => { - var serverSideBlazorBuilder = context.Services.AddServerSideBlazor(); - context.Services.ExecutePreConfiguredActions(serverSideBlazorBuilder); - - Configure(options => + if (context.Services.GetHostingEnvironment().IsDevelopment()) { - options.IgnoredUrls.AddIfNotContains("/_blazor"); - }); + options.DetailedErrors = true; + } + }); + context.Services.ExecutePreConfiguredActions(serverSideBlazorBuilder); - Configure(options => - { - options.IgnoredUrls.AddIfNotContains("/_blazor"); - }); + Configure(options => + { + options.IgnoredUrls.AddIfNotContains("/_blazor"); + }); - Configure(options => + Configure(options => + { + options.IgnoredUrls.AddIfNotContains("/_blazor"); + }); + + Configure(options => + { + options.EndpointConfigureActions.Add(endpointContext => { - options.EndpointConfigureActions.Add(endpointContext => - { - endpointContext.Endpoints.MapBlazorHub(); - endpointContext.Endpoints.MapFallbackToPage("/_Host"); - }); + endpointContext.Endpoints.MapBlazorHub(); + endpointContext.Endpoints.MapFallbackToPage("/_Host"); }); - } + }); + } - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - context.GetEnvironment().WebRootFileProvider = - new CompositeFileProvider( - new ManifestEmbeddedFileProvider(typeof(IServerSideBlazorBuilder).Assembly), - context.GetEnvironment().WebRootFileProvider - ); - } + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + context.GetEnvironment().WebRootFileProvider = + new CompositeFileProvider( + new ManifestEmbeddedFileProvider(typeof(IServerSideBlazorBuilder).Assembly), + context.GetEnvironment().WebRootFileProvider + ); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs index 1e3cd857e5..02de9d8bf7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs @@ -4,26 +4,25 @@ using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus.Local; -namespace Volo.Abp.AspNetCore.Components.Server.Configuration +namespace Volo.Abp.AspNetCore.Components.Server.Configuration; + +[Dependency(ReplaceServices = true)] +public class BlazorServerCurrentApplicationConfigurationCacheResetService : + ICurrentApplicationConfigurationCacheResetService, + ITransientDependency { - [Dependency(ReplaceServices = true)] - public class BlazorServerCurrentApplicationConfigurationCacheResetService : - ICurrentApplicationConfigurationCacheResetService, - ITransientDependency - { - private readonly ILocalEventBus _localEventBus; + private readonly ILocalEventBus _localEventBus; - public BlazorServerCurrentApplicationConfigurationCacheResetService( - ILocalEventBus localEventBus) - { - _localEventBus = localEventBus; - } + public BlazorServerCurrentApplicationConfigurationCacheResetService( + ILocalEventBus localEventBus) + { + _localEventBus = localEventBus; + } - public async Task ResetAsync() - { - await _localEventBus.PublishAsync( - new CurrentApplicationConfigurationCacheResetEventData() - ); - } + public async Task ResetAsync() + { + await _localEventBus.PublishAsync( + new CurrentApplicationConfigurationCacheResetEventData() + ); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Extensibility/BlazorServerLookupApiRequestService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Extensibility/BlazorServerLookupApiRequestService.cs index 952a2d6de4..2071e40f0b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Extensibility/BlazorServerLookupApiRequestService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Extensibility/BlazorServerLookupApiRequestService.cs @@ -13,78 +13,77 @@ using Volo.Abp.Http.Client; using Volo.Abp.Http.Client.Authentication; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.Components.Server.Extensibility +namespace Volo.Abp.AspNetCore.Components.Server.Extensibility; + +public class BlazorServerLookupApiRequestService : ILookupApiRequestService, ITransientDependency { - public class BlazorServerLookupApiRequestService : ILookupApiRequestService, ITransientDependency + public IHttpClientFactory HttpClientFactory { get; } + public IRemoteServiceHttpClientAuthenticator HttpClientAuthenticator { get; } + public IRemoteServiceConfigurationProvider RemoteServiceConfigurationProvider { get; } + public ICurrentTenant CurrentTenant { get; } + public IHttpContextAccessor HttpContextAccessor { get; } + public NavigationManager NavigationManager { get; } + + public BlazorServerLookupApiRequestService(IHttpClientFactory httpClientFactory, + IRemoteServiceHttpClientAuthenticator httpClientAuthenticator, + ICurrentTenant currentTenant, + IHttpContextAccessor httpContextAccessor, + NavigationManager navigationManager, + IRemoteServiceConfigurationProvider remoteServiceConfigurationProvider) { - public IHttpClientFactory HttpClientFactory { get; } - public IRemoteServiceHttpClientAuthenticator HttpClientAuthenticator { get; } - public IRemoteServiceConfigurationProvider RemoteServiceConfigurationProvider { get; } - public ICurrentTenant CurrentTenant { get; } - public IHttpContextAccessor HttpContextAccessor { get; } - public NavigationManager NavigationManager { get; } + HttpClientFactory = httpClientFactory; + HttpClientAuthenticator = httpClientAuthenticator; + CurrentTenant = currentTenant; + HttpContextAccessor = httpContextAccessor; + NavigationManager = navigationManager; + RemoteServiceConfigurationProvider = remoteServiceConfigurationProvider; + } - public BlazorServerLookupApiRequestService(IHttpClientFactory httpClientFactory, - IRemoteServiceHttpClientAuthenticator httpClientAuthenticator, - ICurrentTenant currentTenant, - IHttpContextAccessor httpContextAccessor, - NavigationManager navigationManager, - IRemoteServiceConfigurationProvider remoteServiceConfigurationProvider) - { - HttpClientFactory = httpClientFactory; - HttpClientAuthenticator = httpClientAuthenticator; - CurrentTenant = currentTenant; - HttpContextAccessor = httpContextAccessor; - NavigationManager = navigationManager; - RemoteServiceConfigurationProvider = remoteServiceConfigurationProvider; - } + public async Task SendAsync(string url) + { + var client = HttpClientFactory.CreateClient(); + var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - public async Task SendAsync(string url) + var uri = new Uri(url, UriKind.RelativeOrAbsolute); + if (!uri.IsAbsoluteUri) { - var client = HttpClientFactory.CreateClient(); - var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - - var uri = new Uri(url, UriKind.RelativeOrAbsolute); - if (!uri.IsAbsoluteUri) + var baseUrl = string.Empty; + try { - var baseUrl = string.Empty; - try - { - //Blazor tiered -- mode - var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("Default"); - baseUrl = remoteServiceConfig.BaseUrl; - client.BaseAddress = new Uri(baseUrl); - AddHeaders(requestMessage); - await HttpClientAuthenticator.Authenticate(new RemoteServiceHttpClientAuthenticateContext(client, - requestMessage, new RemoteServiceConfiguration(baseUrl), string.Empty)); - } - catch (AbpException) // Blazor-Server mode. + //Blazor tiered -- mode + var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("Default"); + baseUrl = remoteServiceConfig.BaseUrl; + client.BaseAddress = new Uri(baseUrl); + AddHeaders(requestMessage); + await HttpClientAuthenticator.Authenticate(new RemoteServiceHttpClientAuthenticateContext(client, + requestMessage, new RemoteServiceConfiguration(baseUrl), string.Empty)); + } + catch (AbpException) // Blazor-Server mode. + { + baseUrl = NavigationManager.BaseUri; + client.BaseAddress = new Uri(baseUrl); + foreach (var header in HttpContextAccessor.HttpContext.Request.Headers) { - baseUrl = NavigationManager.BaseUri; - client.BaseAddress = new Uri(baseUrl); - foreach (var header in HttpContextAccessor.HttpContext.Request.Headers) - { - requestMessage.Headers.Add(header.Key, header.Value.ToArray()); - } + requestMessage.Headers.Add(header.Key, header.Value.ToArray()); } } - - var response = await client.SendAsync(requestMessage); - return await response.Content.ReadAsStringAsync(); } - protected virtual void AddHeaders(HttpRequestMessage requestMessage) + var response = await client.SendAsync(requestMessage); + return await response.Content.ReadAsStringAsync(); + } + + protected virtual void AddHeaders(HttpRequestMessage requestMessage) + { + if (CurrentTenant.Id.HasValue) { - if (CurrentTenant.Id.HasValue) - { - requestMessage.Headers.Add(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.Value.ToString()); - } + requestMessage.Headers.Add(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.Value.ToString()); + } - var currentCulture = CultureInfo.CurrentUICulture.Name ?? CultureInfo.CurrentCulture.Name; - if (!currentCulture.IsNullOrEmpty()) - { - requestMessage.Headers.AcceptLanguage.Add(new(currentCulture)); - } + var currentCulture = CultureInfo.CurrentUICulture.Name ?? CultureInfo.CurrentCulture.Name; + if (!currentCulture.IsNullOrEmpty()) + { + requestMessage.Headers.AcceptLanguage.Add(new(currentCulture)); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/AbpAspNetCoreComponentsWebThemingModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/AbpAspNetCoreComponentsWebThemingModule.cs index e5c71c8496..e2a0617b90 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/AbpAspNetCoreComponentsWebThemingModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/AbpAspNetCoreComponentsWebThemingModule.cs @@ -2,14 +2,13 @@ using Volo.Abp.Modularity; using Volo.Abp.UI.Navigation; -namespace Volo.Abp.AspNetCore.Components.Web.Theming +namespace Volo.Abp.AspNetCore.Components.Web.Theming; + +[DependsOn( + typeof(AbpBlazoriseUIModule), + typeof(AbpUiNavigationModule) + )] +public class AbpAspNetCoreComponentsWebThemingModule : AbpModule { - [DependsOn( - typeof(AbpBlazoriseUIModule), - typeof(AbpUiNavigationModule) - )] - public class AbpAspNetCoreComponentsWebThemingModule : AbpModule - { - - } -} \ No newline at end of file + +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageHeader.razor.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageHeader.razor.cs index 7806aa4b44..fd6bd50942 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageHeader.razor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageHeader.razor.cs @@ -5,69 +5,68 @@ using System.Threading.Tasks; using Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; using Volo.Abp.BlazoriseUI; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Layout +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Layout; + +public partial class PageHeader : ComponentBase { - public partial class PageHeader : ComponentBase - { - protected List ToolbarItemRenders { get; set; } - - public IPageToolbarManager PageToolbarManager { get; set; } - - [Parameter] - public string Title { get; set; } + protected List ToolbarItemRenders { get; set; } - [Parameter] - public bool BreadcrumbShowHome { get; set; } = true; + public IPageToolbarManager PageToolbarManager { get; set; } - [Parameter] - public bool BreadcrumbShowCurrent { get; set; } = true; + [Parameter] + public string Title { get; set; } - [Parameter] - public RenderFragment ChildContent { get; set; } + [Parameter] + public bool BreadcrumbShowHome { get; set; } = true; - [Parameter] - public List BreadcrumbItems { get; set; } - - [Parameter] - public PageToolbar Toolbar { get; set; } + [Parameter] + public bool BreadcrumbShowCurrent { get; set; } = true; - public PageHeader() - { - BreadcrumbItems = new List(); - ToolbarItemRenders = new List(); - } + [Parameter] + public RenderFragment ChildContent { get; set; } + + [Parameter] + public List BreadcrumbItems { get; set; } + + [Parameter] + public PageToolbar Toolbar { get; set; } + + public PageHeader() + { + BreadcrumbItems = new List(); + ToolbarItemRenders = new List(); + } - protected override async Task OnParametersSetAsync() + protected async override Task OnParametersSetAsync() + { + await base.OnParametersSetAsync(); + if (Toolbar != null) { - await base.OnParametersSetAsync(); - if (Toolbar!=null) - { - var toolbarItems = await PageToolbarManager.GetItemsAsync(Toolbar); - ToolbarItemRenders.Clear(); + var toolbarItems = await PageToolbarManager.GetItemsAsync(Toolbar); + ToolbarItemRenders.Clear(); - foreach (var item in toolbarItems) + foreach (var item in toolbarItems) + { + var sequence = 0; + ToolbarItemRenders.Add(builder => { - var sequence = 0; - ToolbarItemRenders.Add(builder => + builder.OpenComponent(sequence, item.ComponentType); + if (item.Arguments != null) { - builder.OpenComponent(sequence, item.ComponentType); - if (item.Arguments != null) + foreach (var argument in item.Arguments) { - foreach (var argument in item.Arguments) - { - sequence++; - builder.AddAttribute(sequence, argument.Key, argument.Value); - } + sequence++; + builder.AddAttribute(sequence, argument.Key, argument.Value); } - builder.CloseComponent(); - }); - } + } + builder.CloseComponent(); + }); } } + } - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - } + protected async override Task OnInitializedAsync() + { + await base.OnInitializedAsync(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/IPageToolbarContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/IPageToolbarContributor.cs index dd04c0a1ff..426fdde4c9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/IPageToolbarContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/IPageToolbarContributor.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public interface IPageToolbarContributor { - public interface IPageToolbarContributor - { - Task ContributeAsync(PageToolbarContributionContext context); - } + Task ContributeAsync(PageToolbarContributionContext context); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/IPageToolbarManager.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/IPageToolbarManager.cs index 507fa8070b..87e256b1ce 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/IPageToolbarManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/IPageToolbarManager.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public interface IPageToolbarManager { - public interface IPageToolbarManager - { - Task GetItemsAsync(PageToolbar toolbar); - } + Task GetItemsAsync(PageToolbar toolbar); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbar.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbar.cs index 84a9bae7c4..f1fb792996 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbar.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbar.cs @@ -1,12 +1,11 @@ -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public class PageToolbar { - public class PageToolbar - { - public PageToolbarContributorList Contributors { get; set; } + public PageToolbarContributorList Contributors { get; set; } - public PageToolbar() - { - Contributors = new PageToolbarContributorList(); - } + public PageToolbar() + { + Contributors = new PageToolbarContributorList(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributionContext.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributionContext.cs index 5544510b71..057777cfdf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributionContext.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributionContext.cs @@ -1,21 +1,20 @@ using JetBrains.Annotations; using System; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public class PageToolbarContributionContext { - public class PageToolbarContributionContext - { - [NotNull] - public IServiceProvider ServiceProvider { get; } + [NotNull] + public IServiceProvider ServiceProvider { get; } - [NotNull] - public PageToolbarItemList Items { get; } + [NotNull] + public PageToolbarItemList Items { get; } - public PageToolbarContributionContext( - [NotNull] IServiceProvider serviceProvider) - { - ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider)); - Items = new PageToolbarItemList(); - } + public PageToolbarContributionContext( + [NotNull] IServiceProvider serviceProvider) + { + ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider)); + Items = new PageToolbarItemList(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributor.cs index 3c5196c449..87521c592f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributor.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public abstract class PageToolbarContributor : IPageToolbarContributor { - public abstract class PageToolbarContributor : IPageToolbarContributor - { - public abstract Task ContributeAsync(PageToolbarContributionContext context); - } + public abstract Task ContributeAsync(PageToolbarContributionContext context); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributorList.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributorList.cs index 984f4618dc..02ef1bfb00 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributorList.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarContributorList.cs @@ -1,8 +1,7 @@ using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public class PageToolbarContributorList : List { - public class PageToolbarContributorList : List - { - } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarDictionary.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarDictionary.cs index 2368ab9caf..65c2e8a25d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarDictionary.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarDictionary.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public class PageToolbarDictionary : Dictionary { - public class PageToolbarDictionary : Dictionary - { - } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarExtensions.cs index 3879e27e3c..dd2063d0cf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarExtensions.cs @@ -5,67 +5,66 @@ using System.Threading.Tasks; using Volo.Abp.BlazoriseUI.Components; using Volo.Abp.Localization; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public static class PageToolbarExtensions { - public static class PageToolbarExtensions + public static PageToolbar AddComponent( + this PageToolbar toolbar, + Dictionary arguments = null, + int order = 0, + string requiredPolicyName = null) { - public static PageToolbar AddComponent( - this PageToolbar toolbar, - Dictionary arguments = null, - int order = 0, - string requiredPolicyName = null) - { - return toolbar.AddComponent( - typeof(TComponent), + return toolbar.AddComponent( + typeof(TComponent), + arguments, + order, + requiredPolicyName + ); + } + + public static PageToolbar AddComponent( + this PageToolbar toolbar, + Type componentType, + Dictionary arguments = null, + int order = 0, + string requiredPolicyName = null) + { + toolbar.Contributors.Add( + new SimplePageToolbarContributor( + componentType, arguments, order, requiredPolicyName - ); - } - - public static PageToolbar AddComponent( - this PageToolbar toolbar, - Type componentType, - Dictionary arguments = null, - int order = 0, - string requiredPolicyName = null) - { - toolbar.Contributors.Add( - new SimplePageToolbarContributor( - componentType, - arguments, - order, - requiredPolicyName - ) - ); + ) + ); - return toolbar; - } + return toolbar; + } - public static PageToolbar AddButton( - this PageToolbar toolbar, - string text, - Func clicked, - object icon = null, - Color color = Color.Primary, - bool disabled = false, - int order = 0, - string requiredPolicyName = null) - { - toolbar.AddComponent( - new Dictionary - { + public static PageToolbar AddButton( + this PageToolbar toolbar, + string text, + Func clicked, + object icon = null, + Color color = Color.Primary, + bool disabled = false, + int order = 0, + string requiredPolicyName = null) + { + toolbar.AddComponent( + new Dictionary + { { nameof(ToolbarButton.Color), color}, { nameof(ToolbarButton.Text), text}, { nameof(ToolbarButton.Disabled), disabled}, { nameof(ToolbarButton.Icon), icon}, { nameof(ToolbarButton.Clicked),clicked}, - }, - order, - requiredPolicyName - ); + }, + order, + requiredPolicyName + ); - return toolbar; - } + return toolbar; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarItem.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarItem.cs index 0e1650a803..f62d770403 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarItem.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarItem.cs @@ -2,26 +2,25 @@ using System; using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public class PageToolbarItem { - public class PageToolbarItem - { - [NotNull] - public Type ComponentType { get; } + [NotNull] + public Type ComponentType { get; } - [CanBeNull] - public Dictionary Arguments { get; set; } + [CanBeNull] + public Dictionary Arguments { get; set; } - public int Order { get; set; } + public int Order { get; set; } - public PageToolbarItem( - [NotNull] Type componentType, - [CanBeNull] Dictionary arguments = null, - int order = 0) - { - ComponentType = Check.NotNull(componentType, nameof(componentType)); - Arguments = arguments; - Order = order; - } + public PageToolbarItem( + [NotNull] Type componentType, + [CanBeNull] Dictionary arguments = null, + int order = 0) + { + ComponentType = Check.NotNull(componentType, nameof(componentType)); + Arguments = arguments; + Order = order; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarItemList.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarItemList.cs index 7042b2ed05..4be5e6fab1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarItemList.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarItemList.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public class PageToolbarItemList : List { - public class PageToolbarItemList : List - { - } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarManager.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarManager.cs index adcd72f25e..05531096d4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarManager.cs @@ -5,36 +5,35 @@ using System.Linq; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public class PageToolbarManager : IPageToolbarManager, ITransientDependency { - public class PageToolbarManager : IPageToolbarManager, ITransientDependency + protected IHybridServiceScopeFactory ServiceScopeFactory { get; } + + public PageToolbarManager( + IHybridServiceScopeFactory serviceScopeFactory) { - protected IHybridServiceScopeFactory ServiceScopeFactory { get; } + ServiceScopeFactory = serviceScopeFactory; + } - public PageToolbarManager( - IHybridServiceScopeFactory serviceScopeFactory) + public virtual async Task GetItemsAsync(PageToolbar toolbar) + { + if (toolbar == null || !toolbar.Contributors.Any()) { - ServiceScopeFactory = serviceScopeFactory; + return Array.Empty(); } - public virtual async Task GetItemsAsync(PageToolbar toolbar) + using (var scope = ServiceScopeFactory.CreateScope()) { - if (toolbar == null || !toolbar.Contributors.Any()) - { - return Array.Empty(); - } + var context = new PageToolbarContributionContext(scope.ServiceProvider); - using (var scope = ServiceScopeFactory.CreateScope()) + foreach (var contributor in toolbar.Contributors) { - var context = new PageToolbarContributionContext(scope.ServiceProvider); - - foreach (var contributor in toolbar.Contributors) - { - await contributor.ContributeAsync(context); - } - - return context.Items.OrderBy(i => i.Order).ToArray(); + await contributor.ContributeAsync(context); } + + return context.Items.OrderBy(i => i.Order).ToArray(); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/SimplePageToolbarContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/SimplePageToolbarContributor.cs index 82afe19f7f..d31b12e73c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/SimplePageToolbarContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/SimplePageToolbarContributor.cs @@ -4,50 +4,49 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; + +public class SimplePageToolbarContributor : IPageToolbarContributor { - public class SimplePageToolbarContributor : IPageToolbarContributor - { - public Type ComponentType { get; } + public Type ComponentType { get; } - public Dictionary Arguments { get; set; } + public Dictionary Arguments { get; set; } - public int Order { get; } + public int Order { get; } - public string RequiredPolicyName { get; } + public string RequiredPolicyName { get; } - public SimplePageToolbarContributor( - Type componentType, - Dictionary arguments = null, - int order = 0, - string requiredPolicyName = null) - { - ComponentType = componentType; - Arguments = arguments; - Order = order; - RequiredPolicyName = requiredPolicyName; - } + public SimplePageToolbarContributor( + Type componentType, + Dictionary arguments = null, + int order = 0, + string requiredPolicyName = null) + { + ComponentType = componentType; + Arguments = arguments; + Order = order; + RequiredPolicyName = requiredPolicyName; + } - public async Task ContributeAsync(PageToolbarContributionContext context) + public async Task ContributeAsync(PageToolbarContributionContext context) + { + if (await ShouldAddComponentAsync(context)) { - if (await ShouldAddComponentAsync(context)) - { - context.Items.Add(new PageToolbarItem(ComponentType, Arguments, Order)); - } + context.Items.Add(new PageToolbarItem(ComponentType, Arguments, Order)); } + } - protected virtual async Task ShouldAddComponentAsync(PageToolbarContributionContext context) + protected virtual async Task ShouldAddComponentAsync(PageToolbarContributionContext context) + { + if (RequiredPolicyName != null) { - if (RequiredPolicyName != null) + var authorizationService = context.ServiceProvider.GetRequiredService(); + if (!await authorizationService.IsGrantedAsync(RequiredPolicyName)) { - var authorizationService = context.ServiceProvider.GetRequiredService(); - if (!await authorizationService.IsGrantedAsync(RequiredPolicyName)) - { - return false; - } + return false; } - - return true; } + + return true; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Routing/AbpRouterOptions.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Routing/AbpRouterOptions.cs index 9655d41186..ce3b2746e4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Routing/AbpRouterOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Routing/AbpRouterOptions.cs @@ -1,16 +1,15 @@ using System.Reflection; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Routing +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Routing; + +public class AbpRouterOptions { - public class AbpRouterOptions - { - public Assembly AppAssembly { get; set; } + public Assembly AppAssembly { get; set; } - public RouterAssemblyList AdditionalAssemblies { get; } + public RouterAssemblyList AdditionalAssemblies { get; } - public AbpRouterOptions() - { - AdditionalAssemblies = new RouterAssemblyList(); - } + public AbpRouterOptions() + { + AdditionalAssemblies = new RouterAssemblyList(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Routing/RouterAssemblyList.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Routing/RouterAssemblyList.cs index ea1153ff1f..ad9d5ea404 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Routing/RouterAssemblyList.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Routing/RouterAssemblyList.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using System.Reflection; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Routing +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Routing; + +public class RouterAssemblyList : List { - public class RouterAssemblyList : List - { - - } -} \ No newline at end of file + +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/AbpToolbarOptions.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/AbpToolbarOptions.cs index a2bae7460d..b713386359 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/AbpToolbarOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/AbpToolbarOptions.cs @@ -1,16 +1,15 @@ using System.Collections.Generic; using JetBrains.Annotations; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars; + +public class AbpToolbarOptions { - public class AbpToolbarOptions - { - [NotNull] - public List Contributors { get; } + [NotNull] + public List Contributors { get; } - public AbpToolbarOptions() - { - Contributors = new List(); - } + public AbpToolbarOptions() + { + Contributors = new List(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarConfigurationContext.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarConfigurationContext.cs index 8ebe76bcb4..e2bfaf06d0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarConfigurationContext.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarConfigurationContext.cs @@ -5,25 +5,24 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Localization; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars; + +public interface IToolbarConfigurationContext : IServiceProviderAccessor { - public interface IToolbarConfigurationContext : IServiceProviderAccessor - { - Toolbar Toolbar { get; } + Toolbar Toolbar { get; } - IAuthorizationService AuthorizationService { get; } + IAuthorizationService AuthorizationService { get; } - IStringLocalizerFactory StringLocalizerFactory { get; } + IStringLocalizerFactory StringLocalizerFactory { get; } - Task IsGrantedAsync(string policyName); + Task IsGrantedAsync(string policyName); - [CanBeNull] - IStringLocalizer GetDefaultLocalizer(); + [CanBeNull] + IStringLocalizer GetDefaultLocalizer(); - [NotNull] - public IStringLocalizer GetLocalizer(); + [NotNull] + public IStringLocalizer GetLocalizer(); - [NotNull] - public IStringLocalizer GetLocalizer(Type resourceType); - } + [NotNull] + public IStringLocalizer GetLocalizer(Type resourceType); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarContributor.cs index 6a5de9c1c9..c56f964265 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarContributor.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars; + +public interface IToolbarContributor { - public interface IToolbarContributor - { - Task ConfigureToolbarAsync(IToolbarConfigurationContext context); - } -} \ No newline at end of file + Task ConfigureToolbarAsync(IToolbarConfigurationContext context); +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarManager.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarManager.cs index 0550ce75b6..df36b9ca32 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/IToolbarManager.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars; + +public interface IToolbarManager { - public interface IToolbarManager - { - Task GetAsync(string name); - } + Task GetAsync(string name); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/StandardToolbars.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/StandardToolbars.cs index 7cda0d8897..9c84d8165e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/StandardToolbars.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/StandardToolbars.cs @@ -1,7 +1,6 @@ -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars; + +public static class StandardToolbars { - public static class StandardToolbars - { - public const string Main = "Main"; - } -} \ No newline at end of file + public const string Main = "Main"; +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/Toolbar.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/Toolbar.cs index b25cc608b6..cd02da35c2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/Toolbar.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/Toolbar.cs @@ -1,18 +1,17 @@ using System.Collections.Generic; using JetBrains.Annotations; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars; + +public class Toolbar { - public class Toolbar - { - public string Name { get; } + public string Name { get; } - public List Items { get; } + public List Items { get; } - public Toolbar([NotNull] string name) - { - Name = Check.NotNull(name, nameof(name)); - Items = new List(); - } + public Toolbar([NotNull] string name) + { + Name = Check.NotNull(name, nameof(name)); + Items = new List(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarConfigurationContext.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarConfigurationContext.cs index fab4eb8f6c..f773ba14e1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarConfigurationContext.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarConfigurationContext.cs @@ -6,48 +6,47 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars; + +public class ToolbarConfigurationContext : IToolbarConfigurationContext { - public class ToolbarConfigurationContext : IToolbarConfigurationContext - { - public IServiceProvider ServiceProvider { get; } + public IServiceProvider ServiceProvider { get; } - private readonly IAbpLazyServiceProvider _lazyServiceProvider; + private readonly IAbpLazyServiceProvider _lazyServiceProvider; - public IAuthorizationService AuthorizationService => _lazyServiceProvider.LazyGetRequiredService(); + public IAuthorizationService AuthorizationService => _lazyServiceProvider.LazyGetRequiredService(); - public IStringLocalizerFactory StringLocalizerFactory => _lazyServiceProvider.LazyGetRequiredService(); + public IStringLocalizerFactory StringLocalizerFactory => _lazyServiceProvider.LazyGetRequiredService(); - public Toolbar Toolbar { get; } + public Toolbar Toolbar { get; } - public ToolbarConfigurationContext(Toolbar toolbar, IServiceProvider serviceProvider) - { - Toolbar = toolbar; - ServiceProvider = serviceProvider; - _lazyServiceProvider = ServiceProvider.GetRequiredService(); - } + public ToolbarConfigurationContext(Toolbar toolbar, IServiceProvider serviceProvider) + { + Toolbar = toolbar; + ServiceProvider = serviceProvider; + _lazyServiceProvider = ServiceProvider.GetRequiredService(); + } - public Task IsGrantedAsync(string policyName) - { - return AuthorizationService.IsGrantedAsync(policyName); - } + public Task IsGrantedAsync(string policyName) + { + return AuthorizationService.IsGrantedAsync(policyName); + } - [CanBeNull] - public IStringLocalizer GetDefaultLocalizer() - { - return StringLocalizerFactory.CreateDefaultOrNull(); - } + [CanBeNull] + public IStringLocalizer GetDefaultLocalizer() + { + return StringLocalizerFactory.CreateDefaultOrNull(); + } - [NotNull] - public IStringLocalizer GetLocalizer() - { - return StringLocalizerFactory.Create(); - } + [NotNull] + public IStringLocalizer GetLocalizer() + { + return StringLocalizerFactory.Create(); + } - [NotNull] - public IStringLocalizer GetLocalizer(Type resourceType) - { - return StringLocalizerFactory.Create(resourceType); - } + [NotNull] + public IStringLocalizer GetLocalizer(Type resourceType) + { + return StringLocalizerFactory.Create(resourceType); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarItem.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarItem.cs index d6bfcd1fb8..966145a4ee 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarItem.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarItem.cs @@ -1,23 +1,21 @@ using System; using JetBrains.Annotations; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars; + +public class ToolbarItem { - public class ToolbarItem - { - public Type ComponentType - { - get => _componentType; - set => _componentType = Check.NotNull(value, nameof(value)); - } - private Type _componentType; + public Type ComponentType { + get => _componentType; + set => _componentType = Check.NotNull(value, nameof(value)); + } + private Type _componentType; - public int Order { get; set; } + public int Order { get; set; } - public ToolbarItem([NotNull] Type componentType, int order = 0) - { - Order = order; - ComponentType = Check.NotNull(componentType, nameof(componentType)); - } + public ToolbarItem([NotNull] Type componentType, int order = 0) + { + Order = order; + ComponentType = Check.NotNull(componentType, nameof(componentType)); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarManager.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarManager.cs index 86bdfc751e..b4746fe85f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Toolbars/ToolbarManager.cs @@ -4,36 +4,35 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars +namespace Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars; + +public class ToolbarManager : IToolbarManager, ITransientDependency { - public class ToolbarManager : IToolbarManager, ITransientDependency + protected AbpToolbarOptions Options { get; } + protected IServiceProvider ServiceProvider { get; } + + public ToolbarManager( + IOptions options, + IServiceProvider serviceProvider) { - protected AbpToolbarOptions Options { get; } - protected IServiceProvider ServiceProvider { get; } + ServiceProvider = serviceProvider; + Options = options.Value; + } - public ToolbarManager( - IOptions options, - IServiceProvider serviceProvider) - { - ServiceProvider = serviceProvider; - Options = options.Value; - } + public async Task GetAsync(string name) + { + var toolbar = new Toolbar(name); - public async Task GetAsync(string name) + using (var scope = ServiceProvider.CreateScope()) { - var toolbar = new Toolbar(name); + var context = new ToolbarConfigurationContext(toolbar, scope.ServiceProvider); - using (var scope = ServiceProvider.CreateScope()) + foreach (var contributor in Options.Contributors) { - var context = new ToolbarConfigurationContext(toolbar, scope.ServiceProvider); - - foreach (var contributor in Options.Contributors) - { - await contributor.ConfigureToolbarAsync(context); - } + await contributor.ConfigureToolbarAsync(context); } - - return toolbar; } + + return toolbar; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAspNetCoreApplicationCreationOptions.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAspNetCoreApplicationCreationOptions.cs index 4eab62f819..27efa5f998 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAspNetCoreApplicationCreationOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAspNetCoreApplicationCreationOptions.cs @@ -1,13 +1,12 @@ -namespace Volo.Abp.AspNetCore.Components.Web +namespace Volo.Abp.AspNetCore.Components.Web; + +public class AbpAspNetCoreApplicationCreationOptions { - public class AbpAspNetCoreApplicationCreationOptions - { - public AbpApplicationCreationOptions ApplicationCreationOptions { get; } + public AbpApplicationCreationOptions ApplicationCreationOptions { get; } - public AbpAspNetCoreApplicationCreationOptions( - AbpApplicationCreationOptions applicationCreationOptions) - { - ApplicationCreationOptions = applicationCreationOptions; - } + public AbpAspNetCoreApplicationCreationOptions( + AbpApplicationCreationOptions applicationCreationOptions) + { + ApplicationCreationOptions = applicationCreationOptions; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAspNetCoreComponentsWebModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAspNetCoreComponentsWebModule.cs index ddcd839e3d..28984ce5d0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAspNetCoreComponentsWebModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAspNetCoreComponentsWebModule.cs @@ -5,22 +5,21 @@ using Volo.Abp.AspNetCore.Components.DependencyInjection; using Volo.Abp.Modularity; using Volo.Abp.UI; -namespace Volo.Abp.AspNetCore.Components.Web +namespace Volo.Abp.AspNetCore.Components.Web; + +[DependsOn( + typeof(AbpUiModule), + typeof(AbpAspNetCoreComponentsModule) + )] +public class AbpAspNetCoreComponentsWebModule : AbpModule { - [DependsOn( - typeof(AbpUiModule), - typeof(AbpAspNetCoreComponentsModule) - )] - public class AbpAspNetCoreComponentsWebModule : AbpModule + public override void PreConfigureServices(ServiceConfigurationContext context) { - public override void PreConfigureServices(ServiceConfigurationContext context) - { - } + } - public override void ConfigureServices(ServiceConfigurationContext context) - { - context.Services.Replace(ServiceDescriptor.Transient()); - } + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.Replace(ServiceDescriptor.Transient()); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorClientHttpMessageHandler.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorClientHttpMessageHandler.cs index 450481a646..eb8bee99bd 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorClientHttpMessageHandler.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorClientHttpMessageHandler.cs @@ -9,90 +9,89 @@ using Microsoft.JSInterop; using Volo.Abp.AspNetCore.Components.Progression; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web +namespace Volo.Abp.AspNetCore.Components.Web; + +public class AbpBlazorClientHttpMessageHandler : DelegatingHandler, ITransientDependency { - public class AbpBlazorClientHttpMessageHandler : DelegatingHandler, ITransientDependency - { - private readonly IJSRuntime _jsRuntime; + private readonly IJSRuntime _jsRuntime; - private readonly ICookieService _cookieService; + private readonly ICookieService _cookieService; - private readonly NavigationManager _navigationManager; + private readonly NavigationManager _navigationManager; - private readonly IUiPageProgressService _uiPageProgressService; + private readonly IUiPageProgressService _uiPageProgressService; - private const string AntiForgeryCookieName = "XSRF-TOKEN"; + private const string AntiForgeryCookieName = "XSRF-TOKEN"; - private const string AntiForgeryHeaderName = "RequestVerificationToken"; + private const string AntiForgeryHeaderName = "RequestVerificationToken"; - public AbpBlazorClientHttpMessageHandler( - IJSRuntime jsRuntime, - ICookieService cookieService, - NavigationManager navigationManager, - IClientScopeServiceProviderAccessor clientScopeServiceProviderAccessor) - { - _jsRuntime = jsRuntime; - _cookieService = cookieService; - _navigationManager = navigationManager; - _uiPageProgressService = clientScopeServiceProviderAccessor.ServiceProvider.GetRequiredService(); - } + public AbpBlazorClientHttpMessageHandler( + IJSRuntime jsRuntime, + ICookieService cookieService, + NavigationManager navigationManager, + IClientScopeServiceProviderAccessor clientScopeServiceProviderAccessor) + { + _jsRuntime = jsRuntime; + _cookieService = cookieService; + _navigationManager = navigationManager; + _uiPageProgressService = clientScopeServiceProviderAccessor.ServiceProvider.GetRequiredService(); + } - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + try { - try + await _uiPageProgressService.Go(null, options => { - await _uiPageProgressService.Go(null, options => - { - options.Type = UiPageProgressType.Info; - }); + options.Type = UiPageProgressType.Info; + }); - await SetLanguageAsync(request, cancellationToken); - await SetAntiForgeryTokenAsync(request); + await SetLanguageAsync(request, cancellationToken); + await SetAntiForgeryTokenAsync(request); - return await base.SendAsync(request, cancellationToken); - } - finally - { - await _uiPageProgressService.Go(-1); - } + return await base.SendAsync(request, cancellationToken); } - - private async Task SetLanguageAsync(HttpRequestMessage request, CancellationToken cancellationToken) + finally { - var selectedLanguage = await _jsRuntime.InvokeAsync( - "localStorage.getItem", - cancellationToken, - "Abp.SelectedLanguage" - ); + await _uiPageProgressService.Go(-1); + } + } - if (!selectedLanguage.IsNullOrWhiteSpace()) - { - request.Headers.AcceptLanguage.Clear(); - request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(selectedLanguage)); - } + private async Task SetLanguageAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var selectedLanguage = await _jsRuntime.InvokeAsync( + "localStorage.getItem", + cancellationToken, + "Abp.SelectedLanguage" + ); + + if (!selectedLanguage.IsNullOrWhiteSpace()) + { + request.Headers.AcceptLanguage.Clear(); + request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(selectedLanguage)); } + } - private async Task SetAntiForgeryTokenAsync(HttpRequestMessage request) + private async Task SetAntiForgeryTokenAsync(HttpRequestMessage request) + { + if (request.Method == HttpMethod.Get || request.Method == HttpMethod.Head || + request.Method == HttpMethod.Trace || request.Method == HttpMethod.Options) { - if (request.Method == HttpMethod.Get || request.Method == HttpMethod.Head || - request.Method == HttpMethod.Trace || request.Method == HttpMethod.Options) - { - return; - } + return; + } - var selfUri = new Uri(_navigationManager.Uri); + var selfUri = new Uri(_navigationManager.Uri); - if (request.RequestUri.Host != selfUri.Host || request.RequestUri.Port != selfUri.Port) - { - return; - } + if (request.RequestUri.Host != selfUri.Host || request.RequestUri.Port != selfUri.Port) + { + return; + } - var token = await _cookieService.GetAsync(AntiForgeryCookieName); + var token = await _cookieService.GetAsync(AntiForgeryCookieName); - if (!token.IsNullOrWhiteSpace()) - { - request.Headers.Add(AntiForgeryHeaderName, token); - } + if (!token.IsNullOrWhiteSpace()) + { + request.Headers.Add(AntiForgeryHeaderName, token); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorMessageLocalizerHelper.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorMessageLocalizerHelper.cs index 5b5a838e17..52efbc6d11 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorMessageLocalizerHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorMessageLocalizerHelper.cs @@ -3,46 +3,45 @@ using System.Linq; using JetBrains.Annotations; using Microsoft.Extensions.Localization; -namespace Volo.Abp.AspNetCore.Components.Web +namespace Volo.Abp.AspNetCore.Components.Web; + +public class AbpBlazorMessageLocalizerHelper { - public class AbpBlazorMessageLocalizerHelper + private readonly IStringLocalizer stringLocalizer; + + public AbpBlazorMessageLocalizerHelper(IStringLocalizer stringLocalizer) { - private readonly IStringLocalizer stringLocalizer; + this.stringLocalizer = stringLocalizer; + } - public AbpBlazorMessageLocalizerHelper(IStringLocalizer stringLocalizer) + public string Localize(string message, [CanBeNull] IEnumerable arguments) + { + try { - this.stringLocalizer = stringLocalizer; + return arguments?.Count() > 0 + ? stringLocalizer[message, LocalizeMessageArguments(arguments)?.ToArray()] + : stringLocalizer[message]; } - - public string Localize(string message, [CanBeNull] IEnumerable arguments) + catch { - try - { - return arguments?.Count() > 0 - ? stringLocalizer[message, LocalizeMessageArguments(arguments)?.ToArray()] - : stringLocalizer[message]; - } - catch - { - return stringLocalizer[message]; - } + return stringLocalizer[message]; } + } - private IEnumerable LocalizeMessageArguments(IEnumerable arguments) + private IEnumerable LocalizeMessageArguments(IEnumerable arguments) + { + foreach (var argument in arguments) { - foreach (var argument in arguments) - { - // first try to localize with "DisplayName:{Name}" - var localization = stringLocalizer[$"DisplayName:{argument}"]; + // first try to localize with "DisplayName:{Name}" + var localization = stringLocalizer[$"DisplayName:{argument}"]; - if (localization.ResourceNotFound) - { - // then try to localize with just "{Name}" - localization = stringLocalizer[argument]; - } - - yield return localization; + if (localization.ResourceNotFound) + { + // then try to localize with just "{Name}" + localization = stringLocalizer[argument]; } + + yield return localization; } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpUtilsService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpUtilsService.cs index b80e927154..082dd7c32f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpUtilsService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpUtilsService.cs @@ -2,50 +2,49 @@ using Microsoft.JSInterop; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web +namespace Volo.Abp.AspNetCore.Components.Web; + +public class AbpUtilsService : IAbpUtilsService, ITransientDependency { - public class AbpUtilsService : IAbpUtilsService, ITransientDependency - { - protected IJSRuntime JsRuntime { get; } - - public AbpUtilsService(IJSRuntime jsRuntime) - { - JsRuntime = jsRuntime; - } - - public ValueTask AddClassToTagAsync(string tagName, string className) - { - return JsRuntime.InvokeVoidAsync("abp.utils.addClassToTag", tagName, className); - } - - public ValueTask RemoveClassFromTagAsync(string tagName, string className) - { - return JsRuntime.InvokeVoidAsync("abp.utils.removeClassFromTag", tagName, className); - } - - public ValueTask HasClassOnTagAsync(string tagName, string className) - { - return JsRuntime.InvokeAsync("abp.utils.hasClassOnTag", tagName, className); - } - - public ValueTask ReplaceLinkHrefByIdAsync(string linkId, string hrefValue) - { - return JsRuntime.InvokeVoidAsync("abp.utils.replaceLinkHrefById", linkId, hrefValue); - } - - public ValueTask ToggleFullscreenAsync() - { - return JsRuntime.InvokeVoidAsync("abp.utils.toggleFullscreen"); - } - - public ValueTask RequestFullscreenAsync() - { - return JsRuntime.InvokeVoidAsync("abp.utils.requestFullscreen"); - } - - public ValueTask ExitFullscreenAsync() - { - return JsRuntime.InvokeVoidAsync("abp.utils.exitFullscreen"); - } + protected IJSRuntime JsRuntime { get; } + + public AbpUtilsService(IJSRuntime jsRuntime) + { + JsRuntime = jsRuntime; + } + + public ValueTask AddClassToTagAsync(string tagName, string className) + { + return JsRuntime.InvokeVoidAsync("abp.utils.addClassToTag", tagName, className); + } + + public ValueTask RemoveClassFromTagAsync(string tagName, string className) + { + return JsRuntime.InvokeVoidAsync("abp.utils.removeClassFromTag", tagName, className); + } + + public ValueTask HasClassOnTagAsync(string tagName, string className) + { + return JsRuntime.InvokeAsync("abp.utils.hasClassOnTag", tagName, className); + } + + public ValueTask ReplaceLinkHrefByIdAsync(string linkId, string hrefValue) + { + return JsRuntime.InvokeVoidAsync("abp.utils.replaceLinkHrefById", linkId, hrefValue); + } + + public ValueTask ToggleFullscreenAsync() + { + return JsRuntime.InvokeVoidAsync("abp.utils.toggleFullscreen"); + } + + public ValueTask RequestFullscreenAsync() + { + return JsRuntime.InvokeVoidAsync("abp.utils.requestFullscreen"); + } + + public ValueTask ExitFullscreenAsync() + { + return JsRuntime.InvokeVoidAsync("abp.utils.exitFullscreen"); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Alerts/AlertManager.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Alerts/AlertManager.cs index aef46b97dd..218e3cbdea 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Alerts/AlertManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Alerts/AlertManager.cs @@ -1,15 +1,14 @@ using Volo.Abp.AspNetCore.Components.Alerts; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web.Alerts +namespace Volo.Abp.AspNetCore.Components.Web.Alerts; + +public class AlertManager : IAlertManager, IScopedDependency { - public class AlertManager : IAlertManager, IScopedDependency - { - public AlertList Alerts { get; } + public AlertList Alerts { get; } - public AlertManager() - { - Alerts = new AlertList(); - } + public AlertManager() + { + Alerts = new AlertList(); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs index b1da30825d..c3e33a9e41 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Web.Configuration +namespace Volo.Abp.AspNetCore.Components.Web.Configuration; + +public interface ICurrentApplicationConfigurationCacheResetService { - public interface ICurrentApplicationConfigurationCacheResetService - { - Task ResetAsync(); - } -} \ No newline at end of file + Task ResetAsync(); +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs index 6eeb0e9471..bb91d70775 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs @@ -1,13 +1,12 @@ using System.Threading.Tasks; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web.Configuration +namespace Volo.Abp.AspNetCore.Components.Web.Configuration; + +public class NullCurrentApplicationConfigurationCacheResetService : ICurrentApplicationConfigurationCacheResetService, ISingletonDependency { - public class NullCurrentApplicationConfigurationCacheResetService : ICurrentApplicationConfigurationCacheResetService, ISingletonDependency + public Task ResetAsync() { - public Task ResetAsync() - { - return Task.CompletedTask; - } + return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/CookieOptions.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/CookieOptions.cs index 2781d9ead3..283f665642 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/CookieOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/CookieOptions.cs @@ -1,11 +1,10 @@ using System; -namespace Volo.Abp.AspNetCore.Components.Web +namespace Volo.Abp.AspNetCore.Components.Web; + +public class CookieOptions { - public class CookieOptions - { - public DateTimeOffset? ExpireDate { get; set; } - public string Path { get; set; } - public bool Secure { get; set; } - } -} \ No newline at end of file + public DateTimeOffset? ExpireDate { get; set; } + public string Path { get; set; } + public bool Secure { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/CookieService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/CookieService.cs index ed682261a3..1b61acb459 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/CookieService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/CookieService.cs @@ -2,31 +2,30 @@ using Microsoft.JSInterop; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web +namespace Volo.Abp.AspNetCore.Components.Web; + +[Dependency(ReplaceServices = true)] +public class CookieService : ICookieService, ITransientDependency { - [Dependency(ReplaceServices = true)] - public class CookieService : ICookieService, ITransientDependency - { - public IJSRuntime JsRuntime { get; } + public IJSRuntime JsRuntime { get; } - public CookieService(IJSRuntime jsRuntime) - { - JsRuntime = jsRuntime; - } + public CookieService(IJSRuntime jsRuntime) + { + JsRuntime = jsRuntime; + } - public async ValueTask SetAsync(string key, string value, CookieOptions options) - { - await JsRuntime.InvokeVoidAsync("abp.utils.setCookieValue", key, value, options?.ExpireDate?.ToString("r"), options?.Path, options?.Secure); - } + public async ValueTask SetAsync(string key, string value, CookieOptions options) + { + await JsRuntime.InvokeVoidAsync("abp.utils.setCookieValue", key, value, options?.ExpireDate?.ToString("r"), options?.Path, options?.Secure); + } - public async ValueTask GetAsync(string key) - { - return await JsRuntime.InvokeAsync("abp.utils.getCookieValue", key); - } + public async ValueTask GetAsync(string key) + { + return await JsRuntime.InvokeAsync("abp.utils.getCookieValue", key); + } - public async ValueTask DeleteAsync(string key, string path = null) - { - await JsRuntime.InvokeVoidAsync("abp.utils.deleteCookie", key); - } + public async ValueTask DeleteAsync(string key, string path = null) + { + await JsRuntime.InvokeVoidAsync("abp.utils.deleteCookie", key); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/DefaultServerUrlProvider.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/DefaultServerUrlProvider.cs index 629a19d9f5..281ee3cd73 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/DefaultServerUrlProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/DefaultServerUrlProvider.cs @@ -1,13 +1,12 @@ using System.Threading.Tasks; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web +namespace Volo.Abp.AspNetCore.Components.Web; + +public class DefaultServerUrlProvider : IServerUrlProvider, ISingletonDependency { - public class DefaultServerUrlProvider : IServerUrlProvider, ISingletonDependency + public Task GetBaseUrlAsync(string remoteServiceName = null) { - public Task GetBaseUrlAsync(string remoteServiceName = null) - { - return Task.FromResult("/"); - } + return Task.FromResult("/"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/DependencyInjection/ComponentsClientScopeServiceProviderAccessor.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/DependencyInjection/ComponentsClientScopeServiceProviderAccessor.cs index 5ced9d658e..f5e07865a7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/DependencyInjection/ComponentsClientScopeServiceProviderAccessor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/DependencyInjection/ComponentsClientScopeServiceProviderAccessor.cs @@ -1,12 +1,11 @@ using System; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web.DependencyInjection +namespace Volo.Abp.AspNetCore.Components.Web.DependencyInjection; + +public class ComponentsClientScopeServiceProviderAccessor : + IClientScopeServiceProviderAccessor, + ISingletonDependency { - public class ComponentsClientScopeServiceProviderAccessor : - IClientScopeServiceProviderAccessor, - ISingletonDependency - { - public IServiceProvider ServiceProvider { get; set; } - } + public IServiceProvider ServiceProvider { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/AbpExceptionHandlingLogger.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/AbpExceptionHandlingLogger.cs index 755947a481..cf66f107a0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/AbpExceptionHandlingLogger.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/AbpExceptionHandlingLogger.cs @@ -3,64 +3,63 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Volo.Abp.AspNetCore.Components.ExceptionHandling; -namespace Volo.Abp.AspNetCore.Components.Web.ExceptionHandling +namespace Volo.Abp.AspNetCore.Components.Web.ExceptionHandling; + +public class AbpExceptionHandlingLogger : ILogger { - public class AbpExceptionHandlingLogger : ILogger + private readonly IServiceCollection _serviceCollection; + private IUserExceptionInformer _userExceptionInformer; + + public AbpExceptionHandlingLogger(IServiceCollection serviceCollection) { - private readonly IServiceCollection _serviceCollection; - private IUserExceptionInformer _userExceptionInformer; + _serviceCollection = serviceCollection; + } - public AbpExceptionHandlingLogger(IServiceCollection serviceCollection) + public virtual void Log( + LogLevel logLevel, + EventId eventId, + TState state, + Exception exception, + Func formatter) + { + if (exception == null) { - _serviceCollection = serviceCollection; + return; } - public virtual void Log( - LogLevel logLevel, - EventId eventId, - TState state, - Exception exception, - Func formatter) + if (logLevel != LogLevel.Critical && logLevel != LogLevel.Error) { - if (exception == null) - { - return; - } - - if (logLevel != LogLevel.Critical && logLevel != LogLevel.Error) - { - return; - } - - TryInitialize(); - - if (_userExceptionInformer == null) - { - return; - } - - _userExceptionInformer.Inform(new UserExceptionInformerContext(exception)); + return; } - protected virtual void TryInitialize() - { - var serviceProvider = _serviceCollection.GetServiceProviderOrNull(); - if (serviceProvider == null) - { - return; - } - - _userExceptionInformer = serviceProvider.GetRequiredService(); - } + TryInitialize(); - public virtual bool IsEnabled(LogLevel logLevel) + if (_userExceptionInformer == null) { - return logLevel == LogLevel.Critical || logLevel == LogLevel.Error; + return; } - public virtual IDisposable BeginScope(TState state) + _userExceptionInformer.Inform(new UserExceptionInformerContext(exception)); + } + + protected virtual void TryInitialize() + { + var serviceProvider = _serviceCollection.GetServiceProviderOrNull(); + if (serviceProvider == null) { - return NullDisposable.Instance; + return; } + + _userExceptionInformer = serviceProvider.GetRequiredService(); + } + + public virtual bool IsEnabled(LogLevel logLevel) + { + return logLevel == LogLevel.Critical || logLevel == LogLevel.Error; + } + + public virtual IDisposable BeginScope(TState state) + { + return NullDisposable.Instance; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/AbpExceptionHandlingLoggerProvider.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/AbpExceptionHandlingLoggerProvider.cs index 85b16d08ba..3f895502fa 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/AbpExceptionHandlingLoggerProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/AbpExceptionHandlingLoggerProvider.cs @@ -1,38 +1,37 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Volo.Abp.AspNetCore.Components.Web.ExceptionHandling +namespace Volo.Abp.AspNetCore.Components.Web.ExceptionHandling; + +public class AbpExceptionHandlingLoggerProvider : ILoggerProvider { - public class AbpExceptionHandlingLoggerProvider : ILoggerProvider - { - private AbpExceptionHandlingLogger _logger; - private static readonly object SyncObj = new object(); - private readonly IServiceCollection _serviceCollection; + private AbpExceptionHandlingLogger _logger; + private static readonly object SyncObj = new object(); + private readonly IServiceCollection _serviceCollection; - public AbpExceptionHandlingLoggerProvider(IServiceCollection serviceCollection) - { - _serviceCollection = serviceCollection; - } + public AbpExceptionHandlingLoggerProvider(IServiceCollection serviceCollection) + { + _serviceCollection = serviceCollection; + } - public ILogger CreateLogger(string categoryName) + public ILogger CreateLogger(string categoryName) + { + if (_logger == null) { - if (_logger == null) + lock (SyncObj) { - lock (SyncObj) + if (_logger == null) { - if (_logger == null) - { - _logger = new AbpExceptionHandlingLogger(_serviceCollection); - } + _logger = new AbpExceptionHandlingLogger(_serviceCollection); } } - - return _logger; } - public void Dispose() - { + return _logger; + } + + public void Dispose() + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/UserExceptionInformer.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/UserExceptionInformer.cs index ecbeb16e81..0f0da11bdf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/UserExceptionInformer.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/UserExceptionInformer.cs @@ -9,65 +9,64 @@ using Volo.Abp.AspNetCore.ExceptionHandling; using Volo.Abp.DependencyInjection; using Volo.Abp.Http; -namespace Volo.Abp.AspNetCore.Components.Web.ExceptionHandling +namespace Volo.Abp.AspNetCore.Components.Web.ExceptionHandling; + +[Dependency(ReplaceServices = true)] +public class UserExceptionInformer : IUserExceptionInformer, IScopedDependency { - [Dependency(ReplaceServices = true)] - public class UserExceptionInformer : IUserExceptionInformer, IScopedDependency + public ILogger Logger { get; set; } + protected IUiMessageService MessageService { get; } + protected IExceptionToErrorInfoConverter ExceptionToErrorInfoConverter { get; } + + protected AbpExceptionHandlingOptions Options { get; } + + public UserExceptionInformer( + IUiMessageService messageService, + IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, + IOptions options) + { + MessageService = messageService; + ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter; + Options = options.Value; + Logger = NullLogger.Instance; + } + + public void Inform(UserExceptionInformerContext context) { - public ILogger Logger { get; set; } - protected IUiMessageService MessageService { get; } - protected IExceptionToErrorInfoConverter ExceptionToErrorInfoConverter { get; } + //TODO: Create sync versions of the MessageService APIs. - protected AbpExceptionHandlingOptions Options { get; } + var errorInfo = GetErrorInfo(context); - public UserExceptionInformer( - IUiMessageService messageService, - IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, - IOptions options) + if (errorInfo.Details.IsNullOrEmpty()) { - MessageService = messageService; - ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter; - Options = options.Value; - Logger = NullLogger.Instance; + MessageService.Error(errorInfo.Message); } - - public void Inform(UserExceptionInformerContext context) + else { - //TODO: Create sync versions of the MessageService APIs. + MessageService.Error(errorInfo.Details, errorInfo.Message); + } + } - var errorInfo = GetErrorInfo(context); + public async Task InformAsync(UserExceptionInformerContext context) + { + var errorInfo = GetErrorInfo(context); - if (errorInfo.Details.IsNullOrEmpty()) - { - MessageService.Error(errorInfo.Message); - } - else - { - MessageService.Error(errorInfo.Details, errorInfo.Message); - } + if (errorInfo.Details.IsNullOrEmpty()) + { + await MessageService.Error(errorInfo.Message); } - - public async Task InformAsync(UserExceptionInformerContext context) + else { - var errorInfo = GetErrorInfo(context); - - if (errorInfo.Details.IsNullOrEmpty()) - { - await MessageService.Error(errorInfo.Message); - } - else - { - await MessageService.Error(errorInfo.Details, errorInfo.Message); - } + await MessageService.Error(errorInfo.Details, errorInfo.Message); } + } - protected virtual RemoteServiceErrorInfo GetErrorInfo(UserExceptionInformerContext context) + protected virtual RemoteServiceErrorInfo GetErrorInfo(UserExceptionInformerContext context) + { + return ExceptionToErrorInfoConverter.Convert(context.Exception, options => { - return ExceptionToErrorInfoConverter.Convert(context.Exception, options => - { - options.SendExceptionsDetailsToClients = Options.SendExceptionsDetailsToClients; - options.SendStackTraceToClients = Options.SendStackTraceToClients; - }); - } + options.SendExceptionsDetailsToClients = Options.SendExceptionsDetailsToClients; + options.SendStackTraceToClients = Options.SendStackTraceToClients; + }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/EntityActions/EntityAction.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/EntityActions/EntityAction.cs index 61bea5c27a..2f800c7257 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/EntityActions/EntityAction.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/EntityActions/EntityAction.cs @@ -1,20 +1,19 @@ using System; using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Web.Extensibility.EntityActions +namespace Volo.Abp.AspNetCore.Components.Web.Extensibility.EntityActions; + +public class EntityAction : IEquatable { - public class EntityAction : IEquatable + public string Text { get; set; } + public Func Clicked { get; set; } + public Func ConfirmationMessage { get; set; } + public bool Primary { get; set; } + public object Color { get; set; } + public string Icon { get; set; } + public Func Visible { get; set; } + public bool Equals(EntityAction other) { - public string Text { get; set; } - public Func Clicked { get; set; } - public Func ConfirmationMessage { get; set; } - public bool Primary { get; set; } - public object Color { get; set; } - public string Icon { get; set; } - public Func Visible { get; set; } - public bool Equals(EntityAction other) - { - return string.Equals(Text, other?.Text, StringComparison.OrdinalIgnoreCase); - } + return string.Equals(Text, other?.Text, StringComparison.OrdinalIgnoreCase); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/EntityActions/EntityActionDictionary.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/EntityActions/EntityActionDictionary.cs index 36117abb37..0781870b69 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/EntityActions/EntityActionDictionary.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/EntityActions/EntityActionDictionary.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Components.Web.Extensibility.EntityActions +namespace Volo.Abp.AspNetCore.Components.Web.Extensibility.EntityActions; + +public class EntityActionDictionary : Dictionary> { - public class EntityActionDictionary : Dictionary> + public List Get() { - public List Get() - { - return this.GetOrAdd(typeof(T).FullName, () => new List()); - } + return this.GetOrAdd(typeof(T).FullName, () => new List()); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/ILookupApiRequestService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/ILookupApiRequestService.cs index 2351606306..f4b3e26e47 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/ILookupApiRequestService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/ILookupApiRequestService.cs @@ -1,10 +1,9 @@ using System.Threading.Tasks; using JetBrains.Annotations; -namespace Volo.Abp.AspNetCore.Components.Web.Extensibility +namespace Volo.Abp.AspNetCore.Components.Web.Extensibility; + +public interface ILookupApiRequestService { - public interface ILookupApiRequestService - { - Task SendAsync([NotNull]string url); - } -} \ No newline at end of file + Task SendAsync([NotNull] string url); +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/TableColumns/TableColumn.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/TableColumns/TableColumn.cs index 2121b8a128..9a6a40a17a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/TableColumns/TableColumn.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/TableColumns/TableColumn.cs @@ -4,25 +4,24 @@ using System.Collections.Generic; using System.Globalization; using Volo.Abp.AspNetCore.Components.Web.Extensibility.EntityActions; -namespace Volo.Abp.AspNetCore.Components.Web.Extensibility.TableColumns +namespace Volo.Abp.AspNetCore.Components.Web.Extensibility.TableColumns; + +public class TableColumn { - public class TableColumn - { - public string Title { get; set; } - public string Data { get; set; } - [CanBeNull] - public string DisplayFormat { get; set; } - public IFormatProvider DisplayFormatProvider { get; set; } = CultureInfo.CurrentCulture; - [CanBeNull] - public Type Component { get; set; } - public List Actions { get; set; } - [CanBeNull] - public Func ValueConverter { get; set; } - public bool Sortable { get; set; } + public string Title { get; set; } + public string Data { get; set; } + [CanBeNull] + public string DisplayFormat { get; set; } + public IFormatProvider DisplayFormatProvider { get; set; } = CultureInfo.CurrentCulture; + [CanBeNull] + public Type Component { get; set; } + public List Actions { get; set; } + [CanBeNull] + public Func ValueConverter { get; set; } + public bool Sortable { get; set; } - public TableColumn() - { - Actions = new List(); - } + public TableColumn() + { + Actions = new List(); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/TableColumns/TableColumnDictionary.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/TableColumns/TableColumnDictionary.cs index a68ad437d4..7eda70177c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/TableColumns/TableColumnDictionary.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Extensibility/TableColumns/TableColumnDictionary.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Components.Web.Extensibility.TableColumns +namespace Volo.Abp.AspNetCore.Components.Web.Extensibility.TableColumns; + +public class TableColumnDictionary : Dictionary> { - public class TableColumnDictionary : Dictionary> + public List Get() { - public List Get() - { - return this.GetOrAdd(typeof(T).FullName, () => new List()); - } + return this.GetOrAdd(typeof(T).FullName, () => new List()); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/IAbpUtilsService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/IAbpUtilsService.cs index a149691ba4..732ce11b4b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/IAbpUtilsService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/IAbpUtilsService.cs @@ -1,21 +1,20 @@ using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Web +namespace Volo.Abp.AspNetCore.Components.Web; + +public interface IAbpUtilsService { - public interface IAbpUtilsService - { - ValueTask AddClassToTagAsync(string tagName, string className); + ValueTask AddClassToTagAsync(string tagName, string className); - ValueTask RemoveClassFromTagAsync(string tagName, string className); + ValueTask RemoveClassFromTagAsync(string tagName, string className); - ValueTask HasClassOnTagAsync(string tagName, string className); + ValueTask HasClassOnTagAsync(string tagName, string className); - ValueTask ReplaceLinkHrefByIdAsync(string linkId, string hrefValue); + ValueTask ReplaceLinkHrefByIdAsync(string linkId, string hrefValue); - ValueTask ToggleFullscreenAsync(); + ValueTask ToggleFullscreenAsync(); - ValueTask RequestFullscreenAsync(); + ValueTask RequestFullscreenAsync(); - ValueTask ExitFullscreenAsync(); - } + ValueTask ExitFullscreenAsync(); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ICookieService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ICookieService.cs index 8f397bf554..afb124644e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ICookieService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ICookieService.cs @@ -1,11 +1,10 @@ using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Web +namespace Volo.Abp.AspNetCore.Components.Web; + +public interface ICookieService { - public interface ICookieService - { - public ValueTask SetAsync(string key, string value, CookieOptions options = null); - public ValueTask GetAsync(string key); - public ValueTask DeleteAsync(string key, string path = null); - } -} \ No newline at end of file + public ValueTask SetAsync(string key, string value, CookieOptions options = null); + public ValueTask GetAsync(string key); + public ValueTask DeleteAsync(string key, string path = null); +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/IServerUrlProvider.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/IServerUrlProvider.cs index fffec2f606..f5266a9f18 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/IServerUrlProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/IServerUrlProvider.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Web +namespace Volo.Abp.AspNetCore.Components.Web; + +public interface IServerUrlProvider { - public interface IServerUrlProvider - { - Task GetBaseUrlAsync(string remoteServiceName = null); - } -} \ No newline at end of file + Task GetBaseUrlAsync(string remoteServiceName = null); +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Messages/SimpleUiMessageService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Messages/SimpleUiMessageService.cs index cc82625b4c..647be65b41 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Messages/SimpleUiMessageService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Messages/SimpleUiMessageService.cs @@ -4,40 +4,39 @@ using Microsoft.JSInterop; using Volo.Abp.AspNetCore.Components.Messages; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web.Messages +namespace Volo.Abp.AspNetCore.Components.Web.Messages; + +public class SimpleUiMessageService : IUiMessageService, ITransientDependency { - public class SimpleUiMessageService : IUiMessageService, ITransientDependency + protected IJSRuntime JsRuntime { get; } + + public SimpleUiMessageService(IJSRuntime jsRuntime) + { + JsRuntime = jsRuntime; + } + + public async Task Info(string message, string title = null, Action options = null) + { + await JsRuntime.InvokeVoidAsync("alert", message); + } + + public async Task Success(string message, string title = null, Action options = null) + { + await JsRuntime.InvokeVoidAsync("alert", message); + } + + public async Task Warn(string message, string title = null, Action options = null) + { + await JsRuntime.InvokeVoidAsync("alert", message); + } + + public async Task Error(string message, string title = null, Action options = null) + { + await JsRuntime.InvokeVoidAsync("alert", message); + } + + public async Task Confirm(string message, string title = null, Action options = null) { - protected IJSRuntime JsRuntime { get; } - - public SimpleUiMessageService(IJSRuntime jsRuntime) - { - JsRuntime = jsRuntime; - } - - public async Task Info(string message, string title = null, Action options = null) - { - await JsRuntime.InvokeVoidAsync("alert", message); - } - - public async Task Success(string message, string title = null, Action options = null) - { - await JsRuntime.InvokeVoidAsync("alert", message); - } - - public async Task Warn(string message, string title = null, Action options = null) - { - await JsRuntime.InvokeVoidAsync("alert", message); - } - - public async Task Error(string message, string title = null, Action options = null) - { - await JsRuntime.InvokeVoidAsync("alert", message); - } - - public async Task Confirm(string message, string title = null, Action options = null) - { - return await JsRuntime.InvokeAsync("confirm", message); - } + return await JsRuntime.InvokeAsync("confirm", message); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs index 787bf5cb6e..bb053eac21 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs @@ -5,39 +5,38 @@ using Microsoft.AspNetCore.Components.Authorization; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Web.Security +namespace Volo.Abp.AspNetCore.Components.Web.Security; + +[ExposeServices( + typeof(AbpComponentsClaimsCache), + typeof(IAsyncInitialize) +)] +public class AbpComponentsClaimsCache : IScopedDependency, IAsyncInitialize { - [ExposeServices( - typeof(AbpComponentsClaimsCache), - typeof(IAsyncInitialize) - )] - public class AbpComponentsClaimsCache : IScopedDependency, IAsyncInitialize - { - public ClaimsPrincipal Principal { get; private set; } + public ClaimsPrincipal Principal { get; private set; } - [CanBeNull] - private readonly AuthenticationStateProvider _authenticationStateProvider; + [CanBeNull] + private readonly AuthenticationStateProvider _authenticationStateProvider; - public AbpComponentsClaimsCache( - IClientScopeServiceProviderAccessor serviceProviderAccessor) + public AbpComponentsClaimsCache( + IClientScopeServiceProviderAccessor serviceProviderAccessor) + { + _authenticationStateProvider = serviceProviderAccessor.ServiceProvider.GetService(); + if (_authenticationStateProvider != null) { - _authenticationStateProvider = serviceProviderAccessor.ServiceProvider.GetService(); - if (_authenticationStateProvider != null) + _authenticationStateProvider.AuthenticationStateChanged += async (task) => { - _authenticationStateProvider.AuthenticationStateChanged += async (task) => - { - Principal = (await task).User; - }; - } + Principal = (await task).User; + }; } + } - public virtual async Task InitializeAsync() + public virtual async Task InitializeAsync() + { + if (_authenticationStateProvider != null) { - if (_authenticationStateProvider != null) - { - var authenticationState = await _authenticationStateProvider.GetAuthenticationStateAsync(); - Principal = authenticationState.User; - } + var authenticationState = await _authenticationStateProvider.GetAuthenticationStateAsync(); + Principal = authenticationState.User; } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/AbpAspNetCoreComponentsWebAssemblyThemingModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/AbpAspNetCoreComponentsWebAssemblyThemingModule.cs index a19df06bc7..38eff974b8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/AbpAspNetCoreComponentsWebAssemblyThemingModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/AbpAspNetCoreComponentsWebAssemblyThemingModule.cs @@ -1,14 +1,13 @@ using Volo.Abp.AspNetCore.Components.Web.Theming; using Volo.Abp.Modularity; -namespace Volo.Abp.AspNetCore.Components.WebAssembly.Theming +namespace Volo.Abp.AspNetCore.Components.WebAssembly.Theming; + +[DependsOn( + typeof(AbpAspNetCoreComponentsWebThemingModule), + typeof(AbpAspNetCoreComponentsWebAssemblyModule) +)] +public class AbpAspNetCoreComponentsWebAssemblyThemingModule : AbpModule { - [DependsOn( - typeof(AbpAspNetCoreComponentsWebThemingModule), - typeof(AbpAspNetCoreComponentsWebAssemblyModule) - )] - public class AbpAspNetCoreComponentsWebAssemblyThemingModule : AbpModule - { - } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs index 69933bd95f..61f42e8222 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs @@ -1,31 +1,30 @@ using Volo.Abp.Bundling; -namespace Volo.Abp.AspNetCore.Components.WebAssembly.Theming +namespace Volo.Abp.AspNetCore.Components.WebAssembly.Theming; + +public class ComponentsComponentsBundleContributor : IBundleContributor { - public class ComponentsComponentsBundleContributor : IBundleContributor + public void AddScripts(BundleContext context) { - public void AddScripts(BundleContext context) - { - context.Add("_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"); - context.Add("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/abp.js"); - context.Add("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/lang-utils.js"); - } + context.Add("_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"); + context.Add("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/abp.js"); + context.Add("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/lang-utils.js"); + } - public void AddStyles(BundleContext context) + public void AddStyles(BundleContext context) + { + context.BundleDefinitions.Insert(0, new BundleDefinition + { + Source = "_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/bootstrap/css/bootstrap.min.css" + }); + context.BundleDefinitions.Insert(1, new BundleDefinition { - context.BundleDefinitions.Insert(0, new BundleDefinition - { - Source = "_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/bootstrap/css/bootstrap.min.css" - }); - context.BundleDefinitions.Insert(1, new BundleDefinition - { - Source = "_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/fontawesome/css/all.css" - }); + Source = "_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/fontawesome/css/all.css" + }); - context.Add("_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/flag-icon/css/flag-icon.css"); - context.Add("_content/Blazorise/blazorise.css"); - context.Add("_content/Blazorise.Bootstrap5/blazorise.bootstrap5.css"); - context.Add("_content/Blazorise.Snackbar/blazorise.snackbar.css"); - } + context.Add("_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/flag-icon/css/flag-icon.css"); + context.Add("_content/Blazorise/blazorise.css"); + context.Add("_content/Blazorise.Bootstrap5/blazorise.bootstrap5.css"); + context.Add("_content/Blazorise.Snackbar/blazorise.snackbar.css"); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs index f05feacae8..5c77dcff49 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs @@ -13,74 +13,97 @@ using Volo.Abp.AspNetCore.Mvc.Client; using Volo.Abp.DependencyInjection; using Volo.Abp.Modularity; -namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting +namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +public static class AbpWebAssemblyHostBuilderExtensions { - public static class AbpWebAssemblyHostBuilderExtensions + public async static Task AddApplicationAsync( + [NotNull] this WebAssemblyHostBuilder builder, + Action options) + where TStartupModule : IAbpModule { - public static IAbpApplicationWithExternalServiceProvider AddApplication( - [NotNull] this WebAssemblyHostBuilder builder, - Action options) - where TStartupModule : IAbpModule + Check.NotNull(builder, nameof(builder)); + + // Related this commit(https://github.com/dotnet/aspnetcore/commit/b99d805bc037fcac56afb79abeb7d5a43141c85e) + // Microsoft.AspNetCore.Blazor.BuildTools has been removed in net 5.0. + // This call may be removed when we find a suitable solution. + // System.Runtime.CompilerServices.AsyncStateMachineAttribute + Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(); + + builder.Services.AddSingleton(builder.Configuration); + builder.Services.AddSingleton(builder); + + var application = await builder.Services.AddApplicationAsync(opts => { - Check.NotNull(builder, nameof(builder)); + options?.Invoke(new AbpWebAssemblyApplicationCreationOptions(builder, opts)); + }); - // Related this commit(https://github.com/dotnet/aspnetcore/commit/b99d805bc037fcac56afb79abeb7d5a43141c85e) - // Microsoft.AspNetCore.Blazor.BuildTools has been removed in net 5.0. - // This call may be removed when we find a suitable solution. - // System.Runtime.CompilerServices.AsyncStateMachineAttribute - Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(); + return application; + } - builder.Services.AddSingleton(builder.Configuration); - builder.Services.AddSingleton(builder); + public static IAbpApplicationWithExternalServiceProvider AddApplication( + [NotNull] this WebAssemblyHostBuilder builder, + Action options) + where TStartupModule : IAbpModule + { + Check.NotNull(builder, nameof(builder)); - var application = builder.Services.AddApplication(opts => - { - options?.Invoke(new AbpWebAssemblyApplicationCreationOptions(builder, opts)); - }); + // Related this commit(https://github.com/dotnet/aspnetcore/commit/b99d805bc037fcac56afb79abeb7d5a43141c85e) + // Microsoft.AspNetCore.Blazor.BuildTools has been removed in net 5.0. + // This call may be removed when we find a suitable solution. + // System.Runtime.CompilerServices.AsyncStateMachineAttribute + Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(); - return application; - } + builder.Services.AddSingleton(builder.Configuration); + builder.Services.AddSingleton(builder); - public static async Task InitializeAsync( - [NotNull] this IAbpApplicationWithExternalServiceProvider application, - [NotNull] IServiceProvider serviceProvider) + var application = builder.Services.AddApplication(opts => { - Check.NotNull(application, nameof(application)); - Check.NotNull(serviceProvider, nameof(serviceProvider)); + options?.Invoke(new AbpWebAssemblyApplicationCreationOptions(builder, opts)); + }); - ((ComponentsClientScopeServiceProviderAccessor) serviceProvider - .GetRequiredService()).ServiceProvider = serviceProvider; + return application; + } - application.Initialize(serviceProvider); - await InitializeModulesAsync(serviceProvider); - await SetCurrentLanguageAsync(serviceProvider); + public async static Task InitializeApplicationAsync( + [NotNull] this IAbpApplicationWithExternalServiceProvider application, + [NotNull] IServiceProvider serviceProvider) + { + Check.NotNull(application, nameof(application)); + Check.NotNull(serviceProvider, nameof(serviceProvider)); + + ((ComponentsClientScopeServiceProviderAccessor)serviceProvider + .GetRequiredService()).ServiceProvider = serviceProvider; + + await application.InitializeAsync(serviceProvider); + await InitializeModulesAsync(serviceProvider); + await SetCurrentLanguageAsync(serviceProvider); + } + + private async static Task InitializeModulesAsync(IServiceProvider serviceProvider) + { + foreach (var service in serviceProvider.GetServices()) + { + await service.InitializeAsync(); } + } - private static async Task InitializeModulesAsync(IServiceProvider serviceProvider) + private async static Task SetCurrentLanguageAsync(IServiceProvider serviceProvider) + { + var configurationClient = serviceProvider.GetRequiredService(); + var utilsService = serviceProvider.GetRequiredService(); + var configuration = await configurationClient.GetAsync(); + var cultureName = configuration.Localization?.CurrentCulture?.CultureName; + if (!cultureName.IsNullOrEmpty()) { - foreach (var service in serviceProvider.GetServices()) - { - await service.InitializeAsync(); - } + var culture = new CultureInfo(cultureName); + CultureInfo.DefaultThreadCurrentCulture = culture; + CultureInfo.DefaultThreadCurrentUICulture = culture; } - private static async Task SetCurrentLanguageAsync(IServiceProvider serviceProvider) + if (CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft) { - var configurationClient = serviceProvider.GetRequiredService(); - var utilsService = serviceProvider.GetRequiredService(); - var configuration = await configurationClient.GetAsync(); - var cultureName = configuration.Localization?.CurrentCulture?.CultureName; - if (!cultureName.IsNullOrEmpty()) - { - var culture = new CultureInfo(cultureName); - CultureInfo.DefaultThreadCurrentCulture = culture; - CultureInfo.DefaultThreadCurrentUICulture = culture; - } - - if (CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft) - { - await utilsService.AddClassToTagAsync("body", "rtl"); - } + await utilsService.AddClassToTagAsync("body", "rtl"); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpWebAssemblyServiceCollectionExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpWebAssemblyServiceCollectionExtensions.cs index 2f541265d1..bbcf1d5a83 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpWebAssemblyServiceCollectionExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpWebAssemblyServiceCollectionExtensions.cs @@ -2,16 +2,15 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Volo.Abp; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class AbpWebAssemblyServiceCollectionExtensions { - public static class AbpWebAssemblyServiceCollectionExtensions + public static WebAssemblyHostBuilder GetHostBuilder( + [NotNull] this IServiceCollection services) { - public static WebAssemblyHostBuilder GetHostBuilder( - [NotNull] this IServiceCollection services) - { - Check.NotNull(services, nameof(services)); + Check.NotNull(services, nameof(services)); - return services.GetSingletonInstance(); - } + return services.GetSingletonInstance(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs index 170bd9c327..56dd8106a3 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs @@ -7,31 +7,30 @@ using Volo.Abp.Http.Client; using Volo.Abp.Modularity; using Volo.Abp.UI; -namespace Volo.Abp.AspNetCore.Components.WebAssembly +namespace Volo.Abp.AspNetCore.Components.WebAssembly; + +[DependsOn( + typeof(AbpAspNetCoreMvcClientCommonModule), + typeof(AbpUiModule), + typeof(AbpAspNetCoreComponentsWebModule) + )] +public class AbpAspNetCoreComponentsWebAssemblyModule : AbpModule { - [DependsOn( - typeof(AbpAspNetCoreMvcClientCommonModule), - typeof(AbpUiModule), - typeof(AbpAspNetCoreComponentsWebModule) - )] - public class AbpAspNetCoreComponentsWebAssemblyModule : AbpModule + public override void PreConfigureServices(ServiceConfigurationContext context) { - public override void PreConfigureServices(ServiceConfigurationContext context) + PreConfigure(options => { - PreConfigure(options => + options.ProxyClientBuildActions.Add((_, builder) => { - options.ProxyClientBuildActions.Add((_, builder) => - { - builder.AddHttpMessageHandler(); - }); + builder.AddHttpMessageHandler(); }); - } + }); + } - public override void ConfigureServices(ServiceConfigurationContext context) - { - context.Services - .GetHostBuilder().Logging - .AddProvider(new AbpExceptionHandlingLoggerProvider(context.Services)); - } + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services + .GetHostBuilder().Logging + .AddProvider(new AbpExceptionHandlingLoggerProvider(context.Services)); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpWebAssemblyApplicationCreationOptions.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpWebAssemblyApplicationCreationOptions.cs index 54fdf7edb4..6ffe5fe17e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpWebAssemblyApplicationCreationOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpWebAssemblyApplicationCreationOptions.cs @@ -1,19 +1,18 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -namespace Volo.Abp.AspNetCore.Components.WebAssembly +namespace Volo.Abp.AspNetCore.Components.WebAssembly; + +public class AbpWebAssemblyApplicationCreationOptions { - public class AbpWebAssemblyApplicationCreationOptions - { - public WebAssemblyHostBuilder HostBuilder { get; } + public WebAssemblyHostBuilder HostBuilder { get; } - public AbpApplicationCreationOptions ApplicationCreationOptions { get; } + public AbpApplicationCreationOptions ApplicationCreationOptions { get; } - public AbpWebAssemblyApplicationCreationOptions( - WebAssemblyHostBuilder hostBuilder, - AbpApplicationCreationOptions applicationCreationOptions) - { - HostBuilder = hostBuilder; - ApplicationCreationOptions = applicationCreationOptions; - } + public AbpWebAssemblyApplicationCreationOptions( + WebAssemblyHostBuilder hostBuilder, + AbpApplicationCreationOptions applicationCreationOptions) + { + HostBuilder = hostBuilder; + ApplicationCreationOptions = applicationCreationOptions; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/ApplicationConfigurationCache.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/ApplicationConfigurationCache.cs index ffbcf4e973..b55d8d8a56 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/ApplicationConfigurationCache.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/ApplicationConfigurationCache.cs @@ -1,20 +1,19 @@ using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.WebAssembly +namespace Volo.Abp.AspNetCore.Components.WebAssembly; + +public class ApplicationConfigurationCache : ISingletonDependency { - public class ApplicationConfigurationCache : ISingletonDependency - { - protected ApplicationConfigurationDto Configuration { get; set; } + protected ApplicationConfigurationDto Configuration { get; set; } - public virtual ApplicationConfigurationDto Get() - { - return Configuration; - } + public virtual ApplicationConfigurationDto Get() + { + return Configuration; + } - public void Set(ApplicationConfigurationDto configuration) - { - Configuration = configuration; - } + public void Set(ApplicationConfigurationDto configuration) + { + Configuration = configuration; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Extensibility/WebAssemblyLookupApiRequestService.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Extensibility/WebAssemblyLookupApiRequestService.cs index 594aa32bae..b8eadacdd6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Extensibility/WebAssemblyLookupApiRequestService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Extensibility/WebAssemblyLookupApiRequestService.cs @@ -11,57 +11,56 @@ using Volo.Abp.Http.Client; using Microsoft.Extensions.Options; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.Components.WebAssembly.Extensibility +namespace Volo.Abp.AspNetCore.Components.WebAssembly.Extensibility; + +public class WebAssemblyLookupApiRequestService : ILookupApiRequestService, ITransientDependency { - public class WebAssemblyLookupApiRequestService : ILookupApiRequestService, ITransientDependency + public IHttpClientFactory HttpClientFactory { get; } + public IRemoteServiceHttpClientAuthenticator HttpClientAuthenticator { get; } + public IRemoteServiceConfigurationProvider RemoteServiceConfigurationProvider { get; } + public ICurrentTenant CurrentTenant { get; } + + public WebAssemblyLookupApiRequestService(IHttpClientFactory httpClientFactory, + IRemoteServiceHttpClientAuthenticator httpClientAuthenticator, + ICurrentTenant currentTenant, + IRemoteServiceConfigurationProvider remoteServiceConfigurationProvider) + { + HttpClientFactory = httpClientFactory; + HttpClientAuthenticator = httpClientAuthenticator; + CurrentTenant = currentTenant; + RemoteServiceConfigurationProvider = remoteServiceConfigurationProvider; + } + + public async Task SendAsync(string url) { - public IHttpClientFactory HttpClientFactory { get; } - public IRemoteServiceHttpClientAuthenticator HttpClientAuthenticator { get; } - public IRemoteServiceConfigurationProvider RemoteServiceConfigurationProvider { get; } - public ICurrentTenant CurrentTenant { get; } + var client = HttpClientFactory.CreateClient(); + var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + AddHeaders(requestMessage); - public WebAssemblyLookupApiRequestService(IHttpClientFactory httpClientFactory, - IRemoteServiceHttpClientAuthenticator httpClientAuthenticator, - ICurrentTenant currentTenant, - IRemoteServiceConfigurationProvider remoteServiceConfigurationProvider) + var uri = new Uri(url, UriKind.RelativeOrAbsolute); + if (!uri.IsAbsoluteUri) { - HttpClientFactory = httpClientFactory; - HttpClientAuthenticator = httpClientAuthenticator; - CurrentTenant = currentTenant; - RemoteServiceConfigurationProvider = remoteServiceConfigurationProvider; + var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("Default"); + client.BaseAddress = new Uri(remoteServiceConfig.BaseUrl); + await HttpClientAuthenticator.Authenticate(new RemoteServiceHttpClientAuthenticateContext(client, requestMessage, new RemoteServiceConfiguration(remoteServiceConfig.BaseUrl), string.Empty)); } - public async Task SendAsync(string url) - { - var client = HttpClientFactory.CreateClient(); - var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - AddHeaders(requestMessage); + var response = await client.SendAsync(requestMessage); - var uri = new Uri(url, UriKind.RelativeOrAbsolute); - if (!uri.IsAbsoluteUri) - { - var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("Default"); - client.BaseAddress = new Uri(remoteServiceConfig.BaseUrl); - await HttpClientAuthenticator.Authenticate(new RemoteServiceHttpClientAuthenticateContext(client, requestMessage, new RemoteServiceConfiguration(remoteServiceConfig.BaseUrl), string.Empty)); - } + return await response.Content.ReadAsStringAsync(); + } - var response = await client.SendAsync(requestMessage); - - return await response.Content.ReadAsStringAsync(); - } - - protected virtual void AddHeaders(HttpRequestMessage requestMessage) + protected virtual void AddHeaders(HttpRequestMessage requestMessage) + { + if (CurrentTenant.Id.HasValue) { - if (CurrentTenant.Id.HasValue) - { - requestMessage.Headers.Add(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.Value.ToString()); - } + requestMessage.Headers.Add(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.Value.ToString()); + } - var currentCulture = CultureInfo.CurrentUICulture.Name ?? CultureInfo.CurrentCulture.Name; - if (!currentCulture.IsNullOrEmpty()) - { - requestMessage.Headers.AcceptLanguage.Add(new (currentCulture)); - } + var currentCulture = CultureInfo.CurrentUICulture.Name ?? CultureInfo.CurrentCulture.Name; + if (!currentCulture.IsNullOrEmpty()) + { + requestMessage.Headers.AcceptLanguage.Add(new(currentCulture)); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs index c7b659a859..4dbd188a47 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs @@ -5,63 +5,62 @@ using Volo.Abp.AspNetCore.Mvc.Client; using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.Components.WebAssembly +namespace Volo.Abp.AspNetCore.Components.WebAssembly; + +[ExposeServices( + typeof(WebAssemblyCachedApplicationConfigurationClient), + typeof(ICachedApplicationConfigurationClient), + typeof(IAsyncInitialize) + )] +public class WebAssemblyCachedApplicationConfigurationClient : ICachedApplicationConfigurationClient, ITransientDependency { - [ExposeServices( - typeof(WebAssemblyCachedApplicationConfigurationClient), - typeof(ICachedApplicationConfigurationClient), - typeof(IAsyncInitialize) - )] - public class WebAssemblyCachedApplicationConfigurationClient : ICachedApplicationConfigurationClient, ITransientDependency - { - protected AbpApplicationConfigurationClientProxy ApplicationConfigurationAppService { get; } + protected AbpApplicationConfigurationClientProxy ApplicationConfigurationAppService { get; } - protected ApplicationConfigurationCache Cache { get; } + protected ApplicationConfigurationCache Cache { get; } - protected ICurrentTenantAccessor CurrentTenantAccessor { get; } + protected ICurrentTenantAccessor CurrentTenantAccessor { get; } - public WebAssemblyCachedApplicationConfigurationClient( - AbpApplicationConfigurationClientProxy applicationConfigurationAppService, - ApplicationConfigurationCache cache, - ICurrentTenantAccessor currentTenantAccessor) - { - ApplicationConfigurationAppService = applicationConfigurationAppService; - Cache = cache; - CurrentTenantAccessor = currentTenantAccessor; - } + public WebAssemblyCachedApplicationConfigurationClient( + AbpApplicationConfigurationClientProxy applicationConfigurationAppService, + ApplicationConfigurationCache cache, + ICurrentTenantAccessor currentTenantAccessor) + { + ApplicationConfigurationAppService = applicationConfigurationAppService; + Cache = cache; + CurrentTenantAccessor = currentTenantAccessor; + } - public virtual async Task InitializeAsync() - { - var configurationDto = await ApplicationConfigurationAppService.GetAsync(); + public virtual async Task InitializeAsync() + { + var configurationDto = await ApplicationConfigurationAppService.GetAsync(); - Cache.Set(configurationDto); + Cache.Set(configurationDto); - CurrentTenantAccessor.Current = new BasicTenantInfo( - configurationDto.CurrentTenant.Id, - configurationDto.CurrentTenant.Name - ); - } + CurrentTenantAccessor.Current = new BasicTenantInfo( + configurationDto.CurrentTenant.Id, + configurationDto.CurrentTenant.Name + ); + } - public virtual Task GetAsync() - { - return Task.FromResult(GetConfigurationByChecking()); - } + public virtual Task GetAsync() + { + return Task.FromResult(GetConfigurationByChecking()); + } - public virtual ApplicationConfigurationDto Get() - { - return GetConfigurationByChecking(); - } + public virtual ApplicationConfigurationDto Get() + { + return GetConfigurationByChecking(); + } - private ApplicationConfigurationDto GetConfigurationByChecking() + private ApplicationConfigurationDto GetConfigurationByChecking() + { + var configuration = Cache.Get(); + if (configuration == null) { - var configuration = Cache.Get(); - if (configuration == null) - { - throw new AbpException( - $"{nameof(WebAssemblyCachedApplicationConfigurationClient)} should be initialized before using it."); - } - - return configuration; + throw new AbpException( + $"{nameof(WebAssemblyCachedApplicationConfigurationClient)} should be initialized before using it."); } + + return configuration; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentPrincipalAccessor.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentPrincipalAccessor.cs index 36438871c1..9f1eb5d0b9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentPrincipalAccessor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentPrincipalAccessor.cs @@ -4,21 +4,20 @@ using Volo.Abp.AspNetCore.Components.Web.Security; using Volo.Abp.DependencyInjection; using Volo.Abp.Security.Claims; -namespace Volo.Abp.AspNetCore.Components.WebAssembly +namespace Volo.Abp.AspNetCore.Components.WebAssembly; + +public class WebAssemblyCurrentPrincipalAccessor : CurrentPrincipalAccessorBase, ITransientDependency { - public class WebAssemblyCurrentPrincipalAccessor : CurrentPrincipalAccessorBase, ITransientDependency - { - protected AbpComponentsClaimsCache ClaimsCache { get; } + protected AbpComponentsClaimsCache ClaimsCache { get; } - public WebAssemblyCurrentPrincipalAccessor( - IClientScopeServiceProviderAccessor clientScopeServiceProviderAccessor) - { - ClaimsCache = clientScopeServiceProviderAccessor.ServiceProvider.GetRequiredService(); - } + public WebAssemblyCurrentPrincipalAccessor( + IClientScopeServiceProviderAccessor clientScopeServiceProviderAccessor) + { + ClaimsCache = clientScopeServiceProviderAccessor.ServiceProvider.GetRequiredService(); + } - protected override ClaimsPrincipal GetClaimsPrincipal() - { - return ClaimsCache.Principal; - } + protected override ClaimsPrincipal GetClaimsPrincipal() + { + return ClaimsCache.Principal; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentTenantAccessor.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentTenantAccessor.cs index eb0ee781ad..e24746f85a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentTenantAccessor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentTenantAccessor.cs @@ -1,11 +1,10 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.Components.WebAssembly +namespace Volo.Abp.AspNetCore.Components.WebAssembly; + +[Dependency(ReplaceServices = true)] +public class WebAssemblyCurrentTenantAccessor : ICurrentTenantAccessor, ISingletonDependency { - [Dependency(ReplaceServices = true)] - public class WebAssemblyCurrentTenantAccessor : ICurrentTenantAccessor, ISingletonDependency - { - public BasicTenantInfo Current { get; set; } - } + public BasicTenantInfo Current { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyServerUrlProvider.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyServerUrlProvider.cs index 477dba5418..2fb133a10d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyServerUrlProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyServerUrlProvider.cs @@ -4,26 +4,25 @@ using Volo.Abp.AspNetCore.Components.Web; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client; -namespace Volo.Abp.AspNetCore.Components.WebAssembly +namespace Volo.Abp.AspNetCore.Components.WebAssembly; + +[Dependency(ReplaceServices = true)] +public class WebAssemblyServerUrlProvider : IServerUrlProvider, ITransientDependency { - [Dependency(ReplaceServices = true)] - public class WebAssemblyServerUrlProvider : IServerUrlProvider, ITransientDependency + protected IRemoteServiceConfigurationProvider RemoteServiceConfigurationProvider { get; } + + public WebAssemblyServerUrlProvider( + IRemoteServiceConfigurationProvider remoteServiceConfigurationProvider) + { + RemoteServiceConfigurationProvider = remoteServiceConfigurationProvider; + } + + public async Task GetBaseUrlAsync(string remoteServiceName = null) { - protected IRemoteServiceConfigurationProvider RemoteServiceConfigurationProvider { get; } + var remoteServiceConfiguration = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync( + remoteServiceName ?? RemoteServiceConfigurationDictionary.DefaultName + ); - public WebAssemblyServerUrlProvider( - IRemoteServiceConfigurationProvider remoteServiceConfigurationProvider) - { - RemoteServiceConfigurationProvider = remoteServiceConfigurationProvider; - } - - public async Task GetBaseUrlAsync(string remoteServiceName = null) - { - var remoteServiceConfiguration = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync( - remoteServiceName ?? RemoteServiceConfigurationDictionary.DefaultName - ); - - return remoteServiceConfiguration.BaseUrl.EnsureEndsWith('/'); - } + return remoteServiceConfiguration.BaseUrl.EnsureEndsWith('/'); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj b/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj index 1b81b8e6d9..628661a184 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj @@ -15,7 +15,7 @@
    - + diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs index 08c7ed8e9f..b73432b38a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs @@ -6,20 +6,20 @@ using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.ObjectMapping; using Volo.Abp.Security; +using Volo.Abp.Timing; -namespace Volo.Abp.AspNetCore.Components +namespace Volo.Abp.AspNetCore.Components; + +[DependsOn( + typeof(AbpObjectMappingModule), + typeof(AbpSecurityModule), + typeof(AbpTimingModule) + )] +public class AbpAspNetCoreComponentsModule : AbpModule { - [DependsOn( - typeof(AbpObjectMappingModule), - typeof(AbpSecurityModule), - typeof(AbpLocalizationModule) - )] - public class AbpAspNetCoreComponentsModule : AbpModule + public override void PreConfigureServices(ServiceConfigurationContext context) { - public override void PreConfigureServices(ServiceConfigurationContext context) - { - DynamicProxyIgnoreTypes.Add(); - context.Services.AddConventionalRegistrar(new AbpWebAssemblyConventionalRegistrar()); - } + DynamicProxyIgnoreTypes.Add(); + context.Services.AddConventionalRegistrar(new AbpWebAssemblyConventionalRegistrar()); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs index 163b2217fa..66acd8f171 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs @@ -11,167 +11,168 @@ using Volo.Abp.AspNetCore.Components.ExceptionHandling; using Volo.Abp.AspNetCore.Components.Messages; using Volo.Abp.AspNetCore.Components.Notifications; using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; +using Volo.Abp.Timing; using Volo.Abp.Users; -namespace Volo.Abp.AspNetCore.Components +namespace Volo.Abp.AspNetCore.Components; + +public abstract class AbpComponentBase : OwningComponentBase { - public abstract class AbpComponentBase : OwningComponentBase - { - protected IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory); - private IStringLocalizerFactory _stringLocalizerFactory; + protected IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory); + private IStringLocalizerFactory _stringLocalizerFactory; - protected IStringLocalizer L - { - get + protected IStringLocalizer L { + get { + if (_localizer == null) { - if (_localizer == null) - { - _localizer = CreateLocalizer(); - } - - return _localizer; + _localizer = CreateLocalizer(); } + + return _localizer; } - private IStringLocalizer _localizer; + } + private IStringLocalizer _localizer; - protected Type LocalizationResource - { - get => _localizationResource; - set - { - _localizationResource = value; - _localizer = null; - } + protected Type LocalizationResource { + get => _localizationResource; + set { + _localizationResource = value; + _localizer = null; } - private Type _localizationResource = typeof(DefaultResource); + } + private Type _localizationResource = typeof(DefaultResource); - protected ILogger Logger => _lazyLogger.Value; - private Lazy _lazyLogger => new Lazy(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); + protected ILogger Logger => _lazyLogger.Value; + private Lazy _lazyLogger => new Lazy(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); - protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); - private ILoggerFactory _loggerFactory; + protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); + private ILoggerFactory _loggerFactory; - protected IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService); - private IAuthorizationService _authorizationService; + protected IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService); + private IAuthorizationService _authorizationService; - protected ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser); - private ICurrentUser _currentUser; - - protected IUiMessageService Message => LazyGetNonScopedRequiredService(ref _message); - private IUiMessageService _message; + protected ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser); + private ICurrentUser _currentUser; - protected IUiNotificationService Notify => LazyGetNonScopedRequiredService(ref _notify); - private IUiNotificationService _notify; - - protected IUserExceptionInformer UserExceptionInformer => LazyGetNonScopedRequiredService(ref _userExceptionInformer); - private IUserExceptionInformer _userExceptionInformer; + protected ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); + private ICurrentTenant _currentTenant; - protected IAlertManager AlertManager => LazyGetNonScopedRequiredService(ref _alertManager); - private IAlertManager _alertManager; + protected IUiMessageService Message => LazyGetNonScopedRequiredService(ref _message); + private IUiMessageService _message; - protected AlertList Alerts => AlertManager.Alerts; + protected IUiNotificationService Notify => LazyGetNonScopedRequiredService(ref _notify); + private IUiNotificationService _notify; - protected IObjectMapper ObjectMapper - { - get - { - if (_objectMapper != null) - { - return _objectMapper; - } - - if (ObjectMapperContext == null) - { - return LazyGetRequiredService(ref _objectMapper); - } - - return LazyGetRequiredService( - typeof(IObjectMapper<>).MakeGenericType(ObjectMapperContext), - ref _objectMapper - ); - } - } + protected IUserExceptionInformer UserExceptionInformer => LazyGetNonScopedRequiredService(ref _userExceptionInformer); + private IUserExceptionInformer _userExceptionInformer; - private IObjectMapper _objectMapper; + protected IAlertManager AlertManager => LazyGetNonScopedRequiredService(ref _alertManager); + private IAlertManager _alertManager; - protected Type ObjectMapperContext { get; set; } + protected IClock Clock => LazyGetNonScopedRequiredService(ref _clock); + private IClock _clock; - protected TService LazyGetRequiredService(ref TService reference) => LazyGetRequiredService(typeof(TService), ref reference); + protected AlertList Alerts => AlertManager.Alerts; - protected TRef LazyGetRequiredService(Type serviceType, ref TRef reference) - { - if (reference == null) + protected IObjectMapper ObjectMapper { + get { + if (_objectMapper != null) { - reference = (TRef)ScopedServices.GetRequiredService(serviceType); + return _objectMapper; } - return reference; - } - - protected TService LazyGetService(ref TService reference) => LazyGetService(typeof(TService), ref reference); - - protected TRef LazyGetService(Type serviceType, ref TRef reference) - { - if (reference == null) + if (ObjectMapperContext == null) { - reference = (TRef)ScopedServices.GetService(serviceType); + return LazyGetRequiredService(ref _objectMapper); } - return reference; + return LazyGetRequiredService( + typeof(IObjectMapper<>).MakeGenericType(ObjectMapperContext), + ref _objectMapper + ); } + } - protected TService LazyGetNonScopedRequiredService(ref TService reference) => LazyGetNonScopedRequiredService(typeof(TService), ref reference); + private IObjectMapper _objectMapper; - protected TRef LazyGetNonScopedRequiredService(Type serviceType, ref TRef reference) - { - if (reference == null) - { - reference = (TRef)NonScopedServices.GetRequiredService(serviceType); - } + protected Type ObjectMapperContext { get; set; } + + protected TService LazyGetRequiredService(ref TService reference) => LazyGetRequiredService(typeof(TService), ref reference); - return reference; + protected TRef LazyGetRequiredService(Type serviceType, ref TRef reference) + { + if (reference == null) + { + reference = (TRef)ScopedServices.GetRequiredService(serviceType); } - - protected TService LazyGetNonScopedService(ref TService reference) => LazyGetNonScopedService(typeof(TService), ref reference); - protected TRef LazyGetNonScopedService(Type serviceType, ref TRef reference) + return reference; + } + + protected TService LazyGetService(ref TService reference) => LazyGetService(typeof(TService), ref reference); + + protected TRef LazyGetService(Type serviceType, ref TRef reference) + { + if (reference == null) { - if (reference == null) - { - reference = (TRef)NonScopedServices.GetService(serviceType); - } + reference = (TRef)ScopedServices.GetService(serviceType); + } + + return reference; + } + + protected TService LazyGetNonScopedRequiredService(ref TService reference) => LazyGetNonScopedRequiredService(typeof(TService), ref reference); - return reference; + protected TRef LazyGetNonScopedRequiredService(Type serviceType, ref TRef reference) + { + if (reference == null) + { + reference = (TRef)NonScopedServices.GetRequiredService(serviceType); } - [Inject] - protected IServiceProvider NonScopedServices { get; set; } + return reference; + } + + protected TService LazyGetNonScopedService(ref TService reference) => LazyGetNonScopedService(typeof(TService), ref reference); - protected virtual IStringLocalizer CreateLocalizer() + protected TRef LazyGetNonScopedService(Type serviceType, ref TRef reference) + { + if (reference == null) { - if (LocalizationResource != null) - { - return StringLocalizerFactory.Create(LocalizationResource); - } + reference = (TRef)NonScopedServices.GetService(serviceType); + } - var localizer = StringLocalizerFactory.CreateDefaultOrNull(); - if (localizer == null) - { - throw new AbpException($"Set {nameof(LocalizationResource)} or define the default localization resource type (by configuring the {nameof(AbpLocalizationOptions)}.{nameof(AbpLocalizationOptions.DefaultResourceType)}) to be able to use the {nameof(L)} object!"); - } + return reference; + } - return localizer; + [Inject] + protected IServiceProvider NonScopedServices { get; set; } + + protected virtual IStringLocalizer CreateLocalizer() + { + if (LocalizationResource != null) + { + return StringLocalizerFactory.Create(LocalizationResource); } - - protected async Task HandleErrorAsync(Exception exception) + + var localizer = StringLocalizerFactory.CreateDefaultOrNull(); + if (localizer == null) { - Logger.LogException(exception); - await InvokeAsync(async () => - { - await UserExceptionInformer.InformAsync(new UserExceptionInformerContext(exception)); - StateHasChanged(); - }); + throw new AbpException($"Set {nameof(LocalizationResource)} or define the default localization resource type (by configuring the {nameof(AbpLocalizationOptions)}.{nameof(AbpLocalizationOptions.DefaultResourceType)}) to be able to use the {nameof(L)} object!"); } + + return localizer; + } + + protected async Task HandleErrorAsync(Exception exception) + { + Logger.LogException(exception); + await InvokeAsync(async () => + { + await UserExceptionInformer.InformAsync(new UserExceptionInformerContext(exception)); + StateHasChanged(); + }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertList.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertList.cs index 866095a9b9..6ea0031352 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertList.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertList.cs @@ -5,33 +5,32 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Alerts +namespace Volo.Abp.AspNetCore.Components.Alerts; + +public class AlertList : ObservableCollection { - public class AlertList : ObservableCollection + public void Add(AlertType type, string text, string title = null, bool dismissible = true) { - public void Add(AlertType type, string text, string title = null, bool dismissible = true) - { - Add(new AlertMessage(type, text, title, dismissible)); - } + Add(new AlertMessage(type, text, title, dismissible)); + } - public void Info(string text, string title = null, bool dismissible = true) - { - Add(new AlertMessage(AlertType.Info, text, title, dismissible)); - } + public void Info(string text, string title = null, bool dismissible = true) + { + Add(new AlertMessage(AlertType.Info, text, title, dismissible)); + } - public void Warning(string text, string title = null, bool dismissible = true) - { - Add(new AlertMessage(AlertType.Warning, text, title, dismissible)); - } + public void Warning(string text, string title = null, bool dismissible = true) + { + Add(new AlertMessage(AlertType.Warning, text, title, dismissible)); + } - public void Danger(string text, string title = null, bool dismissible = true) - { - Add(new AlertMessage(AlertType.Danger, text, title, dismissible)); - } + public void Danger(string text, string title = null, bool dismissible = true) + { + Add(new AlertMessage(AlertType.Danger, text, title, dismissible)); + } - public void Success(string text, string title = null, bool dismissible = true) - { - Add(new AlertMessage(AlertType.Success, text, title, dismissible)); - } + public void Success(string text, string title = null, bool dismissible = true) + { + Add(new AlertMessage(AlertType.Success, text, title, dismissible)); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertMessage.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertMessage.cs index 1b6a7dd21d..7a38e422a0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertMessage.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertMessage.cs @@ -1,30 +1,28 @@ using JetBrains.Annotations; -namespace Volo.Abp.AspNetCore.Components.Alerts +namespace Volo.Abp.AspNetCore.Components.Alerts; + +public class AlertMessage { - public class AlertMessage - { - [NotNull] - public string Text - { - get => _text; - set => _text = Check.NotNullOrWhiteSpace(value, nameof(value)); - } - private string _text; + [NotNull] + public string Text { + get => _text; + set => _text = Check.NotNullOrWhiteSpace(value, nameof(value)); + } + private string _text; - public AlertType Type { get; set; } + public AlertType Type { get; set; } - [CanBeNull] - public string Title { get; set; } + [CanBeNull] + public string Title { get; set; } - public bool Dismissible { get; set; } + public bool Dismissible { get; set; } - public AlertMessage(AlertType type, [NotNull] string text, string title = null, bool dismissible = true) - { - Type = type; - Text = Check.NotNullOrWhiteSpace(text, nameof(text)); - Title = title; - Dismissible = dismissible; - } + public AlertMessage(AlertType type, [NotNull] string text, string title = null, bool dismissible = true) + { + Type = type; + Text = Check.NotNullOrWhiteSpace(text, nameof(text)); + Title = title; + Dismissible = dismissible; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertType.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertType.cs index 4703158b12..6149f9bb24 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertType.cs @@ -4,18 +4,17 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Alerts +namespace Volo.Abp.AspNetCore.Components.Alerts; + +public enum AlertType { - public enum AlertType - { - Default, - Primary, - Secondary, - Success, - Danger, - Warning, - Info, - Light, - Dark - } + Default, + Primary, + Secondary, + Success, + Danger, + Warning, + Info, + Light, + Dark } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/IAlertManager.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/IAlertManager.cs index 542b576b01..0e6344baaf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/IAlertManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/IAlertManager.cs @@ -4,10 +4,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Alerts +namespace Volo.Abp.AspNetCore.Components.Alerts; + +public interface IAlertManager { - public interface IAlertManager - { - AlertList Alerts { get; } - } + AlertList Alerts { get; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/DependencyInjection/AbpWebAssemblyConventionalRegistrar.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/DependencyInjection/AbpWebAssemblyConventionalRegistrar.cs index 3929f6c4cc..776c80108a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/DependencyInjection/AbpWebAssemblyConventionalRegistrar.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/DependencyInjection/AbpWebAssemblyConventionalRegistrar.cs @@ -3,23 +3,22 @@ using Microsoft.AspNetCore.Components; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.DependencyInjection +namespace Volo.Abp.AspNetCore.Components.DependencyInjection; + +public class AbpWebAssemblyConventionalRegistrar : DefaultConventionalRegistrar { - public class AbpWebAssemblyConventionalRegistrar : DefaultConventionalRegistrar + protected override bool IsConventionalRegistrationDisabled(Type type) { - protected override bool IsConventionalRegistrationDisabled(Type type) - { - return !IsComponent(type) || base.IsConventionalRegistrationDisabled(type); - } + return !IsComponent(type) || base.IsConventionalRegistrationDisabled(type); + } - private static bool IsComponent(Type type) - { - return typeof(ComponentBase).IsAssignableFrom(type); - } + private static bool IsComponent(Type type) + { + return typeof(ComponentBase).IsAssignableFrom(type); + } - protected override ServiceLifetime? GetDefaultLifeTimeOrNull(Type type) - { - return ServiceLifetime.Transient; - } + protected override ServiceLifetime? GetDefaultLifeTimeOrNull(Type type) + { + return ServiceLifetime.Transient; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/DependencyInjection/ServiceProviderComponentActivator.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/DependencyInjection/ServiceProviderComponentActivator.cs index 3841f48c4a..001659395e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/DependencyInjection/ServiceProviderComponentActivator.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/DependencyInjection/ServiceProviderComponentActivator.cs @@ -1,32 +1,31 @@ using System; using Microsoft.AspNetCore.Components; -namespace Volo.Abp.AspNetCore.Components.DependencyInjection +namespace Volo.Abp.AspNetCore.Components.DependencyInjection; + +public class ServiceProviderComponentActivator : IComponentActivator { - public class ServiceProviderComponentActivator : IComponentActivator + public IServiceProvider ServiceProvider { get; } + + public ServiceProviderComponentActivator(IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + } + + public IComponent CreateInstance(Type componentType) { - public IServiceProvider ServiceProvider { get; } + var instance = ServiceProvider.GetService(componentType); - public ServiceProviderComponentActivator(IServiceProvider serviceProvider) + if (instance == null) { - ServiceProvider = serviceProvider; + instance = Activator.CreateInstance(componentType); } - public IComponent CreateInstance(Type componentType) + if (!(instance is IComponent component)) { - var instance = ServiceProvider.GetService(componentType); - - if (instance == null) - { - instance = Activator.CreateInstance(componentType); - } - - if (!(instance is IComponent component)) - { - throw new ArgumentException($"The type {componentType.FullName} does not implement {nameof(IComponent)}.", nameof(componentType)); - } - - return component; + throw new ArgumentException($"The type {componentType.FullName} does not implement {nameof(IComponent)}.", nameof(componentType)); } + + return component; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/IUserExceptionInformer.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/IUserExceptionInformer.cs index aaab5057eb..26b04b805e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/IUserExceptionInformer.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/IUserExceptionInformer.cs @@ -1,11 +1,10 @@ using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.ExceptionHandling +namespace Volo.Abp.AspNetCore.Components.ExceptionHandling; + +public interface IUserExceptionInformer { - public interface IUserExceptionInformer - { - void Inform(UserExceptionInformerContext context); - - Task InformAsync(UserExceptionInformerContext context); - } + void Inform(UserExceptionInformerContext context); + + Task InformAsync(UserExceptionInformerContext context); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/NullUserExceptionInformer.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/NullUserExceptionInformer.cs index 56522bef01..3a23390f51 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/NullUserExceptionInformer.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/NullUserExceptionInformer.cs @@ -1,18 +1,17 @@ using System.Threading.Tasks; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.ExceptionHandling +namespace Volo.Abp.AspNetCore.Components.ExceptionHandling; + +public class NullUserExceptionInformer : IUserExceptionInformer, ISingletonDependency { - public class NullUserExceptionInformer : IUserExceptionInformer, ISingletonDependency + public void Inform(UserExceptionInformerContext context) { - public void Inform(UserExceptionInformerContext context) - { - - } - public Task InformAsync(UserExceptionInformerContext context) - { - return Task.CompletedTask; - } } -} \ No newline at end of file + + public Task InformAsync(UserExceptionInformerContext context) + { + return Task.CompletedTask; + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/UserExceptionInformerContext.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/UserExceptionInformerContext.cs index d174a98488..978e6b82e9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/UserExceptionInformerContext.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/ExceptionHandling/UserExceptionInformerContext.cs @@ -1,16 +1,15 @@ using System; using JetBrains.Annotations; -namespace Volo.Abp.AspNetCore.Components.ExceptionHandling +namespace Volo.Abp.AspNetCore.Components.ExceptionHandling; + +public class UserExceptionInformerContext { - public class UserExceptionInformerContext - { - [NotNull] - public Exception Exception { get; } + [NotNull] + public Exception Exception { get; } - public UserExceptionInformerContext(Exception exception) - { - Exception = exception; - } + public UserExceptionInformerContext(Exception exception) + { + Exception = exception; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/IUiMessageService.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/IUiMessageService.cs index 4f43cb90d6..86e878d2a9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/IUiMessageService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/IUiMessageService.cs @@ -1,18 +1,17 @@ using System; using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Messages +namespace Volo.Abp.AspNetCore.Components.Messages; + +public interface IUiMessageService { - public interface IUiMessageService - { - Task Info(string message, string title = null, Action options = null); + Task Info(string message, string title = null, Action options = null); - Task Success(string message, string title = null, Action options = null); + Task Success(string message, string title = null, Action options = null); - Task Warn(string message, string title = null, Action options = null); + Task Warn(string message, string title = null, Action options = null); - Task Error(string message, string title = null, Action options = null); + Task Error(string message, string title = null, Action options = null); - Task Confirm(string message, string title = null, Action options = null); - } + Task Confirm(string message, string title = null, Action options = null); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageEventArgs.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageEventArgs.cs index 51ef8ed558..4d73fb2812 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageEventArgs.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageEventArgs.cs @@ -1,35 +1,34 @@ using System; using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Messages +namespace Volo.Abp.AspNetCore.Components.Messages; + +public class UiMessageEventArgs : EventArgs { - public class UiMessageEventArgs : EventArgs + public UiMessageEventArgs(UiMessageType messageType, string message, string title, UiMessageOptions options) { - public UiMessageEventArgs(UiMessageType messageType, string message, string title, UiMessageOptions options) - { - MessageType = messageType; - Message = message; - Title = title; - Options = options; - } + MessageType = messageType; + Message = message; + Title = title; + Options = options; + } - public UiMessageEventArgs(UiMessageType messageType, string message, string title, UiMessageOptions options, TaskCompletionSource callback) - { - MessageType = messageType; - Message = message; - Title = title; - Options = options; - Callback = callback; - } + public UiMessageEventArgs(UiMessageType messageType, string message, string title, UiMessageOptions options, TaskCompletionSource callback) + { + MessageType = messageType; + Message = message; + Title = title; + Options = options; + Callback = callback; + } - public UiMessageType MessageType { get; set; } + public UiMessageType MessageType { get; set; } - public string Message { get; } + public string Message { get; } - public string Title { get; } + public string Title { get; } - public UiMessageOptions Options { get; } + public UiMessageOptions Options { get; } - public TaskCompletionSource Callback { get; } - } + public TaskCompletionSource Callback { get; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageOptions.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageOptions.cs index 9da4bcad60..5cb8e56eee 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageOptions.cs @@ -1,53 +1,52 @@ -namespace Volo.Abp.AspNetCore.Components.Messages +namespace Volo.Abp.AspNetCore.Components.Messages; + +/// +/// Options to override message dialog appearance. +/// +public class UiMessageOptions { /// - /// Options to override message dialog appearance. - /// - public class UiMessageOptions - { - /// - /// If true, the message dialogue will be centered on the screen. - /// - public bool CenterMessage { get; set; } - - /// - /// If true, the message dialogue will show the large icon for the current message type. - /// - public bool ShowMessageIcon { get; set; } - - /// - /// Overrides the build-in message icon. - /// - public object MessageIcon { get; set; } - - /// - /// Custom text for the Ok button. - /// - public string OkButtonText { get; set; } - - /// - /// Custom icon for the Ok button. - /// - public object OkButtonIcon { get; set; } - - /// - /// Custom text for the Confirmation button. - /// - public string ConfirmButtonText { get; set; } - - /// - /// Custom icon for the Confirmation button. - /// - public object ConfirmButtonIcon { get; set; } - - /// - /// Custom text for the Cancel button. - /// - public string CancelButtonText { get; set; } - - /// - /// Custom icon for the Cancel button. - /// - public object CancelButtonIcon { get; set; } - } + /// If true, the message dialogue will be centered on the screen. + /// + public bool CenterMessage { get; set; } + + /// + /// If true, the message dialogue will show the large icon for the current message type. + /// + public bool ShowMessageIcon { get; set; } + + /// + /// Overrides the build-in message icon. + /// + public object MessageIcon { get; set; } + + /// + /// Custom text for the Ok button. + /// + public string OkButtonText { get; set; } + + /// + /// Custom icon for the Ok button. + /// + public object OkButtonIcon { get; set; } + + /// + /// Custom text for the Confirmation button. + /// + public string ConfirmButtonText { get; set; } + + /// + /// Custom icon for the Confirmation button. + /// + public object ConfirmButtonIcon { get; set; } + + /// + /// Custom text for the Cancel button. + /// + public string CancelButtonText { get; set; } + + /// + /// Custom icon for the Cancel button. + /// + public object CancelButtonIcon { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageType.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageType.cs index 677297055e..0f8264ecd1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Messages/UiMessageType.cs @@ -1,14 +1,13 @@ -namespace Volo.Abp.AspNetCore.Components.Messages +namespace Volo.Abp.AspNetCore.Components.Messages; + +/// +/// Defines the possible ui message types with predefined actions. +/// +public enum UiMessageType { - /// - /// Defines the possible ui message types with predefined actions. - /// - public enum UiMessageType - { - Info, - Success, - Warning, - Error, - Confirmation, - } + Info, + Success, + Warning, + Error, + Confirmation, } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/IUiNotificationService.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/IUiNotificationService.cs index dfbde9b1eb..394fdec2a7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/IUiNotificationService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/IUiNotificationService.cs @@ -1,16 +1,15 @@ using System; using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Notifications +namespace Volo.Abp.AspNetCore.Components.Notifications; + +public interface IUiNotificationService { - public interface IUiNotificationService - { - Task Info(string message, string title = null, Action options = null); - - Task Success(string message, string title = null, Action options = null); - - Task Warn(string message, string title = null, Action options = null); - - Task Error(string message, string title = null, Action options = null); - } + Task Info(string message, string title = null, Action options = null); + + Task Success(string message, string title = null, Action options = null); + + Task Warn(string message, string title = null, Action options = null); + + Task Error(string message, string title = null, Action options = null); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/NullUiNotificationService.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/NullUiNotificationService.cs index ed8217348e..6c47586325 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/NullUiNotificationService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/NullUiNotificationService.cs @@ -2,27 +2,26 @@ using System.Threading.Tasks; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Notifications +namespace Volo.Abp.AspNetCore.Components.Notifications; + +public class NullUiNotificationService : IUiNotificationService, ITransientDependency { - public class NullUiNotificationService : IUiNotificationService, ITransientDependency + public Task Info(string message, string title = null, Action options = null) { - public Task Info(string message, string title = null, Action options = null) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public Task Success(string message, string title = null, Action options = null) - { - return Task.CompletedTask; - } + public Task Success(string message, string title = null, Action options = null) + { + return Task.CompletedTask; + } - public Task Warn(string message, string title = null, Action options = null) - { - return Task.CompletedTask; - } - public Task Error(string message, string title = null, Action options = null) - { - return Task.CompletedTask; - } + public Task Warn(string message, string title = null, Action options = null) + { + return Task.CompletedTask; + } + public Task Error(string message, string title = null, Action options = null) + { + return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationEventArgs.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationEventArgs.cs index 30d497d2ab..554450d025 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationEventArgs.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationEventArgs.cs @@ -1,23 +1,22 @@ using System; -namespace Volo.Abp.AspNetCore.Components.Notifications +namespace Volo.Abp.AspNetCore.Components.Notifications; + +public class UiNotificationEventArgs : EventArgs { - public class UiNotificationEventArgs : EventArgs + public UiNotificationEventArgs(UiNotificationType notificationType, string message, string title, UiNotificationOptions options) { - public UiNotificationEventArgs(UiNotificationType notificationType, string message, string title, UiNotificationOptions options) - { - NotificationType = notificationType; - Message = message; - Title = title; - Options = options; - } + NotificationType = notificationType; + Message = message; + Title = title; + Options = options; + } - public UiNotificationType NotificationType { get; set; } + public UiNotificationType NotificationType { get; set; } - public string Message { get; } + public string Message { get; } - public string Title { get; } + public string Title { get; } - public UiNotificationOptions Options { get; } - } + public UiNotificationOptions Options { get; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationOptions.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationOptions.cs index eb1c5d7102..c3f6a74fa5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationOptions.cs @@ -1,20 +1,19 @@ using Volo.Abp.Localization; -namespace Volo.Abp.AspNetCore.Components.Notifications +namespace Volo.Abp.AspNetCore.Components.Notifications; + +/// +/// Options to override notification appearance. +/// +public class UiNotificationOptions { /// - /// Options to override notification appearance. + /// Custom text for the Ok button. /// - public class UiNotificationOptions - { - /// - /// Custom text for the Ok button. - /// - public ILocalizableString OkButtonText { get; set; } + public ILocalizableString OkButtonText { get; set; } - /// - /// Custom icon for the Ok button. - /// - public object OkButtonIcon { get; set; } - } + /// + /// Custom icon for the Ok button. + /// + public object OkButtonIcon { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationType.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationType.cs index 3547d451e9..bd31445558 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Notifications/UiNotificationType.cs @@ -1,10 +1,9 @@ -namespace Volo.Abp.AspNetCore.Components.Notifications +namespace Volo.Abp.AspNetCore.Components.Notifications; + +public enum UiNotificationType { - public enum UiNotificationType - { - Info, - Success, - Warning, - Error, - } + Info, + Success, + Warning, + Error, } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/IUiPageProgressService.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/IUiPageProgressService.cs index fa9781e2d9..156c763722 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/IUiPageProgressService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/IUiPageProgressService.cs @@ -1,21 +1,20 @@ using System; using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Components.Progression +namespace Volo.Abp.AspNetCore.Components.Progression; + +public interface IUiPageProgressService { - public interface IUiPageProgressService - { - /// - /// An event raised after the notification is received. - /// - public event EventHandler ProgressChanged; + /// + /// An event raised after the notification is received. + /// + public event EventHandler ProgressChanged; - /// - /// Sets the progress percentage. - /// - /// Value of the progress from 0 to 100, or null for indeterminate progress. - /// Additional options. - /// Awaitable task. - Task Go(int? percentage, Action options = null); - } + /// + /// Sets the progress percentage. + /// + /// Value of the progress from 0 to 100, or null for indeterminate progress. + /// Additional options. + /// Awaitable task. + Task Go(int? percentage, Action options = null); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/NullUiPageProgressService.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/NullUiPageProgressService.cs index 5c375e2b40..2277122418 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/NullUiPageProgressService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/NullUiPageProgressService.cs @@ -2,15 +2,14 @@ using System.Threading.Tasks; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Components.Progression +namespace Volo.Abp.AspNetCore.Components.Progression; + +public class NullUiPageProgressService : IUiPageProgressService, ISingletonDependency { - public class NullUiPageProgressService : IUiPageProgressService, ISingletonDependency - { - public event EventHandler ProgressChanged; + public event EventHandler ProgressChanged; - public Task Go(int? percentage, Action options = null) - { - return Task.CompletedTask; - } + public Task Go(int? percentage, Action options = null) + { + return Task.CompletedTask; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressEventArgs.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressEventArgs.cs index 74af5cb971..adb4b73a64 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressEventArgs.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressEventArgs.cs @@ -1,17 +1,16 @@ using System; -namespace Volo.Abp.AspNetCore.Components.Progression +namespace Volo.Abp.AspNetCore.Components.Progression; + +public class UiPageProgressEventArgs : EventArgs { - public class UiPageProgressEventArgs : EventArgs + public UiPageProgressEventArgs(int? percentage, UiPageProgressOptions options) { - public UiPageProgressEventArgs(int? percentage, UiPageProgressOptions options) - { - Percentage = percentage; - Options = options; - } + Percentage = percentage; + Options = options; + } - public int? Percentage { get; } + public int? Percentage { get; } - public UiPageProgressOptions Options { get; } - } + public UiPageProgressOptions Options { get; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressOptions.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressOptions.cs index 0ce243552d..d747e60d03 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressOptions.cs @@ -1,13 +1,12 @@ -namespace Volo.Abp.AspNetCore.Components.Progression +namespace Volo.Abp.AspNetCore.Components.Progression; + +/// +/// Options to override page progress appearance. +/// +public class UiPageProgressOptions { /// - /// Options to override page progress appearance. + /// Type or color, of the page progress. /// - public class UiPageProgressOptions - { - /// - /// Type or color, of the page progress. - /// - public UiPageProgressType Type { get; set; } - } + public UiPageProgressType Type { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressType.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressType.cs index db1d00932d..fe9c02b98e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressType.cs @@ -1,11 +1,10 @@ -namespace Volo.Abp.AspNetCore.Components.Progression +namespace Volo.Abp.AspNetCore.Components.Progression; + +public enum UiPageProgressType { - public enum UiPageProgressType - { - Default, - Info, - Success, - Warning, - Error, - } + Default, + Info, + Success, + Warning, + Error, } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Microsoft/AspNetCore/Builder/AbpAspNetCoreMultiTenancyApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Microsoft/AspNetCore/Builder/AbpAspNetCoreMultiTenancyApplicationBuilderExtensions.cs index afc05f9233..ceb872e5c3 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Microsoft/AspNetCore/Builder/AbpAspNetCoreMultiTenancyApplicationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Microsoft/AspNetCore/Builder/AbpAspNetCoreMultiTenancyApplicationBuilderExtensions.cs @@ -1,13 +1,12 @@ using Volo.Abp.AspNetCore.MultiTenancy; -namespace Microsoft.AspNetCore.Builder +namespace Microsoft.AspNetCore.Builder; + +public static class AbpAspNetCoreMultiTenancyApplicationBuilderExtensions { - public static class AbpAspNetCoreMultiTenancyApplicationBuilderExtensions + public static IApplicationBuilder UseMultiTenancy(this IApplicationBuilder app) { - public static IApplicationBuilder UseMultiTenancy(this IApplicationBuilder app) - { - return app - .UseMiddleware(); - } + return app + .UseMiddleware(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs index 1d0268556f..ad36cf0f11 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs @@ -2,24 +2,23 @@ using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +[DependsOn( + typeof(AbpMultiTenancyModule), + typeof(AbpAspNetCoreModule) + )] +public class AbpAspNetCoreMultiTenancyModule : AbpModule { - [DependsOn( - typeof(AbpMultiTenancyModule), - typeof(AbpAspNetCoreModule) - )] - public class AbpAspNetCoreMultiTenancyModule : AbpModule + public override void ConfigureServices(ServiceConfigurationContext context) { - public override void ConfigureServices(ServiceConfigurationContext context) + Configure(options => { - Configure(options => - { - options.TenantResolvers.Add(new QueryStringTenantResolveContributor()); - options.TenantResolvers.Add(new FormTenantResolveContributor()); - options.TenantResolvers.Add(new RouteTenantResolveContributor()); - options.TenantResolvers.Add(new HeaderTenantResolveContributor()); - options.TenantResolvers.Add(new CookieTenantResolveContributor()); - }); - } + options.TenantResolvers.Add(new QueryStringTenantResolveContributor()); + options.TenantResolvers.Add(new FormTenantResolveContributor()); + options.TenantResolvers.Add(new RouteTenantResolveContributor()); + options.TenantResolvers.Add(new HeaderTenantResolveContributor()); + options.TenantResolvers.Add(new CookieTenantResolveContributor()); + }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyOptions.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyOptions.cs index b2ecf708c5..4f89ef7b97 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyOptions.cs @@ -5,35 +5,34 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +public class AbpAspNetCoreMultiTenancyOptions { - public class AbpAspNetCoreMultiTenancyOptions - { - /// - /// Default: . - /// - public string TenantKey { get; set; } + /// + /// Default: . + /// + public string TenantKey { get; set; } - public Func MultiTenancyMiddlewareErrorPageBuilder { get; set; } + public Func MultiTenancyMiddlewareErrorPageBuilder { get; set; } - public AbpAspNetCoreMultiTenancyOptions() + public AbpAspNetCoreMultiTenancyOptions() + { + TenantKey = TenantResolverConsts.DefaultTenantKey; + MultiTenancyMiddlewareErrorPageBuilder = async (context, exception) => { - TenantKey = TenantResolverConsts.DefaultTenantKey; - MultiTenancyMiddlewareErrorPageBuilder = async (context, exception)=> - { - context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;; - context.Response.ContentType = "text/html"; + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; ; + context.Response.ContentType = "text/html"; - var message = exception.Message; - var details = exception is BusinessException businessException ? businessException.Details : string.Empty; + var message = exception.Message; + var details = exception is BusinessException businessException ? businessException.Details : string.Empty; - await context.Response.WriteAsync($"\r\n"); - await context.Response.WriteAsync($"

    {message}

    {details}
    \r\n"); - await context.Response.WriteAsync("\r\n"); + await context.Response.WriteAsync($"\r\n"); + await context.Response.WriteAsync($"

    {message}

    {details}
    \r\n"); + await context.Response.WriteAsync("\r\n"); // Note the 500 spaces are to work around an IE 'feature' await context.Response.WriteAsync(new string(' ', 500)); - }; - } + }; } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpMultiTenancyCookieHelper.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpMultiTenancyCookieHelper.cs index 2c2b2de0e9..d4a91f3b12 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpMultiTenancyCookieHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpMultiTenancyCookieHelper.cs @@ -1,33 +1,32 @@ using System; using Microsoft.AspNetCore.Http; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +public static class AbpMultiTenancyCookieHelper { - public static class AbpMultiTenancyCookieHelper + public static void SetTenantCookie( + HttpContext context, + Guid? tenantId, + string tenantKey) { - public static void SetTenantCookie( - HttpContext context, - Guid? tenantId, - string tenantKey) + if (tenantId != null) + { + context.Response.Cookies.Append( + tenantKey, + tenantId.ToString(), + new CookieOptions + { + Path = "/", + HttpOnly = false, + IsEssential = true, + Expires = DateTimeOffset.Now.AddYears(10) + } + ); + } + else { - if (tenantId != null) - { - context.Response.Cookies.Append( - tenantKey, - tenantId.ToString(), - new CookieOptions - { - Path = "/", - HttpOnly = false, - IsEssential = true, - Expires = DateTimeOffset.Now.AddYears(10) - } - ); - } - else - { - context.Response.Cookies.Delete(tenantKey); - } + context.Response.Cookies.Delete(tenantKey); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolveContributor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolveContributor.cs index dd7ca5685e..1eabd3ebfb 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolveContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolveContributor.cs @@ -2,17 +2,16 @@ using Microsoft.AspNetCore.Http; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +public class CookieTenantResolveContributor : HttpTenantResolveContributorBase { - public class CookieTenantResolveContributor : HttpTenantResolveContributorBase - { - public const string ContributorName = "Cookie"; + public const string ContributorName = "Cookie"; - public override string Name => ContributorName; + public override string Name => ContributorName; - protected override Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) - { - return Task.FromResult(httpContext.Request.Cookies[context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey]); - } + protected override Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) + { + return Task.FromResult(httpContext.Request.Cookies[context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey]); } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributor.cs index 255e0ac89b..78f92ac20e 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributor.cs @@ -4,38 +4,37 @@ using Microsoft.AspNetCore.Http; using Volo.Abp.MultiTenancy; using Volo.Abp.Text.Formatting; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +//TODO: Create a better domain format. We can accept regex for example. + +public class DomainTenantResolveContributor : HttpTenantResolveContributorBase { - //TODO: Create a better domain format. We can accept regex for example. + public const string ContributorName = "Domain"; - public class DomainTenantResolveContributor : HttpTenantResolveContributorBase - { - public const string ContributorName = "Domain"; + public override string Name => ContributorName; - public override string Name => ContributorName; + private static readonly string[] ProtocolPrefixes = { "http://", "https://" }; - private static readonly string[] ProtocolPrefixes = { "http://", "https://" }; + private readonly string _domainFormat; - private readonly string _domainFormat; + public DomainTenantResolveContributor(string domainFormat) + { + _domainFormat = domainFormat.RemovePreFix(ProtocolPrefixes); + } - public DomainTenantResolveContributor(string domainFormat) + protected override Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) + { + if (!httpContext.Request.Host.HasValue) { - _domainFormat = domainFormat.RemovePreFix(ProtocolPrefixes); + return Task.FromResult(null); } - protected override Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) - { - if (!httpContext.Request.Host.HasValue) - { - return Task.FromResult(null); - } - - var hostName = httpContext.Request.Host.Value.RemovePreFix(ProtocolPrefixes); - var extractResult = FormattedStringValueExtracter.Extract(hostName, _domainFormat, ignoreCase: true); + var hostName = httpContext.Request.Host.Value.RemovePreFix(ProtocolPrefixes); + var extractResult = FormattedStringValueExtracter.Extract(hostName, _domainFormat, ignoreCase: true); - context.Handled = true; + context.Handled = true; - return Task.FromResult(extractResult.IsMatch ? extractResult.Matches[0].Value : null); - } + return Task.FromResult(extractResult.IsMatch ? extractResult.Matches[0].Value : null); } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/FormTenantResolveContributor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/FormTenantResolveContributor.cs index 77c12bef7b..67ef0a4c2c 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/FormTenantResolveContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/FormTenantResolveContributor.cs @@ -3,23 +3,22 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +public class FormTenantResolveContributor : HttpTenantResolveContributorBase { - public class FormTenantResolveContributor : HttpTenantResolveContributorBase - { - public const string ContributorName = "Form"; + public const string ContributorName = "Form"; - public override string Name => ContributorName; + public override string Name => ContributorName; - protected override async Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) + protected override async Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) + { + if (!httpContext.Request.HasFormContentType) { - if (!httpContext.Request.HasFormContentType) - { - return null; - } - - var form = await httpContext.Request.ReadFormAsync(); - return form[context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey]; + return null; } + + var form = await httpContext.Request.ReadFormAsync(); + return form[context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey]; } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolveContributor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolveContributor.cs index a6a52290be..a0bec0d606 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolveContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolveContributor.cs @@ -6,43 +6,42 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +public class HeaderTenantResolveContributor : HttpTenantResolveContributorBase { - public class HeaderTenantResolveContributor : HttpTenantResolveContributorBase - { - public const string ContributorName = "Header"; + public const string ContributorName = "Header"; - public override string Name => ContributorName; + public override string Name => ContributorName; - protected override Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) + protected override Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) + { + if (httpContext.Request.Headers.IsNullOrEmpty()) { - if (httpContext.Request.Headers.IsNullOrEmpty()) - { - return Task.FromResult((string)null); - } - - var tenantIdKey = context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey; - - var tenantIdHeader = httpContext.Request.Headers[tenantIdKey]; - if (tenantIdHeader == string.Empty || tenantIdHeader.Count < 1) - { - return Task.FromResult((string)null); - } + return Task.FromResult((string)null); + } - if (tenantIdHeader.Count > 1) - { - Log(context, $"HTTP request includes more than one {tenantIdKey} header value. First one will be used. All of them: {tenantIdHeader.JoinAsString(", ")}"); - } + var tenantIdKey = context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey; - return Task.FromResult(tenantIdHeader.First()); + var tenantIdHeader = httpContext.Request.Headers[tenantIdKey]; + if (tenantIdHeader == string.Empty || tenantIdHeader.Count < 1) + { + return Task.FromResult((string)null); } - protected virtual void Log(ITenantResolveContext context, string text) + if (tenantIdHeader.Count > 1) { - context - .ServiceProvider - .GetRequiredService>() - .LogWarning(text); + Log(context, $"HTTP request includes more than one {tenantIdKey} header value. First one will be used. All of them: {tenantIdHeader.JoinAsString(", ")}"); } + + return Task.FromResult(tenantIdHeader.First()); + } + + protected virtual void Log(ITenantResolveContext context, string text) + { + context + .ServiceProvider + .GetRequiredService>() + .LogWarning(text); } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs index 593d9a7024..1197e16baa 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs @@ -2,32 +2,29 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +[Dependency(ReplaceServices = true)] +public class HttpContextTenantResolveResultAccessor : ITenantResolveResultAccessor, ITransientDependency { - [Dependency(ReplaceServices = true)] - public class HttpContextTenantResolveResultAccessor : ITenantResolveResultAccessor, ITransientDependency - { - public const string HttpContextItemName = "__AbpTenantResolveResult"; + public const string HttpContextItemName = "__AbpTenantResolveResult"; - public TenantResolveResult Result - { - get => _httpContextAccessor.HttpContext?.Items[HttpContextItemName] as TenantResolveResult; - set + public TenantResolveResult Result { + get => _httpContextAccessor.HttpContext?.Items[HttpContextItemName] as TenantResolveResult; + set { + if (_httpContextAccessor.HttpContext == null) { - if (_httpContextAccessor.HttpContext == null) - { - return; - } - - _httpContextAccessor.HttpContext.Items[HttpContextItemName] = value; + return; } + + _httpContextAccessor.HttpContext.Items[HttpContextItemName] = value; } + } - private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; - public HttpContextTenantResolveResultAccessor(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } + public HttpContextTenantResolveResultAccessor(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolveContributorBase.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolveContributorBase.cs index 3b454b5ef9..b0900c899f 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolveContributorBase.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolveContributorBase.cs @@ -6,39 +6,38 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +public abstract class HttpTenantResolveContributorBase : TenantResolveContributorBase { - public abstract class HttpTenantResolveContributorBase : TenantResolveContributorBase + public override async Task ResolveAsync(ITenantResolveContext context) { - public override async Task ResolveAsync(ITenantResolveContext context) + var httpContext = context.GetHttpContext(); + if (httpContext == null) { - var httpContext = context.GetHttpContext(); - if (httpContext == null) - { - return; - } - - try - { - await ResolveFromHttpContextAsync(context, httpContext); - } - catch (Exception e) - { - context.ServiceProvider - .GetRequiredService>() - .LogWarning(e.ToString()); - } + return; } - protected virtual async Task ResolveFromHttpContextAsync(ITenantResolveContext context, HttpContext httpContext) + try { - var tenantIdOrName = await GetTenantIdOrNameFromHttpContextOrNullAsync(context, httpContext); - if (!tenantIdOrName.IsNullOrEmpty()) - { - context.TenantIdOrName = tenantIdOrName; - } + await ResolveFromHttpContextAsync(context, httpContext); } + catch (Exception e) + { + context.ServiceProvider + .GetRequiredService>() + .LogWarning(e.ToString()); + } + } - protected abstract Task GetTenantIdOrNameFromHttpContextOrNullAsync([NotNull] ITenantResolveContext context, [NotNull] HttpContext httpContext); + protected virtual async Task ResolveFromHttpContextAsync(ITenantResolveContext context, HttpContext httpContext) + { + var tenantIdOrName = await GetTenantIdOrNameFromHttpContextOrNullAsync(context, httpContext); + if (!tenantIdOrName.IsNullOrEmpty()) + { + context.TenantIdOrName = tenantIdOrName; + } } + + protected abstract Task GetTenantIdOrNameFromHttpContextOrNullAsync([NotNull] ITenantResolveContext context, [NotNull] HttpContext httpContext); } 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 f3d21a804b..9cd89272f4 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 @@ -11,112 +11,111 @@ using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.Settings; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +public class MultiTenancyMiddleware : IMiddleware, ITransientDependency { - public class MultiTenancyMiddleware : IMiddleware, ITransientDependency + private readonly ITenantConfigurationProvider _tenantConfigurationProvider; + private readonly ICurrentTenant _currentTenant; + private readonly AbpAspNetCoreMultiTenancyOptions _options; + private readonly ITenantResolveResultAccessor _tenantResolveResultAccessor; + + public MultiTenancyMiddleware( + ITenantConfigurationProvider tenantConfigurationProvider, + ICurrentTenant currentTenant, + IOptions options, + ITenantResolveResultAccessor tenantResolveResultAccessor) { - private readonly ITenantConfigurationProvider _tenantConfigurationProvider; - private readonly ICurrentTenant _currentTenant; - private readonly AbpAspNetCoreMultiTenancyOptions _options; - private readonly ITenantResolveResultAccessor _tenantResolveResultAccessor; + _tenantConfigurationProvider = tenantConfigurationProvider; + _currentTenant = currentTenant; + _tenantResolveResultAccessor = tenantResolveResultAccessor; + _options = options.Value; + } - public MultiTenancyMiddleware( - ITenantConfigurationProvider tenantConfigurationProvider, - ICurrentTenant currentTenant, - IOptions options, - ITenantResolveResultAccessor tenantResolveResultAccessor) + public async Task InvokeAsync(HttpContext context, RequestDelegate next) + { + TenantConfiguration tenant; + try { - _tenantConfigurationProvider = tenantConfigurationProvider; - _currentTenant = currentTenant; - _tenantResolveResultAccessor = tenantResolveResultAccessor; - _options = options.Value; + tenant = await _tenantConfigurationProvider.GetAsync(saveResolveResult: true); } - - public async Task InvokeAsync(HttpContext context, RequestDelegate next) + catch (Exception e) { - TenantConfiguration tenant; - try - { - tenant = await _tenantConfigurationProvider.GetAsync(saveResolveResult: true); - } - catch (Exception e) - { - await _options.MultiTenancyMiddlewareErrorPageBuilder(context, e); - return; - } + await _options.MultiTenancyMiddlewareErrorPageBuilder(context, e); + return; + } - if (tenant?.Id != _currentTenant.Id) + if (tenant?.Id != _currentTenant.Id) + { + using (_currentTenant.Change(tenant?.Id, tenant?.Name)) { - using (_currentTenant.Change(tenant?.Id, tenant?.Name)) + if (_tenantResolveResultAccessor.Result != null && + _tenantResolveResultAccessor.Result.AppliedResolvers.Contains(QueryStringTenantResolveContributor.ContributorName)) { - if (_tenantResolveResultAccessor.Result != null && - _tenantResolveResultAccessor.Result.AppliedResolvers.Contains(QueryStringTenantResolveContributor.ContributorName)) - { - AbpMultiTenancyCookieHelper.SetTenantCookie(context, _currentTenant.Id, _options.TenantKey); - } - - var requestCulture = await TryGetRequestCultureAsync(context); - if (requestCulture != null) - { - CultureInfo.CurrentCulture = requestCulture.Culture; - CultureInfo.CurrentUICulture = requestCulture.UICulture; - AbpRequestCultureCookieHelper.SetCultureCookie( - context, - requestCulture - ); - context.Items[AbpRequestLocalizationMiddleware.HttpContextItemName] = true; - } + AbpMultiTenancyCookieHelper.SetTenantCookie(context, _currentTenant.Id, _options.TenantKey); + } - await next(context); + var requestCulture = await TryGetRequestCultureAsync(context); + if (requestCulture != null) + { + CultureInfo.CurrentCulture = requestCulture.Culture; + CultureInfo.CurrentUICulture = requestCulture.UICulture; + AbpRequestCultureCookieHelper.SetCultureCookie( + context, + requestCulture + ); + context.Items[AbpRequestLocalizationMiddleware.HttpContextItemName] = true; } - } - else - { + await next(context); } } - - private async Task TryGetRequestCultureAsync(HttpContext httpContext) + else { - var requestCultureFeature = httpContext.Features.Get(); + await next(context); + } + } - /* If requestCultureFeature == null, that means the RequestLocalizationMiddleware was not used - * and we don't want to set the culture. */ - if (requestCultureFeature == null) - { - return null; - } + private async Task TryGetRequestCultureAsync(HttpContext httpContext) + { + var requestCultureFeature = httpContext.Features.Get(); - /* If requestCultureFeature.Provider is not null, that means RequestLocalizationMiddleware - * already picked a language, so we don't need to set the default. */ - if (requestCultureFeature.Provider != null) - { - return null; - } + /* If requestCultureFeature == null, that means the RequestLocalizationMiddleware was not used + * and we don't want to set the culture. */ + if (requestCultureFeature == null) + { + return null; + } - var settingProvider = httpContext.RequestServices.GetRequiredService(); - var defaultLanguage = await settingProvider.GetOrNullAsync(LocalizationSettingNames.DefaultLanguage); - if (defaultLanguage.IsNullOrWhiteSpace()) - { - return null; - } + /* If requestCultureFeature.Provider is not null, that means RequestLocalizationMiddleware + * already picked a language, so we don't need to set the default. */ + if (requestCultureFeature.Provider != null) + { + return null; + } - string culture; - string uiCulture; + var settingProvider = httpContext.RequestServices.GetRequiredService(); + var defaultLanguage = await settingProvider.GetOrNullAsync(LocalizationSettingNames.DefaultLanguage); + if (defaultLanguage.IsNullOrWhiteSpace()) + { + return null; + } - if (defaultLanguage.Contains(';')) - { - var splitted = defaultLanguage.Split(';'); - culture = splitted[0]; - uiCulture = splitted[1]; - } - else - { - culture = defaultLanguage; - uiCulture = defaultLanguage; - } + string culture; + string uiCulture; - return new RequestCulture(culture, uiCulture); + if (defaultLanguage.Contains(';')) + { + var splitted = defaultLanguage.Split(';'); + culture = splitted[0]; + uiCulture = splitted[1]; } + else + { + culture = defaultLanguage; + uiCulture = defaultLanguage; + } + + return new RequestCulture(culture, uiCulture); } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs index 2edcac0eaf..f4f2cf8cb6 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs @@ -3,33 +3,32 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +public class QueryStringTenantResolveContributor : HttpTenantResolveContributorBase { - public class QueryStringTenantResolveContributor : HttpTenantResolveContributorBase - { - public const string ContributorName = "QueryString"; + public const string ContributorName = "QueryString"; - public override string Name => ContributorName; + public override string Name => ContributorName; - protected override Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) + protected override Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) + { + if (httpContext.Request.QueryString.HasValue) { - if (httpContext.Request.QueryString.HasValue) + var tenantKey = context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey; + if (httpContext.Request.Query.ContainsKey(tenantKey)) { - var tenantKey = context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey; - if (httpContext.Request.Query.ContainsKey(tenantKey)) + var tenantValue = httpContext.Request.Query[tenantKey].ToString(); + if (tenantValue.IsNullOrWhiteSpace()) { - var tenantValue = httpContext.Request.Query[tenantKey].ToString(); - if (tenantValue.IsNullOrWhiteSpace()) - { - context.Handled = true; - return Task.FromResult(null); - } - - return Task.FromResult(tenantValue); + context.Handled = true; + return Task.FromResult(null); } - } - return Task.FromResult(null); + return Task.FromResult(tenantValue); + } } + + return Task.FromResult(null); } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolveContributor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolveContributor.cs index 7ac3943c84..06e7972aa1 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolveContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolveContributor.cs @@ -4,18 +4,17 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +public class RouteTenantResolveContributor : HttpTenantResolveContributorBase { - public class RouteTenantResolveContributor : HttpTenantResolveContributorBase - { - public const string ContributorName = "Route"; + public const string ContributorName = "Route"; - public override string Name => ContributorName; + public override string Name => ContributorName; - protected override Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) - { - var tenantId = httpContext.GetRouteValue(context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey); - return Task.FromResult(tenantId != null ? Convert.ToString(tenantId) : null); - } + protected override Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext) + { + var tenantId = httpContext.GetRouteValue(context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey); + return Task.FromResult(tenantId != null ? Convert.ToString(tenantId) : null); } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/TenantResolveContextExtensions.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/TenantResolveContextExtensions.cs index 701833bc70..a4a8669304 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/TenantResolveContextExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/TenantResolveContextExtensions.cs @@ -2,13 +2,12 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.AspNetCore.MultiTenancy +namespace Volo.Abp.AspNetCore.MultiTenancy; + +public static class TenantResolveContextExtensions { - public static class TenantResolveContextExtensions + public static AbpAspNetCoreMultiTenancyOptions GetAbpAspNetCoreMultiTenancyOptions(this ITenantResolveContext context) { - public static AbpAspNetCoreMultiTenancyOptions GetAbpAspNetCoreMultiTenancyOptions(this ITenantResolveContext context) - { - return context.ServiceProvider.GetRequiredService>().Value; - } + return context.ServiceProvider.GetRequiredService>().Value; } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/MultiTenancy/AbpMultiTenancyOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/MultiTenancy/AbpMultiTenancyOptionsExtensions.cs index aa6f0fdb0e..8ec02c027a 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/MultiTenancy/AbpMultiTenancyOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/MultiTenancy/AbpMultiTenancyOptionsExtensions.cs @@ -1,16 +1,15 @@ using System.Collections.Generic; using Volo.Abp.AspNetCore.MultiTenancy; -namespace Volo.Abp.MultiTenancy +namespace Volo.Abp.MultiTenancy; + +public static class AbpMultiTenancyOptionsExtensions { - public static class AbpMultiTenancyOptionsExtensions + public static void AddDomainTenantResolver(this AbpTenantResolveOptions options, string domainFormat) { - public static void AddDomainTenantResolver(this AbpTenantResolveOptions options, string domainFormat) - { - options.TenantResolvers.InsertAfter( - r => r is CurrentUserTenantResolveContributor, - new DomainTenantResolveContributor(domainFormat) - ); - } + options.TenantResolvers.InsertAfter( + r => r is CurrentUserTenantResolveContributor, + new DomainTenantResolveContributor(domainFormat) + ); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.Generated.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.Generated.cs index 9d1c472bc7..198cfce711 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.Generated.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.Generated.cs @@ -9,15 +9,14 @@ using Volo.Abp.Http.Client.ClientProxying; using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; // ReSharper disable once CheckNamespace -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies; + +[Dependency(ReplaceServices = true)] +[ExposeServices(typeof(IAbpApplicationConfigurationAppService), typeof(AbpApplicationConfigurationClientProxy))] +public partial class AbpApplicationConfigurationClientProxy : ClientProxyBase, IAbpApplicationConfigurationAppService { - [Dependency(ReplaceServices = true)] - [ExposeServices(typeof(IAbpApplicationConfigurationAppService), typeof(AbpApplicationConfigurationClientProxy))] - public partial class AbpApplicationConfigurationClientProxy : ClientProxyBase, IAbpApplicationConfigurationAppService + public virtual async Task GetAsync() { - public virtual async Task GetAsync() - { - return await RequestAsync(nameof(GetAsync)); - } + return await RequestAsync(nameof(GetAsync)); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.cs index 41c1031556..62b582b01b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpApplicationConfigurationClientProxy.cs @@ -3,11 +3,10 @@ using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies; + +[RemoteService(false)] +[DisableConventionalRegistration] +public partial class AbpApplicationConfigurationClientProxy { - [RemoteService(false)] - [DisableConventionalRegistration] - public partial class AbpApplicationConfigurationClientProxy - { - } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpTenantClientProxy.Generated.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpTenantClientProxy.Generated.cs index 8984abfc4c..291a6e95d1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpTenantClientProxy.Generated.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpTenantClientProxy.Generated.cs @@ -9,26 +9,25 @@ using Volo.Abp.Http.Client.ClientProxying; using Volo.Abp.AspNetCore.Mvc.MultiTenancy; // ReSharper disable once CheckNamespace -namespace Pages.Abp.MultiTenancy.ClientProxies +namespace Pages.Abp.MultiTenancy.ClientProxies; + +[Dependency(ReplaceServices = true)] +[ExposeServices(typeof(IAbpTenantAppService), typeof(AbpTenantClientProxy))] +public partial class AbpTenantClientProxy : ClientProxyBase, IAbpTenantAppService { - [Dependency(ReplaceServices = true)] - [ExposeServices(typeof(IAbpTenantAppService), typeof(AbpTenantClientProxy))] - public partial class AbpTenantClientProxy : ClientProxyBase, IAbpTenantAppService + public virtual async Task FindTenantByNameAsync(string name) { - public virtual async Task FindTenantByNameAsync(string name) + return await RequestAsync(nameof(FindTenantByNameAsync), new ClientProxyRequestTypeValue { - return await RequestAsync(nameof(FindTenantByNameAsync), new ClientProxyRequestTypeValue - { - { typeof(string), name } - }); - } + { typeof(string), name } + }); + } - public virtual async Task FindTenantByIdAsync(Guid id) + public virtual async Task FindTenantByIdAsync(Guid id) + { + return await RequestAsync(nameof(FindTenantByIdAsync), new ClientProxyRequestTypeValue { - return await RequestAsync(nameof(FindTenantByIdAsync), new ClientProxyRequestTypeValue - { - { typeof(Guid), id } - }); - } + { typeof(Guid), id } + }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpTenantClientProxy.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpTenantClientProxy.cs index e73b05b573..8ca24d1317 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpTenantClientProxy.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/ClientProxies/AbpTenantClientProxy.cs @@ -4,11 +4,11 @@ using Volo.Abp; using Volo.Abp.DependencyInjection; -namespace Pages.Abp.MultiTenancy.ClientProxies +namespace Pages.Abp.MultiTenancy.ClientProxies; + +[RemoteService(false)] +[DisableConventionalRegistration] +public partial class AbpTenantClientProxy { - [RemoteService(false)] - [DisableConventionalRegistration] - public partial class AbpTenantClientProxy - { - } } + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientCommonModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientCommonModule.cs index e48a63c86c..d3734e7c66 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientCommonModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientCommonModule.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Pages.Abp.MultiTenancy.ClientProxies; using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies; using Volo.Abp.Authorization; @@ -9,37 +9,36 @@ using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +[DependsOn( + typeof(AbpHttpClientModule), + typeof(AbpAspNetCoreMvcContractsModule), + typeof(AbpCachingModule), + typeof(AbpLocalizationModule), + typeof(AbpAuthorizationModule), + typeof(AbpFeaturesModule), + typeof(AbpVirtualFileSystemModule) +)] +public class AbpAspNetCoreMvcClientCommonModule : AbpModule { - [DependsOn( - typeof(AbpHttpClientModule), - typeof(AbpAspNetCoreMvcContractsModule), - typeof(AbpCachingModule), - typeof(AbpLocalizationModule), - typeof(AbpAuthorizationModule), - typeof(AbpFeaturesModule), - typeof(AbpVirtualFileSystemModule) - )] - public class AbpAspNetCoreMvcClientCommonModule : AbpModule + public const string RemoteServiceName = "AbpMvcClient"; + + public override void ConfigureServices(ServiceConfigurationContext context) { - public const string RemoteServiceName = "AbpMvcClient"; + context.Services.AddStaticHttpClientProxies(typeof(AbpAspNetCoreMvcContractsModule).Assembly, RemoteServiceName); - public override void ConfigureServices(ServiceConfigurationContext context) + Configure(options => { - context.Services.AddStaticHttpClientProxies(typeof(AbpAspNetCoreMvcContractsModule).Assembly, RemoteServiceName); + options.FileSets.AddEmbedded(); + }); - Configure(options => - { - options.FileSets.AddEmbedded(); - }); - - Configure(options => - { - options.GlobalContributors.Add(); - }); + Configure(options => + { + options.GlobalContributors.Add(); + }); - context.Services.AddTransient(); - context.Services.AddTransient(); - } + context.Services.AddTransient(); + context.Services.AddTransient(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/ICachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/ICachedApplicationConfigurationClient.cs index 80e6c4e054..f173082fa3 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/ICachedApplicationConfigurationClient.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/ICachedApplicationConfigurationClient.cs @@ -1,12 +1,11 @@ using System.Threading.Tasks; using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +public interface ICachedApplicationConfigurationClient : IAsyncInitialize { - public interface ICachedApplicationConfigurationClient : IAsyncInitialize - { - Task GetAsync(); + Task GetAsync(); - ApplicationConfigurationDto Get(); - } + ApplicationConfigurationDto Get(); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteFeatureChecker.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteFeatureChecker.cs index f769f512a5..d3969e7888 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteFeatureChecker.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteFeatureChecker.cs @@ -2,21 +2,20 @@ using System.Threading.Tasks; using Volo.Abp.Features; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +public class RemoteFeatureChecker : FeatureCheckerBase { - public class RemoteFeatureChecker : FeatureCheckerBase - { - protected ICachedApplicationConfigurationClient ConfigurationClient { get; } + protected ICachedApplicationConfigurationClient ConfigurationClient { get; } - public RemoteFeatureChecker(ICachedApplicationConfigurationClient configurationClient) - { - ConfigurationClient = configurationClient; - } + public RemoteFeatureChecker(ICachedApplicationConfigurationClient configurationClient) + { + ConfigurationClient = configurationClient; + } - public override async Task GetOrNullAsync(string name) - { - var configuration = await ConfigurationClient.GetAsync(); - return configuration.Features.Values.GetOrDefault(name); - } + public override async Task GetOrNullAsync(string name) + { + var configuration = await ConfigurationClient.GetAsync(); + return configuration.Features.Values.GetOrDefault(name); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLanguageProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLanguageProvider.cs index 02c97c0598..cb6b76ad74 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLanguageProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLanguageProvider.cs @@ -3,21 +3,20 @@ using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Localization; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +public class RemoteLanguageProvider : ILanguageProvider, ITransientDependency { - public class RemoteLanguageProvider : ILanguageProvider, ITransientDependency - { - protected ICachedApplicationConfigurationClient ConfigurationClient { get; } + protected ICachedApplicationConfigurationClient ConfigurationClient { get; } - public RemoteLanguageProvider(ICachedApplicationConfigurationClient configurationClient) - { - ConfigurationClient = configurationClient; - } + public RemoteLanguageProvider(ICachedApplicationConfigurationClient configurationClient) + { + ConfigurationClient = configurationClient; + } - public async Task> GetLanguagesAsync() - { - var configuration = await ConfigurationClient.GetAsync(); - return configuration.Localization.Languages; - } + public async Task> GetLanguagesAsync() + { + var configuration = await ConfigurationClient.GetAsync(); + return configuration.Localization.Languages; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLocalizationContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLocalizationContributor.cs index f8502fce78..c01fc6a1af 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLocalizationContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteLocalizationContributor.cs @@ -5,67 +5,66 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Localization; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +public class RemoteLocalizationContributor : ILocalizationResourceContributor { - public class RemoteLocalizationContributor : ILocalizationResourceContributor + private LocalizationResource _resource; + private ICachedApplicationConfigurationClient _applicationConfigurationClient; + private ILogger _logger; + + public void Initialize(LocalizationResourceInitializationContext context) { - private LocalizationResource _resource; - private ICachedApplicationConfigurationClient _applicationConfigurationClient; - private ILogger _logger; + _resource = context.Resource; + _applicationConfigurationClient = context.ServiceProvider.GetRequiredService(); + _logger = context.ServiceProvider.GetService>() + ?? NullLogger.Instance; + } - public void Initialize(LocalizationResourceInitializationContext context) + public LocalizedString GetOrNull(string cultureName, string name) + { + var resource = GetResourceOrNull(); + if (resource == null) { - _resource = context.Resource; - _applicationConfigurationClient = context.ServiceProvider.GetRequiredService(); - _logger = context.ServiceProvider.GetService>() - ?? NullLogger.Instance; + return null; } - public LocalizedString GetOrNull(string cultureName, string name) + var value = resource.GetOrDefault(name); + if (value == null) { - var resource = GetResourceOrNull(); - if (resource == null) - { - return null; - } - - var value = resource.GetOrDefault(name); - if (value == null) - { - return null; - } - - return new LocalizedString(name, value); + return null; } - public void Fill(string cultureName, Dictionary dictionary) - { - var resource = GetResourceOrNull(); - if (resource == null) - { - return; - } + return new LocalizedString(name, value); + } - foreach (var keyValue in resource) - { - dictionary[keyValue.Key] = new LocalizedString(keyValue.Key, keyValue.Value); - } + public void Fill(string cultureName, Dictionary dictionary) + { + var resource = GetResourceOrNull(); + if (resource == null) + { + return; } - private Dictionary GetResourceOrNull() + foreach (var keyValue in resource) { - var applicationConfigurationDto = _applicationConfigurationClient.Get(); + dictionary[keyValue.Key] = new LocalizedString(keyValue.Key, keyValue.Value); + } + } - var resource = applicationConfigurationDto - .Localization.Values - .GetOrDefault(_resource.ResourceName); + private Dictionary GetResourceOrNull() + { + var applicationConfigurationDto = _applicationConfigurationClient.Get(); - if (resource == null) - { - _logger.LogWarning($"Could not find the localization resource {_resource.ResourceName} on the remote server!"); - } + var resource = applicationConfigurationDto + .Localization.Values + .GetOrDefault(_resource.ResourceName); - return resource; + if (resource == null) + { + _logger.LogWarning($"Could not find the localization resource {_resource.ResourceName} on the remote server!"); } + + return resource; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemotePermissionChecker.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemotePermissionChecker.cs index 5ce1ea9edc..3274c5010c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemotePermissionChecker.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemotePermissionChecker.cs @@ -3,48 +3,47 @@ using System.Threading.Tasks; using Volo.Abp.Authorization.Permissions; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +public class RemotePermissionChecker : IPermissionChecker, ITransientDependency { - public class RemotePermissionChecker : IPermissionChecker, ITransientDependency + protected ICachedApplicationConfigurationClient ConfigurationClient { get; } + + public RemotePermissionChecker(ICachedApplicationConfigurationClient configurationClient) { - protected ICachedApplicationConfigurationClient ConfigurationClient { get; } + ConfigurationClient = configurationClient; + } - public RemotePermissionChecker(ICachedApplicationConfigurationClient configurationClient) - { - ConfigurationClient = configurationClient; - } + public async Task IsGrantedAsync(string name) + { + var configuration = await ConfigurationClient.GetAsync(); - public async Task IsGrantedAsync(string name) - { - var configuration = await ConfigurationClient.GetAsync(); + return configuration.Auth.GrantedPolicies.ContainsKey(name); + } - return configuration.Auth.GrantedPolicies.ContainsKey(name); - } + public async Task IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name) + { + /* This provider always works for the current principal. */ + return await IsGrantedAsync(name); + } - public async Task IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name) + public async Task IsGrantedAsync(string[] names) + { + var result = new MultiplePermissionGrantResult(); + var configuration = await ConfigurationClient.GetAsync(); + foreach (var name in names) { - /* This provider always works for the current principal. */ - return await IsGrantedAsync(name); + result.Result.Add(name, configuration.Auth.GrantedPolicies.ContainsKey(name) ? + PermissionGrantResult.Granted : + PermissionGrantResult.Undefined); } - public async Task IsGrantedAsync(string[] names) - { - var result = new MultiplePermissionGrantResult(); - var configuration = await ConfigurationClient.GetAsync(); - foreach (var name in names) - { - result.Result.Add(name, configuration.Auth.GrantedPolicies.ContainsKey(name) ? - PermissionGrantResult.Granted : - PermissionGrantResult.Undefined); - } - - return result; - } + return result; + } - public async Task IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string[] names) - { - /* This provider always works for the current principal. */ - return await IsGrantedAsync(names); - } + public async Task IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string[] names) + { + /* This provider always works for the current principal. */ + return await IsGrantedAsync(names); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteSettingProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteSettingProvider.cs index 89db44b43d..2599df1ef6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteSettingProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteSettingProvider.cs @@ -4,36 +4,35 @@ using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Settings; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +public class RemoteSettingProvider : ISettingProvider, ITransientDependency { - public class RemoteSettingProvider : ISettingProvider, ITransientDependency - { - protected ICachedApplicationConfigurationClient ConfigurationClient { get; } + protected ICachedApplicationConfigurationClient ConfigurationClient { get; } - public RemoteSettingProvider(ICachedApplicationConfigurationClient configurationClient) - { - ConfigurationClient = configurationClient; - } + public RemoteSettingProvider(ICachedApplicationConfigurationClient configurationClient) + { + ConfigurationClient = configurationClient; + } - public async Task GetOrNullAsync(string name) - { - var configuration = await ConfigurationClient.GetAsync(); - return configuration.Setting.Values.GetOrDefault(name); - } + public async Task GetOrNullAsync(string name) + { + var configuration = await ConfigurationClient.GetAsync(); + return configuration.Setting.Values.GetOrDefault(name); + } - public async Task> GetAllAsync(string[] names) - { - var configuration = await ConfigurationClient.GetAsync(); - return names.Select(x => new SettingValue(x, configuration.Setting.Values.GetOrDefault(x))).ToList(); - } + public async Task> GetAllAsync(string[] names) + { + var configuration = await ConfigurationClient.GetAsync(); + return names.Select(x => new SettingValue(x, configuration.Setting.Values.GetOrDefault(x))).ToList(); + } - public async Task> GetAllAsync() - { - var configuration = await ConfigurationClient.GetAsync(); - return configuration - .Setting.Values - .Select(s => new SettingValue(s.Key, s.Value)) - .ToList(); - } + public async Task> GetAllAsync() + { + var configuration = await ConfigurationClient.GetAsync(); + return configuration + .Setting.Values + .Select(s => new SettingValue(s.Key, s.Value)) + .ToList(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs index 48e2461167..a7dc58f82a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs @@ -1,13 +1,12 @@ using Volo.Abp.EventBus; using Volo.Abp.Modularity; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +[DependsOn( + typeof(AbpAspNetCoreMvcClientCommonModule), + typeof(AbpEventBusModule) + )] +public class AbpAspNetCoreMvcClientModule : AbpModule { - [DependsOn( - typeof(AbpAspNetCoreMvcClientCommonModule), - typeof(AbpEventBusModule) - )] - public class AbpAspNetCoreMvcClientModule : AbpModule - { - } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs index e7a78e1d15..fba7a3fdfe 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.AspNetCore.Http; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Distributed; @@ -9,81 +9,80 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.Threading; using Volo.Abp.Users; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +[ExposeServices( + typeof(MvcCachedApplicationConfigurationClient), + typeof(ICachedApplicationConfigurationClient), + typeof(IAsyncInitialize) + )] +public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigurationClient, ITransientDependency { - [ExposeServices( - typeof(MvcCachedApplicationConfigurationClient), - typeof(ICachedApplicationConfigurationClient), - typeof(IAsyncInitialize) - )] - public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigurationClient, ITransientDependency + protected IHttpContextAccessor HttpContextAccessor { get; } + protected AbpApplicationConfigurationClientProxy ApplicationConfigurationAppService { get; } + protected ICurrentUser CurrentUser { get; } + protected IDistributedCache Cache { get; } + + public MvcCachedApplicationConfigurationClient( + IDistributedCache cache, + AbpApplicationConfigurationClientProxy applicationConfigurationAppService, + ICurrentUser currentUser, + IHttpContextAccessor httpContextAccessor) { - protected IHttpContextAccessor HttpContextAccessor { get; } - protected AbpApplicationConfigurationClientProxy ApplicationConfigurationAppService { get; } - protected ICurrentUser CurrentUser { get; } - protected IDistributedCache Cache { get; } + ApplicationConfigurationAppService = applicationConfigurationAppService; + CurrentUser = currentUser; + HttpContextAccessor = httpContextAccessor; + Cache = cache; + } - public MvcCachedApplicationConfigurationClient( - IDistributedCache cache, - AbpApplicationConfigurationClientProxy applicationConfigurationAppService, - ICurrentUser currentUser, - IHttpContextAccessor httpContextAccessor) - { - ApplicationConfigurationAppService = applicationConfigurationAppService; - CurrentUser = currentUser; - HttpContextAccessor = httpContextAccessor; - Cache = cache; - } + public async Task InitializeAsync() + { + await GetAsync(); + } - public async Task InitializeAsync() + public async Task GetAsync() + { + var cacheKey = CreateCacheKey(); + var httpContext = HttpContextAccessor?.HttpContext; + + if (httpContext != null && httpContext.Items[cacheKey] is ApplicationConfigurationDto configuration) { - await GetAsync(); + return configuration; } - public async Task GetAsync() - { - var cacheKey = CreateCacheKey(); - var httpContext = HttpContextAccessor?.HttpContext; - if (httpContext != null && httpContext.Items[cacheKey] is ApplicationConfigurationDto configuration) + configuration = await Cache.GetOrAddAsync( + cacheKey, + async () => await ApplicationConfigurationAppService.GetAsync(), + () => new DistributedCacheEntryOptions { - return configuration; + AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(300) //TODO: Should be configurable. } + ); + if (httpContext != null) + { + httpContext.Items[cacheKey] = configuration; + } - configuration = await Cache.GetOrAddAsync( - cacheKey, - async () => await ApplicationConfigurationAppService.GetAsync(), - () => new DistributedCacheEntryOptions - { - AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(300) //TODO: Should be configurable. - } - ); + return configuration; + } - if (httpContext != null) - { - httpContext.Items[cacheKey] = configuration; - } + public ApplicationConfigurationDto Get() + { + var cacheKey = CreateCacheKey(); + var httpContext = HttpContextAccessor?.HttpContext; + if (httpContext != null && httpContext.Items[cacheKey] is ApplicationConfigurationDto configuration) + { return configuration; } - public ApplicationConfigurationDto Get() - { - var cacheKey = CreateCacheKey(); - var httpContext = HttpContextAccessor?.HttpContext; - - if (httpContext != null && httpContext.Items[cacheKey] is ApplicationConfigurationDto configuration) - { - return configuration; - } - - return AsyncHelper.RunSync(GetAsync); - } + return AsyncHelper.RunSync(GetAsync); + } - protected virtual string CreateCacheKey() - { - return MvcCachedApplicationConfigurationClientHelper.CreateCacheKey(CurrentUser); - } + protected virtual string CreateCacheKey() + { + return MvcCachedApplicationConfigurationClientHelper.CreateCacheKey(CurrentUser); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs index ae013c4343..740a79c884 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs @@ -1,14 +1,13 @@ using System.Globalization; using Volo.Abp.Users; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +internal static class MvcCachedApplicationConfigurationClientHelper { - internal static class MvcCachedApplicationConfigurationClientHelper + public static string CreateCacheKey(ICurrentUser currentUser) { - public static string CreateCacheKey(ICurrentUser currentUser) - { - var userKey = currentUser.Id?.ToString("N") ?? "Anonymous"; - return $"ApplicationConfiguration_{userKey}_{CultureInfo.CurrentUICulture.Name}"; - } + var userKey = currentUser.Id?.ToString("N") ?? "Anonymous"; + return $"ApplicationConfiguration_{userKey}_{CultureInfo.CurrentUICulture.Name}"; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs index e8f5b201da..8bd3971779 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs @@ -5,30 +5,29 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; using Volo.Abp.Users; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +public class MvcCurrentApplicationConfigurationCacheResetEventHandler : + ILocalEventHandler, + ITransientDependency { - public class MvcCurrentApplicationConfigurationCacheResetEventHandler : - ILocalEventHandler, - ITransientDependency - { - protected ICurrentUser CurrentUser { get; } - protected IDistributedCache Cache { get; } + protected ICurrentUser CurrentUser { get; } + protected IDistributedCache Cache { get; } - public MvcCurrentApplicationConfigurationCacheResetEventHandler(ICurrentUser currentUser, - IDistributedCache cache) - { - CurrentUser = currentUser; - Cache = cache; - } + public MvcCurrentApplicationConfigurationCacheResetEventHandler(ICurrentUser currentUser, + IDistributedCache cache) + { + CurrentUser = currentUser; + Cache = cache; + } - public virtual async Task HandleEventAsync(CurrentApplicationConfigurationCacheResetEventData eventData) - { - await Cache.RemoveAsync(CreateCacheKey()); - } + public virtual async Task HandleEventAsync(CurrentApplicationConfigurationCacheResetEventData eventData) + { + await Cache.RemoveAsync(CreateCacheKey()); + } - protected virtual string CreateCacheKey() - { - return MvcCachedApplicationConfigurationClientHelper.CreateCacheKey(CurrentUser); - } + protected virtual string CreateCacheKey() + { + return MvcCachedApplicationConfigurationClientHelper.CreateCacheKey(CurrentUser); } } 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 f4744ca587..dd4411255e 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 @@ -9,154 +9,153 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; using Volo.Abp.Threading; -namespace Volo.Abp.AspNetCore.Mvc.Client +namespace Volo.Abp.AspNetCore.Mvc.Client; + +public class MvcRemoteTenantStore : ITenantStore, ITransientDependency { - public class MvcRemoteTenantStore : ITenantStore, ITransientDependency + protected AbpTenantClientProxy TenantAppService { get; } + protected IHttpContextAccessor HttpContextAccessor { get; } + protected IDistributedCache Cache { get; } + + public MvcRemoteTenantStore( + AbpTenantClientProxy tenantAppService, + IHttpContextAccessor httpContextAccessor, + IDistributedCache cache) { - protected AbpTenantClientProxy TenantAppService { get; } - protected IHttpContextAccessor HttpContextAccessor { get; } - protected IDistributedCache Cache { get; } - - public MvcRemoteTenantStore( - AbpTenantClientProxy tenantAppService, - IHttpContextAccessor httpContextAccessor, - IDistributedCache cache) - { - TenantAppService = tenantAppService; - HttpContextAccessor = httpContextAccessor; - Cache = cache; - } + TenantAppService = tenantAppService; + HttpContextAccessor = httpContextAccessor; + Cache = cache; + } - public async Task FindAsync(string name) + public async Task FindAsync(string name) + { + var cacheKey = CreateCacheKey(name); + var httpContext = HttpContextAccessor?.HttpContext; + + if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration) { - var cacheKey = CreateCacheKey(name); - var httpContext = HttpContextAccessor?.HttpContext; + return tenantConfiguration; + } - if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration) + tenantConfiguration = await Cache.GetOrAddAsync( + cacheKey, + async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name)), + () => new DistributedCacheEntryOptions { - return tenantConfiguration; - } - - tenantConfiguration = await Cache.GetOrAddAsync( - cacheKey, - async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name)), - () => new DistributedCacheEntryOptions - { - AbsoluteExpirationRelativeToNow = - TimeSpan.FromMinutes(5) //TODO: Should be configurable. + AbsoluteExpirationRelativeToNow = + TimeSpan.FromMinutes(5) //TODO: Should be configurable. } - ); - - if (httpContext != null) - { - httpContext.Items[cacheKey] = tenantConfiguration; - } + ); - return tenantConfiguration; - } - - public async Task FindAsync(Guid id) + if (httpContext != null) { - var cacheKey = CreateCacheKey(id); - var httpContext = HttpContextAccessor?.HttpContext; + httpContext.Items[cacheKey] = tenantConfiguration; + } - if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration) - { - return tenantConfiguration; - } - - tenantConfiguration = await Cache.GetOrAddAsync( - cacheKey, - async () => CreateTenantConfiguration(await TenantAppService.FindTenantByIdAsync(id)), - () => new DistributedCacheEntryOptions - { - AbsoluteExpirationRelativeToNow = - TimeSpan.FromMinutes(5) //TODO: Should be configurable. - } - ); + return tenantConfiguration; + } - if (httpContext != null) - { - httpContext.Items[cacheKey] = tenantConfiguration; - } + public async Task FindAsync(Guid id) + { + var cacheKey = CreateCacheKey(id); + var httpContext = HttpContextAccessor?.HttpContext; + if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration) + { return tenantConfiguration; } - public TenantConfiguration Find(string name) - { - var cacheKey = CreateCacheKey(name); - var httpContext = HttpContextAccessor?.HttpContext; - - if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration) + tenantConfiguration = await Cache.GetOrAddAsync( + cacheKey, + async () => CreateTenantConfiguration(await TenantAppService.FindTenantByIdAsync(id)), + () => new DistributedCacheEntryOptions { - return tenantConfiguration; - } - - tenantConfiguration = Cache.GetOrAdd( - cacheKey, - () => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name))), - () => new DistributedCacheEntryOptions - { - AbsoluteExpirationRelativeToNow = - TimeSpan.FromMinutes(5) //TODO: Should be configurable. + AbsoluteExpirationRelativeToNow = + TimeSpan.FromMinutes(5) //TODO: Should be configurable. } - ); - - if (httpContext != null) - { - httpContext.Items[cacheKey] = tenantConfiguration; - } + ); - return tenantConfiguration; + if (httpContext != null) + { + httpContext.Items[cacheKey] = tenantConfiguration; } - public TenantConfiguration Find(Guid id) + return tenantConfiguration; + } + + public TenantConfiguration Find(string name) + { + var cacheKey = CreateCacheKey(name); + var httpContext = HttpContextAccessor?.HttpContext; + + if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration) { - var cacheKey = CreateCacheKey(id); - var httpContext = HttpContextAccessor?.HttpContext; + return tenantConfiguration; + } - if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration) + tenantConfiguration = Cache.GetOrAdd( + cacheKey, + () => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name))), + () => new DistributedCacheEntryOptions { - return tenantConfiguration; - } - - tenantConfiguration = Cache.GetOrAdd( - cacheKey, - () => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByIdAsync(id))), - () => new DistributedCacheEntryOptions - { - AbsoluteExpirationRelativeToNow = - TimeSpan.FromMinutes(5) //TODO: Should be configurable. + AbsoluteExpirationRelativeToNow = + TimeSpan.FromMinutes(5) //TODO: Should be configurable. } - ); + ); - if (httpContext != null) - { - httpContext.Items[cacheKey] = tenantConfiguration; - } + if (httpContext != null) + { + httpContext.Items[cacheKey] = tenantConfiguration; + } + + return tenantConfiguration; + } + public TenantConfiguration Find(Guid id) + { + var cacheKey = CreateCacheKey(id); + var httpContext = HttpContextAccessor?.HttpContext; + + if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration) + { return tenantConfiguration; } - protected virtual TenantConfiguration CreateTenantConfiguration(FindTenantResultDto tenantResultDto) - { - if (!tenantResultDto.Success || tenantResultDto.TenantId == null) + tenantConfiguration = Cache.GetOrAdd( + cacheKey, + () => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByIdAsync(id))), + () => new DistributedCacheEntryOptions { - return null; - } - - return new TenantConfiguration(tenantResultDto.TenantId.Value, tenantResultDto.Name); - } + AbsoluteExpirationRelativeToNow = + TimeSpan.FromMinutes(5) //TODO: Should be configurable. + } + ); - protected virtual string CreateCacheKey(string tenantName) + if (httpContext != null) { - return $"RemoteTenantStore_Name_{tenantName}"; + httpContext.Items[cacheKey] = tenantConfiguration; } - protected virtual string CreateCacheKey(Guid tenantId) + return tenantConfiguration; + } + + protected virtual TenantConfiguration CreateTenantConfiguration(FindTenantResultDto tenantResultDto) + { + if (!tenantResultDto.Success || tenantResultDto.TenantId == null) { - return $"RemoteTenantStore_Id_{tenantId:N}"; + return null; } + + return new TenantConfiguration(tenantResultDto.TenantId.Value, tenantResultDto.Name); + } + + protected virtual string CreateCacheKey(string tenantName) + { + return $"RemoteTenantStore_Name_{tenantName}"; + } + + protected virtual string CreateCacheKey(Guid tenantId) + { + return $"RemoteTenantStore_Id_{tenantId:N}"; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcContractsModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcContractsModule.cs index 3bf299437c..278fe6eb21 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcContractsModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcContractsModule.cs @@ -1,13 +1,12 @@ using Volo.Abp.Application; using Volo.Abp.Modularity; -namespace Volo.Abp.AspNetCore.Mvc +namespace Volo.Abp.AspNetCore.Mvc; + +[DependsOn( + typeof(AbpDddApplicationContractsModule) + )] +public class AbpAspNetCoreMvcContractsModule : AbpModule { - [DependsOn( - typeof(AbpDddApplicationContractsModule) - )] - public class AbpAspNetCoreMvcContractsModule : AbpModule - { - } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationAuthConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationAuthConfigurationDto.cs index 39b3ca394b..be5a022b01 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationAuthConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationAuthConfigurationDto.cs @@ -1,19 +1,18 @@ using System; using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +[Serializable] +public class ApplicationAuthConfigurationDto { - [Serializable] - public class ApplicationAuthConfigurationDto - { - public Dictionary Policies { get; set; } + public Dictionary Policies { get; set; } - public Dictionary GrantedPolicies { get; set; } + public Dictionary GrantedPolicies { get; set; } - public ApplicationAuthConfigurationDto() - { - Policies = new Dictionary(); - GrantedPolicies = new Dictionary(); - } + public ApplicationAuthConfigurationDto() + { + Policies = new Dictionary(); + GrantedPolicies = new Dictionary(); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs index 6a4aa49a1b..9b6a66992f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs @@ -2,29 +2,28 @@ using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; using Volo.Abp.AspNetCore.Mvc.MultiTenancy; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +[Serializable] +public class ApplicationConfigurationDto { - [Serializable] - public class ApplicationConfigurationDto - { - public ApplicationLocalizationConfigurationDto Localization { get; set; } + public ApplicationLocalizationConfigurationDto Localization { get; set; } - public ApplicationAuthConfigurationDto Auth { get; set; } + public ApplicationAuthConfigurationDto Auth { get; set; } - public ApplicationSettingConfigurationDto Setting { get; set; } + public ApplicationSettingConfigurationDto Setting { get; set; } - public CurrentUserDto CurrentUser { get; set; } + public CurrentUserDto CurrentUser { get; set; } - public ApplicationFeatureConfigurationDto Features { get; set; } + public ApplicationFeatureConfigurationDto Features { get; set; } - public MultiTenancyInfoDto MultiTenancy { get; set; } + public MultiTenancyInfoDto MultiTenancy { get; set; } - public CurrentTenantDto CurrentTenant { get; set; } + public CurrentTenantDto CurrentTenant { get; set; } - public TimingDto Timing { get; set; } + public TimingDto Timing { get; set; } - public ClockDto Clock { get; set; } + public ClockDto Clock { get; set; } - public ObjectExtensionsDto ObjectExtensions { get; set; } - } + public ObjectExtensionsDto ObjectExtensions { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationFeatureConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationFeatureConfigurationDto.cs index 4972d2242d..9215a46616 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationFeatureConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationFeatureConfigurationDto.cs @@ -1,16 +1,15 @@ using System; using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +[Serializable] +public class ApplicationFeatureConfigurationDto { - [Serializable] - public class ApplicationFeatureConfigurationDto - { - public Dictionary Values { get; set; } + public Dictionary Values { get; set; } - public ApplicationFeatureConfigurationDto() - { - Values = new Dictionary(); - } + public ApplicationFeatureConfigurationDto() + { + Values = new Dictionary(); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs index 5f53694177..0b229235fa 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs @@ -2,28 +2,27 @@ using System.Collections.Generic; using Volo.Abp.Localization; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +[Serializable] +public class ApplicationLocalizationConfigurationDto { - [Serializable] - public class ApplicationLocalizationConfigurationDto - { - //TODO: Rename to Texts? - public Dictionary> Values { get; set; } + //TODO: Rename to Texts? + public Dictionary> Values { get; set; } - public List Languages { get; set; } + public List Languages { get; set; } - public CurrentCultureDto CurrentCulture { get; set; } + public CurrentCultureDto CurrentCulture { get; set; } - public string DefaultResourceName { get; set; } + public string DefaultResourceName { get; set; } - public Dictionary> LanguagesMap { get; set; } + public Dictionary> LanguagesMap { get; set; } - public Dictionary> LanguageFilesMap { get; set; } + public Dictionary> LanguageFilesMap { get; set; } - public ApplicationLocalizationConfigurationDto() - { - Values = new Dictionary>(); - Languages = new List(); - } + public ApplicationLocalizationConfigurationDto() + { + Values = new Dictionary>(); + Languages = new List(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationSettingConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationSettingConfigurationDto.cs index 40cbd89fd1..a1fbba2bbf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationSettingConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationSettingConfigurationDto.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +[Serializable] +public class ApplicationSettingConfigurationDto { - [Serializable] - public class ApplicationSettingConfigurationDto - { - public Dictionary Values { get; set; } - } -} \ No newline at end of file + public Dictionary Values { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ClockDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ClockDto.cs index 2cff97f814..5112ac80ed 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ClockDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ClockDto.cs @@ -1,7 +1,6 @@ -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +public class ClockDto { - public class ClockDto - { - public string Kind { get; set; } - } + public string Kind { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs index e84e96d9dd..a50cb7b136 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs @@ -1,10 +1,9 @@ -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +/// +/// This event is used to invalidate current user's cached configuration. +/// +public class CurrentApplicationConfigurationCacheResetEventData { - /// - /// This event is used to invalidate current user's cached configuration. - /// - public class CurrentApplicationConfigurationCacheResetEventData - { - } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentCultureDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentCultureDto.cs index b225fac8f0..8f1896e31f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentCultureDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentCultureDto.cs @@ -1,23 +1,22 @@ -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +public class CurrentCultureDto { - public class CurrentCultureDto - { - public string DisplayName { get; set; } + public string DisplayName { get; set; } - public string EnglishName { get; set; } + public string EnglishName { get; set; } - public string ThreeLetterIsoLanguageName { get; set; } + public string ThreeLetterIsoLanguageName { get; set; } - public string TwoLetterIsoLanguageName { get; set; } + public string TwoLetterIsoLanguageName { get; set; } - public bool IsRightToLeft { get; set; } + public bool IsRightToLeft { get; set; } - public string CultureName { get; set; } + public string CultureName { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public string NativeName { get; set; } + public string NativeName { get; set; } - public DateTimeFormatDto DateTimeFormat { get; set; } - } -} \ No newline at end of file + public DateTimeFormatDto DateTimeFormat { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs index b1b97c4b91..08a8c4c873 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs @@ -1,34 +1,37 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +[Serializable] +public class CurrentUserDto { - [Serializable] - public class CurrentUserDto - { - public bool IsAuthenticated { get; set; } + public bool IsAuthenticated { get; set; } + + public Guid? Id { get; set; } + + public Guid? TenantId { get; set; } - public Guid? Id { get; set; } + public Guid? ImpersonatorUserId { get; set; } - public Guid? TenantId { get; set; } + public Guid? ImpersonatorTenantId { get; set; } - public Guid? ImpersonatorUserId { get; set; } + public string ImpersonatorUserName { get; set; } - public Guid? ImpersonatorTenantId { get; set; } + public string ImpersonatorTenantName { get; set; } - public string UserName { get; set; } + public string UserName { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public string SurName { get; set; } + public string SurName { get; set; } - public string Email { get; set; } + public string Email { get; set; } - public bool EmailVerified { get; set; } + public bool EmailVerified { get; set; } - public string PhoneNumber { get; set; } + public string PhoneNumber { get; set; } - public bool PhoneNumberVerified { get; set; } + public bool PhoneNumberVerified { get; set; } - public string[] Roles { get; set; } - } + public string[] Roles { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/DateTimeFormatDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/DateTimeFormatDto.cs index 259dff1da6..5f5ad14a5a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/DateTimeFormatDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/DateTimeFormatDto.cs @@ -1,19 +1,18 @@ -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +public class DateTimeFormatDto { - public class DateTimeFormatDto - { - public string CalendarAlgorithmType { get; set; } + public string CalendarAlgorithmType { get; set; } - public string DateTimeFormatLong { get; set; } + public string DateTimeFormatLong { get; set; } - public string ShortDatePattern { get; set; } + public string ShortDatePattern { get; set; } - public string FullDateTimePattern { get; set; } + public string FullDateTimePattern { get; set; } - public string DateSeparator { get; set; } + public string DateSeparator { get; set; } - public string ShortTimePattern { get; set; } + public string ShortTimePattern { get; set; } - public string LongTimePattern { get; set; } - } -} \ No newline at end of file + public string LongTimePattern { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationConfigurationAppService.cs index 4c521b9efc..9c8fd3d414 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationConfigurationAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationConfigurationAppService.cs @@ -1,10 +1,9 @@ using System.Threading.Tasks; using Volo.Abp.Application.Services; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +public interface IAbpApplicationConfigurationAppService : IApplicationService { - public interface IAbpApplicationConfigurationAppService : IApplicationService - { - Task GetAsync(); - } -} \ No newline at end of file + Task GetAsync(); +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs index d15870e7c1..742da779f6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class EntityExtensionDto { - [Serializable] - public class EntityExtensionDto - { - public Dictionary Properties { get; set; } + public Dictionary Properties { get; set; } - public Dictionary Configuration { get; set; } - } -} \ No newline at end of file + public Dictionary Configuration { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs index 4f2dc526eb..24e9ec9a2f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ExtensionEnumDto { - [Serializable] - public class ExtensionEnumDto - { - public List Fields { get; set; } + public List Fields { get; set; } - public string LocalizationResource { get; set; } - } -} \ No newline at end of file + public string LocalizationResource { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs index ddc7c349d4..13728d8409 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs @@ -1,12 +1,11 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ExtensionEnumFieldDto { - [Serializable] - public class ExtensionEnumFieldDto - { - public string Name { get; set; } + public string Name { get; set; } - public object Value { get; set; } - } -} \ No newline at end of file + public object Value { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiCreateDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiCreateDto.cs index a2a588a358..9179ce7e2e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiCreateDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiCreateDto.cs @@ -1,13 +1,12 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ExtensionPropertyApiCreateDto { - [Serializable] - public class ExtensionPropertyApiCreateDto - { - /// - /// Default: true. - /// - public bool IsAvailable { get; set; } = true; - } -} \ No newline at end of file + /// + /// Default: true. + /// + public bool IsAvailable { get; set; } = true; +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiDto.cs index a9b4ec5b24..920e66141a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiDto.cs @@ -1,23 +1,22 @@ using JetBrains.Annotations; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +public class ExtensionPropertyApiDto { - public class ExtensionPropertyApiDto - { - [NotNull] - public ExtensionPropertyApiGetDto OnGet { get; set; } + [NotNull] + public ExtensionPropertyApiGetDto OnGet { get; set; } - [NotNull] - public ExtensionPropertyApiCreateDto OnCreate { get; set; } + [NotNull] + public ExtensionPropertyApiCreateDto OnCreate { get; set; } - [NotNull] - public ExtensionPropertyApiUpdateDto OnUpdate { get; set; } + [NotNull] + public ExtensionPropertyApiUpdateDto OnUpdate { get; set; } - public ExtensionPropertyApiDto() - { - OnGet = new ExtensionPropertyApiGetDto(); - OnCreate = new ExtensionPropertyApiCreateDto(); - OnUpdate = new ExtensionPropertyApiUpdateDto(); - } + public ExtensionPropertyApiDto() + { + OnGet = new ExtensionPropertyApiGetDto(); + OnCreate = new ExtensionPropertyApiCreateDto(); + OnUpdate = new ExtensionPropertyApiUpdateDto(); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiGetDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiGetDto.cs index e50a45d15a..2a9f71295c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiGetDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiGetDto.cs @@ -1,13 +1,12 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ExtensionPropertyApiGetDto { - [Serializable] - public class ExtensionPropertyApiGetDto - { - /// - /// Default: true. - /// - public bool IsAvailable { get; set; } = true; - } -} \ No newline at end of file + /// + /// Default: true. + /// + public bool IsAvailable { get; set; } = true; +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiUpdateDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiUpdateDto.cs index a517b258da..d234fe37b0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiUpdateDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiUpdateDto.cs @@ -1,13 +1,12 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ExtensionPropertyApiUpdateDto { - [Serializable] - public class ExtensionPropertyApiUpdateDto - { - /// - /// Default: true. - /// - public bool IsAvailable { get; set; } = true; - } -} \ No newline at end of file + /// + /// Default: true. + /// + public bool IsAvailable { get; set; } = true; +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyAttributeDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyAttributeDto.cs index 12941a3b89..492756168d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyAttributeDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyAttributeDto.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ExtensionPropertyAttributeDto { - [Serializable] - public class ExtensionPropertyAttributeDto - { - public string TypeSimple { get; set; } + public string TypeSimple { get; set; } - public Dictionary Config { get; set; } - } -} \ No newline at end of file + public Dictionary Config { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyDto.cs index b1ba6f7ea9..a1fb628e1a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyDto.cs @@ -2,26 +2,25 @@ using System.Collections.Generic; using JetBrains.Annotations; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ExtensionPropertyDto { - [Serializable] - public class ExtensionPropertyDto - { - public string Type { get; set; } + public string Type { get; set; } + + public string TypeSimple { get; set; } + + [CanBeNull] + public LocalizableStringDto DisplayName { get; set; } - public string TypeSimple { get; set; } + public ExtensionPropertyApiDto Api { get; set; } - [CanBeNull] - public LocalizableStringDto DisplayName { get; set; } + public ExtensionPropertyUiDto Ui { get; set; } - public ExtensionPropertyApiDto Api { get; set; } - - public ExtensionPropertyUiDto Ui { get; set; } + public List Attributes { get; set; } - public List Attributes { get; set; } + public Dictionary Configuration { get; set; } - public Dictionary Configuration { get; set; } - - public object DefaultValue { get; set; } - } -} \ No newline at end of file + public object DefaultValue { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiDto.cs index caa5d67957..cd43df5446 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiDto.cs @@ -1,13 +1,12 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ExtensionPropertyUiDto { - [Serializable] - public class ExtensionPropertyUiDto - { - public ExtensionPropertyUiTableDto OnTable { get; set; } - public ExtensionPropertyUiFormDto OnCreateForm { get; set; } - public ExtensionPropertyUiFormDto OnEditForm { get; set; } - public ExtensionPropertyUiLookupDto Lookup { get; set; } - } -} \ No newline at end of file + public ExtensionPropertyUiTableDto OnTable { get; set; } + public ExtensionPropertyUiFormDto OnCreateForm { get; set; } + public ExtensionPropertyUiFormDto OnEditForm { get; set; } + public ExtensionPropertyUiLookupDto Lookup { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiFormDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiFormDto.cs index c4f6f5211c..2eb506c13d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiFormDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiFormDto.cs @@ -1,10 +1,9 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ExtensionPropertyUiFormDto { - [Serializable] - public class ExtensionPropertyUiFormDto - { - public bool IsVisible { get; set; } - } -} \ No newline at end of file + public bool IsVisible { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiLookupDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiLookupDto.cs index 035178df1e..b9bc94399c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiLookupDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiLookupDto.cs @@ -1,14 +1,13 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ExtensionPropertyUiLookupDto { - [Serializable] - public class ExtensionPropertyUiLookupDto - { - public string Url { get; set; } - public string ResultListPropertyName { get; set; } - public string DisplayPropertyName { get; set; } - public string ValuePropertyName { get; set; } - public string FilterParamName { get; set; } - } -} \ No newline at end of file + public string Url { get; set; } + public string ResultListPropertyName { get; set; } + public string DisplayPropertyName { get; set; } + public string ValuePropertyName { get; set; } + public string FilterParamName { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiTableDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiTableDto.cs index 4c9e4a7ce0..cba33fb2f8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiTableDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiTableDto.cs @@ -1,10 +1,9 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ExtensionPropertyUiTableDto { - [Serializable] - public class ExtensionPropertyUiTableDto - { - public bool IsVisible { get; set; } - } -} \ No newline at end of file + public bool IsVisible { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/LocalizableStringDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/LocalizableStringDto.cs index b80352d791..8127a90bd7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/LocalizableStringDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/LocalizableStringDto.cs @@ -1,21 +1,20 @@ using System; using JetBrains.Annotations; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class LocalizableStringDto { - [Serializable] - public class LocalizableStringDto - { - [NotNull] - public string Name { get; private set; } + [NotNull] + public string Name { get; private set; } - [CanBeNull] - public string Resource { get; set; } + [CanBeNull] + public string Resource { get; set; } - public LocalizableStringDto([NotNull] string name, string resource = null) - { - Name = Check.NotNullOrEmpty(name, nameof(name)); - Resource = resource; - } + public LocalizableStringDto([NotNull] string name, string resource = null) + { + Name = Check.NotNullOrEmpty(name, nameof(name)); + Resource = resource; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ModuleExtensionDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ModuleExtensionDto.cs index e077596f59..aadd08cd28 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ModuleExtensionDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ModuleExtensionDto.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ModuleExtensionDto { - [Serializable] - public class ModuleExtensionDto - { - public Dictionary Entities { get; set; } + public Dictionary Entities { get; set; } - public Dictionary Configuration { get; set; } - } -} \ No newline at end of file + public Dictionary Configuration { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs index 0338bbd4dc..f63935847b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; + +[Serializable] +public class ObjectExtensionsDto { - [Serializable] - public class ObjectExtensionsDto - { - public Dictionary Modules { get; set; } - - public Dictionary Enums { get; set; } - } -} \ No newline at end of file + public Dictionary Modules { get; set; } + + public Dictionary Enums { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TimingDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TimingDto.cs index bd95bfba0d..ca03407ee5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TimingDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TimingDto.cs @@ -1,24 +1,23 @@ -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +public class TimingDto { - public class TimingDto - { - public TimeZone TimeZone { get; set; } - } + public TimeZone TimeZone { get; set; } +} - public class TimeZone - { - public IanaTimeZone Iana { get; set; } +public class TimeZone +{ + public IanaTimeZone Iana { get; set; } - public WindowsTimeZone Windows { get; set; } - } + public WindowsTimeZone Windows { get; set; } +} - public class WindowsTimeZone - { - public string TimeZoneId { get; set; } - } +public class WindowsTimeZone +{ + public string TimeZoneId { get; set; } +} - public class IanaTimeZone - { - public string TimeZoneName { get; set; } - } +public class IanaTimeZone +{ + public string TimeZoneName { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/CurrentTenantDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/CurrentTenantDto.cs index 06b24f1235..b8b361e7c8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/CurrentTenantDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/CurrentTenantDto.cs @@ -2,14 +2,13 @@ using System.Collections.Generic; using System.Text; -namespace Volo.Abp.AspNetCore.Mvc.MultiTenancy +namespace Volo.Abp.AspNetCore.Mvc.MultiTenancy; + +public class CurrentTenantDto { - public class CurrentTenantDto - { - public Guid? Id { get; set; } + public Guid? Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public bool IsAvailable { get; set; } - } + public bool IsAvailable { get; set; } } 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 6f834b18ea..64bcc77554 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 @@ -1,16 +1,15 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.MultiTenancy +namespace Volo.Abp.AspNetCore.Mvc.MultiTenancy; + +[Serializable] +public class FindTenantResultDto { - [Serializable] - public class FindTenantResultDto - { - public bool Success { get; set; } + public bool Success { get; set; } - public Guid? TenantId { get; set; } + public Guid? TenantId { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public bool IsActive { get; set; } - } + public bool IsActive { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/IAbpTenantAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/IAbpTenantAppService.cs index eb02e84ceb..feb4fc4af7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/IAbpTenantAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/IAbpTenantAppService.cs @@ -2,12 +2,11 @@ using System.Threading.Tasks; using Volo.Abp.Application.Services; -namespace Volo.Abp.AspNetCore.Mvc.MultiTenancy +namespace Volo.Abp.AspNetCore.Mvc.MultiTenancy; + +public interface IAbpTenantAppService : IApplicationService { - public interface IAbpTenantAppService : IApplicationService - { - Task FindTenantByNameAsync(string name); + Task FindTenantByNameAsync(string name); - Task FindTenantByIdAsync(Guid id); - } -} \ No newline at end of file + Task FindTenantByIdAsync(Guid id); +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/MultiTenancyInfoDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/MultiTenancyInfoDto.cs index 165a0e5c21..c08e4ab60d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/MultiTenancyInfoDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/MultiTenancyInfoDto.cs @@ -2,10 +2,9 @@ using System.Collections.Generic; using System.Text; -namespace Volo.Abp.AspNetCore.Mvc.MultiTenancy +namespace Volo.Abp.AspNetCore.Mvc.MultiTenancy; + +public class MultiTenancyInfoDto { - public class MultiTenancyInfoDto - { - public bool IsEnabled { get; set; } - } + public bool IsEnabled { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/AbpAspNetCoreMvcUiBootstrapModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/AbpAspNetCoreMvcUiBootstrapModule.cs index 889ffdc96e..fe06c4e084 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/AbpAspNetCoreMvcUiBootstrapModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/AbpAspNetCoreMvcUiBootstrapModule.cs @@ -1,17 +1,16 @@ using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; + +[DependsOn(typeof(AbpAspNetCoreMvcUiModule))] +public class AbpAspNetCoreMvcUiBootstrapModule : AbpModule { - [DependsOn(typeof(AbpAspNetCoreMvcUiModule))] - public class AbpAspNetCoreMvcUiBootstrapModule : AbpModule + public override void ConfigureServices(ServiceConfigurationContext context) { - public override void ConfigureServices(ServiceConfigurationContext context) + Configure(options => { - Configure(options => - { - options.FileSets.AddEmbedded("Volo.Abp.AspNetCore.Mvc.UI.Bootstrap"); - }); - } + options.FileSets.AddEmbedded("Volo.Abp.AspNetCore.Mvc.UI.Bootstrap"); + }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Microsoft/AspNetCore/Razor/TagHelpers/AbpTagHelperAttributeListExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Microsoft/AspNetCore/Razor/TagHelpers/AbpTagHelperAttributeListExtensions.cs index ae6684a6a6..81aefb7382 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Microsoft/AspNetCore/Razor/TagHelpers/AbpTagHelperAttributeListExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Microsoft/AspNetCore/Razor/TagHelpers/AbpTagHelperAttributeListExtensions.cs @@ -4,54 +4,54 @@ using System.Linq; using Microsoft.AspNetCore.Razor.TagHelpers; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers //TODO: Move to AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers namespace +; + +public static class AbpTagHelperAttributeListExtensions { - public static class AbpTagHelperAttributeListExtensions + public static void AddClass(this TagHelperAttributeList attributes, string className) { - public static void AddClass(this TagHelperAttributeList attributes, string className) + if (string.IsNullOrWhiteSpace(className)) { - if (string.IsNullOrWhiteSpace(className)) - { - return; - } - - var classAttribute = attributes["class"]; - if (classAttribute == null) - { - attributes.Add("class", className); - } - else - { - var existingClasses = classAttribute.Value.ToString().Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList(); - existingClasses.AddIfNotContains(className); - attributes.SetAttribute("class", string.Join(" ", existingClasses)); - } + return; } - public static void RemoveClass(this TagHelperAttributeList attributes, string className) + var classAttribute = attributes["class"]; + if (classAttribute == null) { - if (string.IsNullOrWhiteSpace(className)) - { - return; - } - - var classAttribute = attributes["class"]; - if (classAttribute == null) - { - return; - } + attributes.Add("class", className); + } + else + { + var existingClasses = classAttribute.Value.ToString().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + existingClasses.AddIfNotContains(className); + attributes.SetAttribute("class", string.Join(" ", existingClasses)); + } + } - var classList = classAttribute.Value.ToString().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList(); - classList.RemoveAll(c => c == className); + public static void RemoveClass(this TagHelperAttributeList attributes, string className) + { + if (string.IsNullOrWhiteSpace(className)) + { + return; + } - attributes.SetAttribute("class", classList.JoinAsString(" ")); + var classAttribute = attributes["class"]; + if (classAttribute == null) + { + return; } - public static void AddIfNotContains(this TagHelperAttributeList attributes, string name, object value) + var classList = classAttribute.Value.ToString().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + classList.RemoveAll(c => c == className); + + attributes.SetAttribute("class", classList.JoinAsString(" ")); + } + + public static void AddIfNotContains(this TagHelperAttributeList attributes, string name, object value) + { + if (!attributes.ContainsName(name)) { - if (!attributes.ContainsName(name)) - { - attributes.Add(name, value); - } + attributes.Add(name, value); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelper.cs index 1188d68056..5b9c1430f2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelper.cs @@ -6,44 +6,43 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.DependencyInjection; using Volo.Abp.Threading; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers; + +public abstract class AbpTagHelper : TagHelper, ITransientDependency { - public abstract class AbpTagHelper : TagHelper, ITransientDependency + +} + +public abstract class AbpTagHelper : AbpTagHelper + where TTagHelper : AbpTagHelper + where TService : class, IAbpTagHelperService +{ + protected TService Service { get; } + + public override int Order => Service.Order; + + [HtmlAttributeNotBound] + [ViewContext] + public ViewContext ViewContext { get; set; } + + protected AbpTagHelper(TService service) + { + Service = service; + Service.As>().TagHelper = (TTagHelper)this; + } + + public override void Init(TagHelperContext context) { + Service.Init(context); + } + public override void Process(TagHelperContext context, TagHelperOutput output) + { + Service.Process(context, output); } - public abstract class AbpTagHelper : AbpTagHelper - where TTagHelper : AbpTagHelper - where TService : class, IAbpTagHelperService + public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { - protected TService Service { get; } - - public override int Order => Service.Order; - - [HtmlAttributeNotBound] - [ViewContext] - public ViewContext ViewContext { get; set; } - - protected AbpTagHelper(TService service) - { - Service = service; - Service.As>().TagHelper = (TTagHelper)this; - } - - public override void Init(TagHelperContext context) - { - Service.Init(context); - } - - public override void Process(TagHelperContext context, TagHelperOutput output) - { - Service.Process(context, output); - } - - public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) - { - return Service.ProcessAsync(context, output); - } + return Service.ProcessAsync(context, output); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperLocalizer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperLocalizer.cs index 700a5f4ee9..c4314134be 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperLocalizer.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperLocalizer.cs @@ -6,45 +6,44 @@ using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Mvc.Localization; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers; + +public class AbpTagHelperLocalizer : IAbpTagHelperLocalizer { - public class AbpTagHelperLocalizer : IAbpTagHelperLocalizer - { - private readonly IStringLocalizerFactory _stringLocalizerFactory; - private readonly AbpMvcDataAnnotationsLocalizationOptions _options; + private readonly IStringLocalizerFactory _stringLocalizerFactory; + private readonly AbpMvcDataAnnotationsLocalizationOptions _options; - public AbpTagHelperLocalizer(IOptions options, IStringLocalizerFactory stringLocalizerFactory) - { - _stringLocalizerFactory = stringLocalizerFactory; - _options = options.Value; - } + public AbpTagHelperLocalizer(IOptions options, IStringLocalizerFactory stringLocalizerFactory) + { + _stringLocalizerFactory = stringLocalizerFactory; + _options = options.Value; + } - public string GetLocalizedText(string text, ModelExplorer explorer) - { - var localizer = GetLocalizerOrNull(explorer); - return localizer == null - ? text - : localizer[text].Value; - } + public string GetLocalizedText(string text, ModelExplorer explorer) + { + var localizer = GetLocalizerOrNull(explorer); + return localizer == null + ? text + : localizer[text].Value; + } - public IStringLocalizer GetLocalizerOrNull(ModelExplorer explorer) - { - return GetLocalizerOrNull(explorer.Container.ModelType.Assembly); - } + public IStringLocalizer GetLocalizerOrNull(ModelExplorer explorer) + { + return GetLocalizerOrNull(explorer.Container.ModelType.Assembly); + } - public IStringLocalizer GetLocalizerOrNull(Assembly assembly) - { - var resourceType = GetResourceType(assembly); - return resourceType == null - ? _stringLocalizerFactory.CreateDefaultOrNull() - : _stringLocalizerFactory.Create(resourceType); - } + public IStringLocalizer GetLocalizerOrNull(Assembly assembly) + { + var resourceType = GetResourceType(assembly); + return resourceType == null + ? _stringLocalizerFactory.CreateDefaultOrNull() + : _stringLocalizerFactory.Create(resourceType); + } - private Type GetResourceType(Assembly assembly) - { - return _options - .AssemblyResources - .GetOrDefault(assembly); - } + private Type GetResourceType(Assembly assembly) + { + return _options + .AssemblyResources + .GetOrDefault(assembly); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs index b53b2b4d56..b9c3aa92e6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs @@ -1,46 +1,45 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers; + +//TODO: Refactor this class, extract bootstrap functionality! +public abstract class AbpTagHelperService : IAbpTagHelperService + where TTagHelper : TagHelper { - //TODO: Refactor this class, extract bootstrap functionality! - public abstract class AbpTagHelperService : IAbpTagHelperService - where TTagHelper : TagHelper + protected const string FormGroupContents = "FormGroupContents"; + protected const string TabItems = "TabItems"; + protected const string AccordionItems = "AccordionItems"; + protected const string BreadcrumbItemsContent = "BreadcrumbItemsContent"; + protected const string CarouselItemsContent = "CarouselItemsContent"; + protected const string TabItemsDataTogglePlaceHolder = "{_data_toggle_Placeholder_}"; + protected const string TabItemNamePlaceHolder = "{_Tab_Tag_Name_Placeholder_}"; + protected const string AbpFormContentPlaceHolder = "{_AbpFormContentPlaceHolder_}"; + protected const string AbpTabItemActivePlaceholder = "{_Tab_Active_Placeholder_}"; + protected const string AbpTabDropdownItemsActivePlaceholder = "{_Tab_DropDown_Items_Placeholder_}"; + protected const string AbpTabItemShowActivePlaceholder = "{_Tab_Show_Active_Placeholder_}"; + protected const string AbpBreadcrumbItemActivePlaceholder = "{_Breadcrumb_Active_Placeholder_}"; + protected const string AbpCarouselItemActivePlaceholder = "{_CarouselItem_Active_Placeholder_}"; + protected const string AbpTabItemSelectedPlaceholder = "{_Tab_Selected_Placeholder_}"; + protected const string AbpAccordionParentIdPlaceholder = "{_Parent_Accordion_Id_}"; + + public TTagHelper TagHelper { get; internal set; } + + public virtual int Order { get; } + + public virtual void Init(TagHelperContext context) + { + + } + + public virtual void Process(TagHelperContext context, TagHelperOutput output) + { + + } + + public virtual Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { - protected const string FormGroupContents = "FormGroupContents"; - protected const string TabItems = "TabItems"; - protected const string AccordionItems = "AccordionItems"; - protected const string BreadcrumbItemsContent = "BreadcrumbItemsContent"; - protected const string CarouselItemsContent = "CarouselItemsContent"; - protected const string TabItemsDataTogglePlaceHolder = "{_data_toggle_Placeholder_}"; - protected const string TabItemNamePlaceHolder = "{_Tab_Tag_Name_Placeholder_}"; - protected const string AbpFormContentPlaceHolder = "{_AbpFormContentPlaceHolder_}"; - protected const string AbpTabItemActivePlaceholder = "{_Tab_Active_Placeholder_}"; - protected const string AbpTabDropdownItemsActivePlaceholder = "{_Tab_DropDown_Items_Placeholder_}"; - protected const string AbpTabItemShowActivePlaceholder = "{_Tab_Show_Active_Placeholder_}"; - protected const string AbpBreadcrumbItemActivePlaceholder = "{_Breadcrumb_Active_Placeholder_}"; - protected const string AbpCarouselItemActivePlaceholder = "{_CarouselItem_Active_Placeholder_}"; - protected const string AbpTabItemSelectedPlaceholder = "{_Tab_Selected_Placeholder_}"; - protected const string AbpAccordionParentIdPlaceholder = "{_Parent_Accordion_Id_}"; - - public TTagHelper TagHelper { get; internal set; } - - public virtual int Order { get; } - - public virtual void Init(TagHelperContext context) - { - - } - - public virtual void Process(TagHelperContext context, TagHelperOutput output) - { - - } - - public virtual Task ProcessAsync(TagHelperContext context, TagHelperOutput output) - { - Process(context, output); - return Task.CompletedTask; - } + Process(context, output); + return Task.CompletedTask; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertHeaderTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertHeaderTagHelper.cs index b32acb7b1b..d86ef557fa 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertHeaderTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertHeaderTagHelper.cs @@ -1,19 +1,18 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert; + +[HtmlTargetElement("h1", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] +[HtmlTargetElement("h2", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] +[HtmlTargetElement("h3", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] +[HtmlTargetElement("h4", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] +[HtmlTargetElement("h5", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] +[HtmlTargetElement("h6", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] +public class AbpAlertHeaderTagHelper : AbpTagHelper { - [HtmlTargetElement("h1", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] - [HtmlTargetElement("h2", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] - [HtmlTargetElement("h3", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] - [HtmlTargetElement("h4", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] - [HtmlTargetElement("h5", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] - [HtmlTargetElement("h6", ParentTag = "abp-alert", TagStructure = TagStructure.NormalOrSelfClosing)] - public class AbpAlertHeaderTagHelper : AbpTagHelper + public AbpAlertHeaderTagHelper(AbpAlertHeaderTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpAlertHeaderTagHelper(AbpAlertHeaderTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertHeaderTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertHeaderTagHelperService.cs index 1e3b9e7c2a..46d3bbf5ef 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertHeaderTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertHeaderTagHelperService.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert; + +public class AbpAlertHeaderTagHelperService : AbpTagHelperService { - public class AbpAlertHeaderTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.AddClass("alert-heading"); - } + output.Attributes.AddClass("alert-heading"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertLinkTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertLinkTagHelper.cs index 17e9a335b9..af659ac401 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertLinkTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertLinkTagHelper.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert; + +[HtmlTargetElement("a", Attributes = "abp-alert-link", TagStructure = TagStructure.NormalOrSelfClosing)] +public class AbpAlertLinkTagHelper : AbpTagHelper { - [HtmlTargetElement("a", Attributes = "abp-alert-link", TagStructure = TagStructure.NormalOrSelfClosing)] - public class AbpAlertLinkTagHelper : AbpTagHelper + public AbpAlertLinkTagHelper(AbpAlertLinkTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpAlertLinkTagHelper(AbpAlertLinkTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertLinkTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertLinkTagHelperService.cs index 79372688c3..40746eac6c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertLinkTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertLinkTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert; + +public class AbpAlertLinkTagHelperService : AbpTagHelperService { - public class AbpAlertLinkTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.AddClass("alert-link"); - output.Attributes.RemoveAll("abp-alert-link"); - } + output.Attributes.AddClass("alert-link"); + output.Attributes.RemoveAll("abp-alert-link"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertTagHelper.cs index f4d0c33ced..19728a99b3 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertTagHelper.cs @@ -1,17 +1,16 @@ using Volo.Abp.AspNetCore.Mvc.UI.Alerts; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert; + +public class AbpAlertTagHelper : AbpTagHelper { - public class AbpAlertTagHelper : AbpTagHelper - { - public AlertType AlertType { get; set; } = AlertType.Default; + public AlertType AlertType { get; set; } = AlertType.Default; - public bool? Dismissible { get; set; } + public bool? Dismissible { get; set; } - public AbpAlertTagHelper(AbpAlertTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpAlertTagHelper(AbpAlertTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertTagHelperService.cs index a8d2342978..6d465f4bb4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Alert/AbpAlertTagHelperService.cs @@ -3,48 +3,47 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Alerts; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert; + +public class AbpAlertTagHelperService : AbpTagHelperService { - public class AbpAlertTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "div"; - output.TagMode = TagMode.StartTagAndEndTag; + output.TagName = "div"; + output.TagMode = TagMode.StartTagAndEndTag; + + AddClasses(context, output); + AddDismissButtonIfDismissible(context, output); + } + + protected virtual void AddClasses(TagHelperContext context, TagHelperOutput output) + { + output.Attributes.Add("role", "alert"); + output.Attributes.AddClass("alert"); - AddClasses(context, output); - AddDismissButtonIfDismissible(context, output); + if (TagHelper.AlertType != AlertType.Default) + { + output.Attributes.AddClass("alert-" + TagHelper.AlertType.ToString().ToLowerInvariant()); } - protected virtual void AddClasses(TagHelperContext context, TagHelperOutput output) + if (TagHelper.Dismissible ?? false) { - output.Attributes.Add("role", "alert"); - output.Attributes.AddClass("alert"); - - if (TagHelper.AlertType != AlertType.Default) - { - output.Attributes.AddClass("alert-" + TagHelper.AlertType.ToString().ToLowerInvariant()); - } - - if (TagHelper.Dismissible ?? false) - { - output.Attributes.AddClass("alert-dismissible"); - output.Attributes.AddClass("fade"); - output.Attributes.AddClass("show"); - } + output.Attributes.AddClass("alert-dismissible"); + output.Attributes.AddClass("fade"); + output.Attributes.AddClass("show"); } + } - protected virtual void AddDismissButtonIfDismissible(TagHelperContext context, TagHelperOutput output) + protected virtual void AddDismissButtonIfDismissible(TagHelperContext context, TagHelperOutput output) + { + if (!TagHelper.Dismissible ?? true) { - if (!TagHelper.Dismissible ?? true) - { - return; - } + return; + } - var buttonAsHtml = - ""; + var buttonAsHtml = + ""; - output.PostContent.SetHtmlContent(buttonAsHtml); - } + output.PostContent.SetHtmlContent(buttonAsHtml); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeTagHelper.cs index bfb83f7363..82da24d088 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeTagHelper.cs @@ -1,24 +1,22 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Badge -{ +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Badge; - [HtmlTargetElement("a", Attributes = "abp-badge")] - [HtmlTargetElement("span", Attributes = "abp-badge")] - [HtmlTargetElement("a", Attributes = "abp-badge-pill")] - [HtmlTargetElement("span", Attributes = "abp-badge-pill")] - public class AbpBadgeTagHelper : AbpTagHelper - { - [HtmlAttributeName("abp-badge")] - public AbpBadgeType BadgeType { get; set; } = AbpBadgeType._; +[HtmlTargetElement("a", Attributes = "abp-badge")] +[HtmlTargetElement("span", Attributes = "abp-badge")] +[HtmlTargetElement("a", Attributes = "abp-badge-pill")] +[HtmlTargetElement("span", Attributes = "abp-badge-pill")] +public class AbpBadgeTagHelper : AbpTagHelper +{ + [HtmlAttributeName("abp-badge")] + public AbpBadgeType BadgeType { get; set; } = AbpBadgeType._; - [HtmlAttributeName("abp-badge-pill")] - public AbpBadgeType BadgePillType { get; set; } = AbpBadgeType._; + [HtmlAttributeName("abp-badge-pill")] + public AbpBadgeType BadgePillType { get; set; } = AbpBadgeType._; - public AbpBadgeTagHelper(AbpBadgeTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpBadgeTagHelper(AbpBadgeTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeTagHelperService.cs index 14aeefeb25..384b7383ec 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeTagHelperService.cs @@ -1,39 +1,38 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Badge +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Badge; + +public class AbpBadgeTagHelperService : AbpTagHelperService { - public class AbpBadgeTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - SetBadgeClass(context, output); - SetBadgeStyle(context, output); - } - - protected virtual void SetBadgeStyle(TagHelperContext context, TagHelperOutput output) - { - var badgeType = GetBadgeType(context, output); + SetBadgeClass(context, output); + SetBadgeStyle(context, output); + } - if (badgeType != AbpBadgeType.Default && badgeType != AbpBadgeType._) - { - output.Attributes.AddClass("bg-" + badgeType.ToString().ToLowerInvariant()); - } - } + protected virtual void SetBadgeStyle(TagHelperContext context, TagHelperOutput output) + { + var badgeType = GetBadgeType(context, output); - protected virtual void SetBadgeClass(TagHelperContext context, TagHelperOutput output) + if (badgeType != AbpBadgeType.Default && badgeType != AbpBadgeType._) { - output.Attributes.AddClass("badge"); - - if (TagHelper.BadgePillType != AbpBadgeType._) - { - output.Attributes.AddClass("rounded-pill"); - } + output.Attributes.AddClass("bg-" + badgeType.ToString().ToLowerInvariant()); } + } + + protected virtual void SetBadgeClass(TagHelperContext context, TagHelperOutput output) + { + output.Attributes.AddClass("badge"); - protected virtual AbpBadgeType GetBadgeType(TagHelperContext context, TagHelperOutput output) + if (TagHelper.BadgePillType != AbpBadgeType._) { - return TagHelper.BadgeType != AbpBadgeType._ ? TagHelper.BadgeType : TagHelper.BadgePillType; + output.Attributes.AddClass("rounded-pill"); } } + + protected virtual AbpBadgeType GetBadgeType(TagHelperContext context, TagHelperOutput output) + { + return TagHelper.BadgeType != AbpBadgeType._ ? TagHelper.BadgeType : TagHelper.BadgePillType; + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeType.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeType.cs index ffc78d377a..77dafac397 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Badge/AbpBadgeType.cs @@ -1,16 +1,15 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Badge +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Badge; + +public enum AbpBadgeType { - public enum AbpBadgeType - { - _, - Default, - Primary, - Secondary, - Success, - Danger, - Warning, - Info, - Light, - Dark - } -} \ No newline at end of file + _, + Default, + Primary, + Secondary, + Success, + Danger, + Warning, + Info, + Light, + Dark +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteFooterTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteFooterTagHelper.cs index d4619390ef..ac3a6342c5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteFooterTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteFooterTagHelper.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote; + +[HtmlTargetElement("footer", ParentTag = "blockquote")] +public class AbpBlockquoteFooterTagHelper : AbpTagHelper { - [HtmlTargetElement("footer", ParentTag = "blockquote")] - public class AbpBlockquoteFooterTagHelper : AbpTagHelper + public AbpBlockquoteFooterTagHelper(AbpBlockquoteFooterTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpBlockquoteFooterTagHelper(AbpBlockquoteFooterTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteFooterTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteFooterTagHelperService.cs index 6280f5de99..55d9d0673e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteFooterTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteFooterTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote; + +public class AbpBlockquoteFooterTagHelperService : AbpTagHelperService { - public class AbpBlockquoteFooterTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.AddClass("blockquote-footer"); - } - + output.Attributes.AddClass("blockquote-footer"); } -} \ No newline at end of file + +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteParagraphTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteParagraphTagHelper.cs index 0e915f2a36..8734cc52e8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteParagraphTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteParagraphTagHelper.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote; + +[HtmlTargetElement("p", ParentTag = "blockquote")] +public class AbpBlockquoteParagraphTagHelper : AbpTagHelper { - [HtmlTargetElement("p", ParentTag = "blockquote")] - public class AbpBlockquoteParagraphTagHelper : AbpTagHelper + public AbpBlockquoteParagraphTagHelper(AbpBlockquoteParagraphTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpBlockquoteParagraphTagHelper(AbpBlockquoteParagraphTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteParagraphTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteParagraphTagHelperService.cs index d3ce37cbc5..513cee7833 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteParagraphTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteParagraphTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote; + +public class AbpBlockquoteParagraphTagHelperService : AbpTagHelperService { - public class AbpBlockquoteParagraphTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.AddClass("mb-0"); - } - + output.Attributes.AddClass("mb-0"); } -} \ No newline at end of file + +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteTagHelper.cs index 74e2a10e2d..72835b53e0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteTagHelper.cs @@ -1,11 +1,10 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote; + +public class AbpBlockquoteTagHelper : AbpTagHelper { - public class AbpBlockquoteTagHelper : AbpTagHelper + public AbpBlockquoteTagHelper(AbpBlockquoteTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpBlockquoteTagHelper(AbpBlockquoteTagHelperService tagHelperService) - : base(tagHelperService) - { - - } + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteTagHelperService.cs index 6c26747d11..53fa7b0b5d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Blockquote/AbpBlockquoteTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Blockquote; + +public class AbpBlockquoteTagHelperService : AbpTagHelperService { - public class AbpBlockquoteTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.AddClass("blockquote"); - output.TagName = "blockquote"; - } + output.Attributes.AddClass("blockquote"); + output.TagName = "blockquote"; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderTagHelper.cs index bc7a6f9829..f701ca4c2d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderTagHelper.cs @@ -1,16 +1,15 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border; + +[HtmlTargetElement(Attributes = "abp-border")] +public class AbpBorderTagHelper : AbpTagHelper { - [HtmlTargetElement(Attributes = "abp-border")] - public class AbpBorderTagHelper : AbpTagHelper - { - public AbpBorderType AbpBorder { get; set; } = AbpBorderType.Default; + public AbpBorderType AbpBorder { get; set; } = AbpBorderType.Default; - public AbpBorderTagHelper(AbpBorderTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpBorderTagHelper(AbpBorderTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderTagHelperService.cs index 3bf4bc16d4..f8446a493d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderTagHelperService.cs @@ -1,91 +1,90 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border; + +public class AbpBorderTagHelperService : AbpTagHelperService { - public class AbpBorderTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - var borderAttributeAsString = TagHelper.AbpBorder.ToString().ToLowerInvariant(); + var borderAttributeAsString = TagHelper.AbpBorder.ToString().ToLowerInvariant(); - var borderClass = "border" + GetBorderDirection(context, output, borderAttributeAsString) + GetExtensionIfBorderIsSubtractive(context, output, borderAttributeAsString); + var borderClass = "border" + GetBorderDirection(context, output, borderAttributeAsString) + GetExtensionIfBorderIsSubtractive(context, output, borderAttributeAsString); - output.Attributes.AddClass(borderClass); + output.Attributes.AddClass(borderClass); - SetBorderType(context, output, borderAttributeAsString); - } + SetBorderType(context, output, borderAttributeAsString); + } - protected virtual string GetBorderDirection(TagHelperContext context, TagHelperOutput output, string borderAttributeAsString) + protected virtual string GetBorderDirection(TagHelperContext context, TagHelperOutput output, string borderAttributeAsString) + { + if (borderAttributeAsString.Contains("top")) { - if (borderAttributeAsString.Contains("top")) - { - return "-top"; - } - if (borderAttributeAsString.Contains("right")) - { - return "-right"; - } - if (borderAttributeAsString.Contains("left")) - { - return "-left"; - } - if (borderAttributeAsString.Contains("bottom")) - { - return "-bottom"; - } - - return ""; + return "-top"; } - - protected virtual string GetExtensionIfBorderIsSubtractive(TagHelperContext context, TagHelperOutput output, string borderAttributeAsString) + if (borderAttributeAsString.Contains("right")) + { + return "-right"; + } + if (borderAttributeAsString.Contains("left")) { - if (borderAttributeAsString.Contains("_0")) - { - return "-0"; - } + return "-left"; + } + if (borderAttributeAsString.Contains("bottom")) + { + return "-bottom"; + } + + return ""; + } - return ""; + protected virtual string GetExtensionIfBorderIsSubtractive(TagHelperContext context, TagHelperOutput output, string borderAttributeAsString) + { + if (borderAttributeAsString.Contains("_0")) + { + return "-0"; } - protected virtual void SetBorderType(TagHelperContext context, TagHelperOutput output, string borderAttributeAsString) + return ""; + } + + protected virtual void SetBorderType(TagHelperContext context, TagHelperOutput output, string borderAttributeAsString) + { + if (borderAttributeAsString.Contains("primary")) + { + output.Attributes.AddClass("border-primary"); + } + if (borderAttributeAsString.Contains("secondary")) + { + output.Attributes.AddClass("border-secondary"); + } + if (borderAttributeAsString.Contains("success")) + { + output.Attributes.AddClass("border-success"); + } + if (borderAttributeAsString.Contains("danger")) + { + output.Attributes.AddClass("border-danger"); + } + if (borderAttributeAsString.Contains("warning")) + { + output.Attributes.AddClass("border-warning"); + } + if (borderAttributeAsString.Contains("info")) + { + output.Attributes.AddClass("border-info"); + } + if (borderAttributeAsString.Contains("light")) + { + output.Attributes.AddClass("border-light"); + } + if (borderAttributeAsString.Contains("dark")) + { + output.Attributes.AddClass("border-dark"); + } + if (borderAttributeAsString.Contains("white")) { - if (borderAttributeAsString.Contains("primary")) - { - output.Attributes.AddClass("border-primary"); - } - if (borderAttributeAsString.Contains("secondary")) - { - output.Attributes.AddClass("border-secondary"); - } - if (borderAttributeAsString.Contains("success")) - { - output.Attributes.AddClass("border-success"); - } - if (borderAttributeAsString.Contains("danger")) - { - output.Attributes.AddClass("border-danger"); - } - if (borderAttributeAsString.Contains("warning")) - { - output.Attributes.AddClass("border-warning"); - } - if (borderAttributeAsString.Contains("info")) - { - output.Attributes.AddClass("border-info"); - } - if (borderAttributeAsString.Contains("light")) - { - output.Attributes.AddClass("border-light"); - } - if (borderAttributeAsString.Contains("dark")) - { - output.Attributes.AddClass("border-dark"); - } - if (borderAttributeAsString.Contains("white")) - { - output.Attributes.AddClass("border-white"); - } + output.Attributes.AddClass("border-white"); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderType.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderType.cs index 14b1684876..c59780971b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpBorderType.cs @@ -1,106 +1,105 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border; + +public enum AbpBorderType { - public enum AbpBorderType - { - Default, - _0, - Primary, - Secondary, - Success, - Danger, - Warning, - Info, - Light, - Dark, - White, - Primary_0, - Secondary_0, - Success_0, - Danger_0, - Warning_0, - Info_0, - Light_0, - Dark_0, - White_0, - Top, - Top_0, - Top_Primary, - Top_Secondary, - Top_Success, - Top_Danger, - Top_Warning, - Top_Info, - Top_Light, - Top_Dark, - Top_White, - Top_Primary_0, - Top_Secondary_0, - Top_Success_0, - Top_Danger_0, - Top_Warning_0, - Top_Info_0, - Top_Light_0, - Top_Dark_0, - Top_White_0, - Right, - Right_0, - Right_Primary, - Right_Secondary, - Right_Success, - Right_Danger, - Right_Warning, - Right_Info, - Right_Light, - Right_Dark, - Right_White, - Right_Primary_0, - Right_Secondary_0, - Right_Success_0, - Right_Danger_0, - Right_Warning_0, - Right_Info_0, - Right_Light_0, - Right_Dark_0, - Right_White_0, - Left, - Left_0, - Left_Primary, - Left_Secondary, - Left_Success, - Left_Danger, - Left_Warning, - Left_Info, - Left_Light, - Left_Dark, - Left_White, - Left_Primary_0, - Left_Secondary_0, - Left_Success_0, - Left_Danger_0, - Left_Warning_0, - Left_Info_0, - Left_Light_0, - Left_Dark_0, - Left_White_0, - Bottom, - Bottom_0, - Bottom_Primary, - Bottom_Secondary, - Bottom_Success, - Bottom_Danger, - Bottom_Warning, - Bottom_Info, - Bottom_Light, - Bottom_Dark, - Bottom_White, - Bottom_Primary_0, - Bottom_Secondary_0, - Bottom_Success_0, - Bottom_Danger_0, - Bottom_Warning_0, - Bottom_Info_0, - Bottom_Light_0, - Bottom_Dark_0, - Bottom_White_0, - } -} \ No newline at end of file + Default, + _0, + Primary, + Secondary, + Success, + Danger, + Warning, + Info, + Light, + Dark, + White, + Primary_0, + Secondary_0, + Success_0, + Danger_0, + Warning_0, + Info_0, + Light_0, + Dark_0, + White_0, + Top, + Top_0, + Top_Primary, + Top_Secondary, + Top_Success, + Top_Danger, + Top_Warning, + Top_Info, + Top_Light, + Top_Dark, + Top_White, + Top_Primary_0, + Top_Secondary_0, + Top_Success_0, + Top_Danger_0, + Top_Warning_0, + Top_Info_0, + Top_Light_0, + Top_Dark_0, + Top_White_0, + Right, + Right_0, + Right_Primary, + Right_Secondary, + Right_Success, + Right_Danger, + Right_Warning, + Right_Info, + Right_Light, + Right_Dark, + Right_White, + Right_Primary_0, + Right_Secondary_0, + Right_Success_0, + Right_Danger_0, + Right_Warning_0, + Right_Info_0, + Right_Light_0, + Right_Dark_0, + Right_White_0, + Left, + Left_0, + Left_Primary, + Left_Secondary, + Left_Success, + Left_Danger, + Left_Warning, + Left_Info, + Left_Light, + Left_Dark, + Left_White, + Left_Primary_0, + Left_Secondary_0, + Left_Success_0, + Left_Danger_0, + Left_Warning_0, + Left_Info_0, + Left_Light_0, + Left_Dark_0, + Left_White_0, + Bottom, + Bottom_0, + Bottom_Primary, + Bottom_Secondary, + Bottom_Success, + Bottom_Danger, + Bottom_Warning, + Bottom_Info, + Bottom_Light, + Bottom_Dark, + Bottom_White, + Bottom_Primary_0, + Bottom_Secondary_0, + Bottom_Success_0, + Bottom_Danger_0, + Bottom_Warning_0, + Bottom_Info_0, + Bottom_Light_0, + Bottom_Dark_0, + Bottom_White_0, +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedTagHelper.cs index f8c3489cc9..6dc0ecd305 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedTagHelper.cs @@ -1,16 +1,15 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border; + +[HtmlTargetElement(Attributes = "abp-rounded")] +public class AbpRoundedTagHelper : AbpTagHelper { - [HtmlTargetElement(Attributes = "abp-rounded")] - public class AbpRoundedTagHelper : AbpTagHelper - { - public AbpRoundedType AbpRounded { get; set; } = AbpRoundedType.Default; + public AbpRoundedType AbpRounded { get; set; } = AbpRoundedType.Default; - public AbpRoundedTagHelper(AbpRoundedTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpRoundedTagHelper(AbpRoundedTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedTagHelperService.cs index 89436785ba..798380e411 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedTagHelperService.cs @@ -1,20 +1,19 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border; + +public class AbpRoundedTagHelperService : AbpTagHelperService { - public class AbpRoundedTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - var roundedClass = "rounded"; - - if (TagHelper.AbpRounded != AbpRoundedType.Default) - { - roundedClass += "-" + TagHelper.AbpRounded.ToString().ToLowerInvariant().Replace("_",""); - } + var roundedClass = "rounded"; - output.Attributes.AddClass(roundedClass); + if (TagHelper.AbpRounded != AbpRoundedType.Default) + { + roundedClass += "-" + TagHelper.AbpRounded.ToString().ToLowerInvariant().Replace("_", ""); } + + output.Attributes.AddClass(roundedClass); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedType.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedType.cs index 1910ffc714..ab4620f0f5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Border/AbpRoundedType.cs @@ -1,12 +1,11 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border; + +public enum AbpRoundedType { - public enum AbpRoundedType - { - Default, - _0, - Top, - Right, - Left, - Bottom - } -} \ No newline at end of file + Default, + _0, + Top, + Right, + Left, + Bottom +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelper.cs index e27781479f..71ce3406fa 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelper.cs @@ -1,17 +1,16 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb; + +public class AbpBreadcrumbItemTagHelper : AbpTagHelper { - public class AbpBreadcrumbItemTagHelper : AbpTagHelper - { - public string Href { get; set; } + public string Href { get; set; } - public string Title { get; set; } + public string Title { get; set; } - public bool Active { get; set; } + public bool Active { get; set; } - public AbpBreadcrumbItemTagHelper(AbpBreadcrumbItemTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpBreadcrumbItemTagHelper(AbpBreadcrumbItemTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs index 3fa8a27472..9935b90903 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs @@ -5,49 +5,48 @@ using System.Text.Encodings.Web; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb; + +public class AbpBreadcrumbItemTagHelperService : AbpTagHelperService { - public class AbpBreadcrumbItemTagHelperService : AbpTagHelperService - { - private readonly HtmlEncoder _encoder; + private readonly HtmlEncoder _encoder; - public AbpBreadcrumbItemTagHelperService(HtmlEncoder encoder) - { - _encoder = encoder; - } + public AbpBreadcrumbItemTagHelperService(HtmlEncoder encoder) + { + _encoder = encoder; + } - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "li"; - output.TagMode = TagMode.StartTagAndEndTag; - output.Attributes.AddClass("breadcrumb-item"); - output.Attributes.AddClass(AbpBreadcrumbItemActivePlaceholder); + public override void Process(TagHelperContext context, TagHelperOutput output) + { + output.TagName = "li"; + output.TagMode = TagMode.StartTagAndEndTag; + output.Attributes.AddClass("breadcrumb-item"); + output.Attributes.AddClass(AbpBreadcrumbItemActivePlaceholder); - var list = context.GetValue>(BreadcrumbItemsContent); + var list = context.GetValue>(BreadcrumbItemsContent); - output.Content.SetHtmlContent(GetInnerHtml(context, output)); + output.Content.SetHtmlContent(GetInnerHtml(context, output)); - list.Add(new BreadcrumbItem - { - Html = output.Render(_encoder), - Active = TagHelper.Active - }); + list.Add(new BreadcrumbItem + { + Html = output.Render(_encoder), + Active = TagHelper.Active + }); - output.SuppressOutput(); - } + output.SuppressOutput(); + } - protected virtual string GetInnerHtml(TagHelperContext context, TagHelperOutput output) + protected virtual string GetInnerHtml(TagHelperContext context, TagHelperOutput output) + { + if (string.IsNullOrWhiteSpace(TagHelper.Href)) { - if (string.IsNullOrWhiteSpace(TagHelper.Href)) - { - output.Attributes.Add("aria-current", "page"); - return _encoder.Encode(TagHelper.Title); - } - - var link = new TagBuilder("a"); - link.Attributes.Add("href", TagHelper.Href); - link.InnerHtml.AppendHtml(TagHelper.Title); - return link.ToHtmlString(); + output.Attributes.Add("aria-current", "page"); + return _encoder.Encode(TagHelper.Title); } + + var link = new TagBuilder("a"); + link.Attributes.Add("href", TagHelper.Href); + link.InnerHtml.AppendHtml(TagHelper.Title); + return link.ToHtmlString(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbTagHelper.cs index a207d46a49..cdb9f29741 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbTagHelper.cs @@ -1,11 +1,10 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb; + +public class AbpBreadcrumbTagHelper : AbpTagHelper { - public class AbpBreadcrumbTagHelper : AbpTagHelper + public AbpBreadcrumbTagHelper(AbpBreadcrumbTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpBreadcrumbTagHelper(AbpBreadcrumbTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbTagHelperService.cs index 4b433ac495..a23dc6c294 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbTagHelperService.cs @@ -4,66 +4,65 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb; + +public class AbpBreadcrumbTagHelperService : AbpTagHelperService { - public class AbpBreadcrumbTagHelperService : AbpTagHelperService + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "nav"; - output.Attributes.Add("aria-label", "breadcrumb"); - - var list = InitilizeFormGroupContentsContext(context, output); + output.TagName = "nav"; + output.Attributes.Add("aria-label", "breadcrumb"); - await output.GetChildContentAsync(); + var list = InitilizeFormGroupContentsContext(context, output); - var listTagBuilder = GetOlTagBuilder(); + await output.GetChildContentAsync(); - SetInnerList(context, output, list, listTagBuilder); + var listTagBuilder = GetOlTagBuilder(); - output.Content.SetHtmlContent(listTagBuilder); - } - - protected virtual TagBuilder GetOlTagBuilder() - { - var builder = new TagBuilder("ol"); - builder.AddCssClass("breadcrumb"); - return builder; - } + SetInnerList(context, output, list, listTagBuilder); - protected virtual void SetInnerList(TagHelperContext context, TagHelperOutput output, List list, TagBuilder listTagBuilder) - { - SetLastOneActiveIfThereIsNotAny(context, output, list); + output.Content.SetHtmlContent(listTagBuilder); + } - foreach (var breadcrumbItem in list) - { - var htmlPart = SetActiveClassIfActiveAndGetHtml(breadcrumbItem); + protected virtual TagBuilder GetOlTagBuilder() + { + var builder = new TagBuilder("ol"); + builder.AddCssClass("breadcrumb"); + return builder; + } - listTagBuilder.InnerHtml.AppendHtml(htmlPart); - } - } + protected virtual void SetInnerList(TagHelperContext context, TagHelperOutput output, List list, TagBuilder listTagBuilder) + { + SetLastOneActiveIfThereIsNotAny(context, output, list); - protected virtual List InitilizeFormGroupContentsContext(TagHelperContext context, TagHelperOutput output) + foreach (var breadcrumbItem in list) { - var items = new List(); - context.Items[BreadcrumbItemsContent] = items; - return items; - } + var htmlPart = SetActiveClassIfActiveAndGetHtml(breadcrumbItem); - protected virtual void SetLastOneActiveIfThereIsNotAny(TagHelperContext context, TagHelperOutput output, List list) - { - if (list.Count > 0 && !list.Any(bc => bc.Active)) - { - list.Last().Active = true; - } + listTagBuilder.InnerHtml.AppendHtml(htmlPart); } + } - protected virtual string SetActiveClassIfActiveAndGetHtml(BreadcrumbItem item) + protected virtual List InitilizeFormGroupContentsContext(TagHelperContext context, TagHelperOutput output) + { + var items = new List(); + context.Items[BreadcrumbItemsContent] = items; + return items; + } + + protected virtual void SetLastOneActiveIfThereIsNotAny(TagHelperContext context, TagHelperOutput output, List list) + { + if (list.Count > 0 && !list.Any(bc => bc.Active)) { - return item.Active ? - item.Html.Replace(AbpBreadcrumbItemActivePlaceholder, " active") : - item.Html.Replace(AbpBreadcrumbItemActivePlaceholder, ""); + list.Last().Active = true; } + } + protected virtual string SetActiveClassIfActiveAndGetHtml(BreadcrumbItem item) + { + return item.Active ? + item.Html.Replace(AbpBreadcrumbItemActivePlaceholder, " active") : + item.Html.Replace(AbpBreadcrumbItemActivePlaceholder, ""); } + } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/BreadcrumbItem.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/BreadcrumbItem.cs index 7f69abdfca..37a194b906 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/BreadcrumbItem.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/BreadcrumbItem.cs @@ -1,9 +1,8 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb; + +public class BreadcrumbItem { - public class BreadcrumbItem - { - public string Html { get; set; } + public string Html { get; set; } - public bool Active { get; set; } - } + public bool Active { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupDirection.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupDirection.cs index 53f3c3b6f9..9b4e42805d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupDirection.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupDirection.cs @@ -3,11 +3,10 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public enum AbpButtonGroupDirection { - public enum AbpButtonGroupDirection - { - Horizontal, - Vertical - } + Horizontal, + Vertical } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupSize.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupSize.cs index 42635dd232..d54cdcea8c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupSize.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupSize.cs @@ -1,10 +1,9 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public enum AbpButtonGroupSize { - public enum AbpButtonGroupSize - { - Default, - Small, - Medium, - Large - } + Default, + Small, + Medium, + Large } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupTagHelper.cs index b1958fc8af..cc48c96bf9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupTagHelper.cs @@ -1,15 +1,14 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public class AbpButtonGroupTagHelper : AbpTagHelper { - public class AbpButtonGroupTagHelper : AbpTagHelper - { - public AbpButtonGroupDirection Direction { get; set; } = AbpButtonGroupDirection.Horizontal; + public AbpButtonGroupDirection Direction { get; set; } = AbpButtonGroupDirection.Horizontal; - public AbpButtonGroupSize Size { get; set; } = AbpButtonGroupSize.Default; + public AbpButtonGroupSize Size { get; set; } = AbpButtonGroupSize.Default; - public AbpButtonGroupTagHelper(AbpButtonGroupTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpButtonGroupTagHelper(AbpButtonGroupTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupTagHelperService.cs index 3250fe6877..fb646e9941 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonGroupTagHelperService.cs @@ -2,56 +2,55 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public class AbpButtonGroupTagHelperService : AbpTagHelperService { - public class AbpButtonGroupTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - AddButtonGroupClass(context, output); - AddSizeClass(context, output); - AddAttributes(context, output); + AddButtonGroupClass(context, output); + AddSizeClass(context, output); + AddAttributes(context, output); - output.TagName = "div"; - } + output.TagName = "div"; + } - protected virtual void AddSizeClass(TagHelperContext context, TagHelperOutput output) + protected virtual void AddSizeClass(TagHelperContext context, TagHelperOutput output) + { + switch (TagHelper.Size) { - switch (TagHelper.Size) - { - case AbpButtonGroupSize.Default: - break; - case AbpButtonGroupSize.Small: - output.Attributes.AddClass("btn-group-sm"); - break; - case AbpButtonGroupSize.Medium: - output.Attributes.AddClass("btn-group-md"); - break; - case AbpButtonGroupSize.Large: - output.Attributes.AddClass("btn-group-lg"); - break; - } + case AbpButtonGroupSize.Default: + break; + case AbpButtonGroupSize.Small: + output.Attributes.AddClass("btn-group-sm"); + break; + case AbpButtonGroupSize.Medium: + output.Attributes.AddClass("btn-group-md"); + break; + case AbpButtonGroupSize.Large: + output.Attributes.AddClass("btn-group-lg"); + break; } + } - protected virtual void AddButtonGroupClass(TagHelperContext context, TagHelperOutput output) + protected virtual void AddButtonGroupClass(TagHelperContext context, TagHelperOutput output) + { + switch (TagHelper.Direction) { - switch (TagHelper.Direction) - { - case AbpButtonGroupDirection.Horizontal: - output.Attributes.AddClass("btn-group"); - break; - case AbpButtonGroupDirection.Vertical: - output.Attributes.AddClass("btn-group-vertical"); - break; - default: - output.Attributes.AddClass("btn-group"); - break; - } + case AbpButtonGroupDirection.Horizontal: + output.Attributes.AddClass("btn-group"); + break; + case AbpButtonGroupDirection.Vertical: + output.Attributes.AddClass("btn-group-vertical"); + break; + default: + output.Attributes.AddClass("btn-group"); + break; } + } - protected virtual void AddAttributes(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.Add("role", "group"); - } + protected virtual void AddAttributes(TagHelperContext context, TagHelperOutput output) + { + output.Attributes.Add("role", "group"); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonSize.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonSize.cs index b3aae45f6d..96f7b27d1e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonSize.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonSize.cs @@ -1,20 +1,19 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public enum AbpButtonSize { - public enum AbpButtonSize - { - Default, - Small, - Medium, - Large, - [Obsolete("https://getbootstrap.com/docs/5.0/components/buttons/#block-buttons")] - Block, - [Obsolete("https://getbootstrap.com/docs/5.0/components/buttons/#block-buttons")] - Block_Small, - [Obsolete("https://getbootstrap.com/docs/5.0/components/buttons/#block-buttons")] - Block_Medium, - [Obsolete("https://getbootstrap.com/docs/5.0/components/buttons/#block-buttons")] - Block_Large, - } + Default, + Small, + Medium, + Large, + [Obsolete("https://getbootstrap.com/docs/5.0/components/buttons/#block-buttons")] + Block, + [Obsolete("https://getbootstrap.com/docs/5.0/components/buttons/#block-buttons")] + Block_Small, + [Obsolete("https://getbootstrap.com/docs/5.0/components/buttons/#block-buttons")] + Block_Medium, + [Obsolete("https://getbootstrap.com/docs/5.0/components/buttons/#block-buttons")] + Block_Large, } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonSizeExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonSizeExtensions.cs index 5927d06e43..bdd0de72f2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonSizeExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonSizeExtensions.cs @@ -1,26 +1,25 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public static class AbpButtonSizeExtensions { - public static class AbpButtonSizeExtensions + public static string ToClassName(this AbpButtonSize size) { - public static string ToClassName(this AbpButtonSize size) + switch (size) { - switch (size) - { - case AbpButtonSize.Small: - case AbpButtonSize.Block_Small: - return "btn-sm"; - case AbpButtonSize.Medium: - case AbpButtonSize.Block_Medium: - return "btn-md"; - case AbpButtonSize.Large: - case AbpButtonSize.Block_Large: - return "btn-lg"; - case AbpButtonSize.Block: - case AbpButtonSize.Default: - return ""; - default: - throw new AbpException($"Unknown {nameof(AbpButtonSize)}: {size}"); - } + case AbpButtonSize.Small: + case AbpButtonSize.Block_Small: + return "btn-sm"; + case AbpButtonSize.Medium: + case AbpButtonSize.Block_Medium: + return "btn-md"; + case AbpButtonSize.Large: + case AbpButtonSize.Block_Large: + return "btn-lg"; + case AbpButtonSize.Block: + case AbpButtonSize.Default: + return ""; + default: + throw new AbpException($"Unknown {nameof(AbpButtonSize)}: {size}"); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelper.cs index f7ffa84c39..624d750274 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelper.cs @@ -1,31 +1,30 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +[HtmlTargetElement("abp-button", TagStructure = TagStructure.NormalOrSelfClosing)] +public class AbpButtonTagHelper : AbpTagHelper, IButtonTagHelperBase { - [HtmlTargetElement("abp-button", TagStructure = TagStructure.NormalOrSelfClosing)] - public class AbpButtonTagHelper : AbpTagHelper, IButtonTagHelperBase - { - public AbpButtonType ButtonType { get; set; } = AbpButtonType.Default; + public AbpButtonType ButtonType { get; set; } = AbpButtonType.Default; - public AbpButtonSize Size { get; set; } = AbpButtonSize.Default; + public AbpButtonSize Size { get; set; } = AbpButtonSize.Default; - public string BusyText { get; set; } + public string BusyText { get; set; } - public string Text { get; set; } + public string Text { get; set; } - public string Icon { get; set; } + public string Icon { get; set; } - public bool? Disabled { get; set; } + public bool? Disabled { get; set; } - public FontIconType IconType { get; set; } = FontIconType.FontAwesome; + public FontIconType IconType { get; set; } = FontIconType.FontAwesome; - public bool BusyTextIsHtml { get; set; } + public bool BusyTextIsHtml { get; set; } - public AbpButtonTagHelper(AbpButtonTagHelperService service) - : base(service) - { + public AbpButtonTagHelper(AbpButtonTagHelperService service) + : base(service) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelperService.cs index fb74d5c1da..e5576332b3 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelperService.cs @@ -3,58 +3,57 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.Localization; using System; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public class AbpButtonTagHelperService : AbpButtonTagHelperServiceBase { - public class AbpButtonTagHelperService : AbpButtonTagHelperServiceBase + protected const string DataBusyTextAttributeName = "data-busy-text"; + protected const string DataBusyTextIsHtmlAttributeName = "data-busy-text-is-html"; + + protected IStringLocalizer L { get; } + + public AbpButtonTagHelperService(IStringLocalizer localizer) { - protected const string DataBusyTextAttributeName = "data-busy-text"; - protected const string DataBusyTextIsHtmlAttributeName = "data-busy-text-is-html"; + L = localizer; + } - protected IStringLocalizer L { get; } + public override void Process(TagHelperContext context, TagHelperOutput output) + { + base.Process(context, output); + output.TagName = "button"; + AddType(context, output); + AddBusyText(context, output); + AddBusyTextIsHtml(context, output); + } - public AbpButtonTagHelperService(IStringLocalizer localizer) + protected virtual void AddType(TagHelperContext context, TagHelperOutput output) + { + if (output.Attributes.ContainsName("type")) { - L = localizer; + return; } - public override void Process(TagHelperContext context, TagHelperOutput output) - { - base.Process(context, output); - output.TagName = "button"; - AddType(context, output); - AddBusyText(context, output); - AddBusyTextIsHtml(context, output); - } + output.Attributes.Add("type", "button"); + } - protected virtual void AddType(TagHelperContext context, TagHelperOutput output) + protected virtual void AddBusyText(TagHelperContext context, TagHelperOutput output) + { + var busyText = TagHelper.BusyText ?? L["ProcessingWithThreeDot"]; + if (busyText.IsNullOrWhiteSpace()) { - if (output.Attributes.ContainsName("type")) - { - return; - } - - output.Attributes.Add("type", "button"); + return; } - protected virtual void AddBusyText(TagHelperContext context, TagHelperOutput output) - { - var busyText = TagHelper.BusyText ?? L["ProcessingWithThreeDot"]; - if (busyText.IsNullOrWhiteSpace()) - { - return; - } - - output.Attributes.SetAttribute(DataBusyTextAttributeName, busyText); - } + output.Attributes.SetAttribute(DataBusyTextAttributeName, busyText); + } - protected virtual void AddBusyTextIsHtml(TagHelperContext context, TagHelperOutput output) + protected virtual void AddBusyTextIsHtml(TagHelperContext context, TagHelperOutput output) + { + if (!TagHelper.BusyTextIsHtml) { - if (!TagHelper.BusyTextIsHtml) - { - return; - } - - output.Attributes.SetAttribute(DataBusyTextIsHtmlAttributeName, TagHelper.BusyTextIsHtml.ToString().ToLower()); + return; } + + output.Attributes.SetAttribute(DataBusyTextIsHtmlAttributeName, TagHelper.BusyTextIsHtml.ToString().ToLower()); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelperServiceBase.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelperServiceBase.cs index cb795bdc6c..17422327c8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelperServiceBase.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonTagHelperServiceBase.cs @@ -3,82 +3,81 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using System; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public abstract class AbpButtonTagHelperServiceBase : AbpTagHelperService + where TTagHelper : TagHelper, IButtonTagHelperBase { - public abstract class AbpButtonTagHelperServiceBase : AbpTagHelperService - where TTagHelper : TagHelper, IButtonTagHelperBase + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - NormalizeTagMode(context, output); - AddClasses(context, output); - AddIcon(context, output); - AddText(context, output); - AddDisabled(context, output); - } + NormalizeTagMode(context, output); + AddClasses(context, output); + AddIcon(context, output); + AddText(context, output); + AddDisabled(context, output); + } + + protected virtual void NormalizeTagMode(TagHelperContext context, TagHelperOutput output) + { + output.TagMode = TagMode.StartTagAndEndTag; + } + + protected virtual void AddClasses(TagHelperContext context, TagHelperOutput output) + { + output.Attributes.AddClass("btn"); - protected virtual void NormalizeTagMode(TagHelperContext context, TagHelperOutput output) + if (TagHelper.ButtonType != AbpButtonType.Default) { - output.TagMode = TagMode.StartTagAndEndTag; + output.Attributes.AddClass("btn-" + TagHelper.ButtonType.ToString().ToLowerInvariant().Replace("_", "-")); } - protected virtual void AddClasses(TagHelperContext context, TagHelperOutput output) + if (TagHelper.Size != AbpButtonSize.Default) { - output.Attributes.AddClass("btn"); - - if (TagHelper.ButtonType != AbpButtonType.Default) - { - output.Attributes.AddClass("btn-" + TagHelper.ButtonType.ToString().ToLowerInvariant().Replace("_","-")); - } - - if (TagHelper.Size != AbpButtonSize.Default) - { - output.Attributes.AddClass(TagHelper.Size.ToClassName()); - } + output.Attributes.AddClass(TagHelper.Size.ToClassName()); } + } - protected virtual void AddIcon(TagHelperContext context, TagHelperOutput output) + protected virtual void AddIcon(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.Icon.IsNullOrWhiteSpace()) { - if (TagHelper.Icon.IsNullOrWhiteSpace()) - { - return; - } - - var icon = new TagBuilder("i"); - icon.AddCssClass(GetIconClass(context, output)); - output.Content.AppendHtml(icon); - output.Content.Append(" "); + return; } - protected virtual string GetIconClass(TagHelperContext context, TagHelperOutput output) + var icon = new TagBuilder("i"); + icon.AddCssClass(GetIconClass(context, output)); + output.Content.AppendHtml(icon); + output.Content.Append(" "); + } + + protected virtual string GetIconClass(TagHelperContext context, TagHelperOutput output) + { + switch (TagHelper.IconType) { - switch (TagHelper.IconType) - { - case FontIconType.FontAwesome: - return "fa fa-" + TagHelper.Icon; - default: - return TagHelper.Icon; - } + case FontIconType.FontAwesome: + return "fa fa-" + TagHelper.Icon; + default: + return TagHelper.Icon; } + } - protected virtual void AddText(TagHelperContext context, TagHelperOutput output) + protected virtual void AddText(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.Text.IsNullOrWhiteSpace()) { - if (TagHelper.Text.IsNullOrWhiteSpace()) - { - return; - } - - var span = new TagBuilder("span"); - span.InnerHtml.AppendHtml(TagHelper.Text); - output.Content.AppendHtml(span); + return; } - protected virtual void AddDisabled(TagHelperContext context, TagHelperOutput output) + var span = new TagBuilder("span"); + span.InnerHtml.AppendHtml(TagHelper.Text); + output.Content.AppendHtml(span); + } + + protected virtual void AddDisabled(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.Disabled ?? false) { - if (TagHelper.Disabled ?? false) - { - output.Attributes.Add("disabled", "disabled"); - } + output.Attributes.Add("disabled", "disabled"); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonToolbarTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonToolbarTagHelper.cs index 7c716b88f8..afbd6bb53a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonToolbarTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonToolbarTagHelper.cs @@ -1,11 +1,10 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public class AbpButtonToolbarTagHelper : AbpTagHelper { - public class AbpButtonToolbarTagHelper : AbpTagHelper + public AbpButtonToolbarTagHelper(AbpButtonToolbarTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpButtonToolbarTagHelper(AbpButtonToolbarTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonToolbarTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonToolbarTagHelperService.cs index 1581cc8bd3..6033621614 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonToolbarTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonToolbarTagHelperService.cs @@ -1,15 +1,14 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public class AbpButtonToolbarTagHelperService : AbpTagHelperService { - public class AbpButtonToolbarTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "div"; - output.Attributes.AddClass("btn-toolbar"); - output.Attributes.Add("role","toolbar"); - } + output.TagName = "div"; + output.Attributes.AddClass("btn-toolbar"); + output.Attributes.Add("role", "toolbar"); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonType.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonType.cs index a6ced457e0..0e9990e683 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpButtonType.cs @@ -1,24 +1,23 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public enum AbpButtonType { - public enum AbpButtonType - { - Default, - Primary, - Secondary, - Success, - Danger, - Warning, - Info, - Light, - Dark, - Outline_Primary, - Outline_Secondary, - Outline_Success, - Outline_Danger, - Outline_Warning, - Outline_Info, - Outline_Light, - Outline_Dark, - Link - } -} \ No newline at end of file + Default, + Primary, + Secondary, + Success, + Danger, + Warning, + Info, + Light, + Dark, + Outline_Primary, + Outline_Secondary, + Outline_Success, + Outline_Danger, + Outline_Warning, + Outline_Info, + Outline_Light, + Outline_Dark, + Link +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpLinkButtonTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpLinkButtonTagHelper.cs index 9a553a1be5..bf3b4d8bac 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpLinkButtonTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpLinkButtonTagHelper.cs @@ -1,28 +1,27 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +[HtmlTargetElement("a", Attributes = "abp-button", TagStructure = TagStructure.NormalOrSelfClosing)] +[HtmlTargetElement("input", Attributes = "abp-button", TagStructure = TagStructure.WithoutEndTag)] +public class AbpLinkButtonTagHelper : AbpTagHelper, IButtonTagHelperBase { - [HtmlTargetElement("a", Attributes = "abp-button", TagStructure = TagStructure.NormalOrSelfClosing)] - [HtmlTargetElement("input", Attributes = "abp-button", TagStructure = TagStructure.WithoutEndTag)] - public class AbpLinkButtonTagHelper : AbpTagHelper, IButtonTagHelperBase - { - [HtmlAttributeName("abp-button")] - public AbpButtonType ButtonType { get; set; } + [HtmlAttributeName("abp-button")] + public AbpButtonType ButtonType { get; set; } - public AbpButtonSize Size { get; set; } = AbpButtonSize.Default; + public AbpButtonSize Size { get; set; } = AbpButtonSize.Default; - public string Text { get; set; } + public string Text { get; set; } - public string Icon { get; set; } + public string Icon { get; set; } - public bool? Disabled { get; set; } + public bool? Disabled { get; set; } - public FontIconType IconType { get; } = FontIconType.FontAwesome; + public FontIconType IconType { get; } = FontIconType.FontAwesome; - public AbpLinkButtonTagHelper(AbpLinkButtonTagHelperService service) - : base(service) - { + public AbpLinkButtonTagHelper(AbpLinkButtonTagHelperService service) + : base(service) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpLinkButtonTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpLinkButtonTagHelperService.cs index f92e2d5137..44dc996284 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpLinkButtonTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/AbpLinkButtonTagHelperService.cs @@ -1,32 +1,31 @@ using System; using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public class AbpLinkButtonTagHelperService : AbpButtonTagHelperServiceBase { - public class AbpLinkButtonTagHelperService : AbpButtonTagHelperServiceBase + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - base.Process(context, output); - AddType(context, output); - AddRole(context, output); - } + base.Process(context, output); + AddType(context, output); + AddRole(context, output); + } - protected virtual void AddType(TagHelperContext context, TagHelperOutput output) + protected virtual void AddType(TagHelperContext context, TagHelperOutput output) + { + if (!output.Attributes.ContainsName("type") && + output.TagName.Equals("input", StringComparison.InvariantCultureIgnoreCase)) { - if (!output.Attributes.ContainsName("type") && - output.TagName.Equals("input", StringComparison.InvariantCultureIgnoreCase)) - { - output.Attributes.Add("type", "button"); - } + output.Attributes.Add("type", "button"); } + } - protected virtual void AddRole(TagHelperContext context, TagHelperOutput output) + protected virtual void AddRole(TagHelperContext context, TagHelperOutput output) + { + if (!output.Attributes.ContainsName("role")) { - if (!output.Attributes.ContainsName("role")) - { - output.Attributes.Add("role", "button"); - } + output.Attributes.Add("role", "button"); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/IButtonTagHelperBase.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/IButtonTagHelperBase.cs index ee88c4d294..8ba73b2ab9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/IButtonTagHelperBase.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Button/IButtonTagHelperBase.cs @@ -1,17 +1,16 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; + +public interface IButtonTagHelperBase { - public interface IButtonTagHelperBase - { - AbpButtonType ButtonType { get; } + AbpButtonType ButtonType { get; } - AbpButtonSize Size { get; } + AbpButtonSize Size { get; } - string Text { get; } + string Text { get; } - string Icon { get; } + string Icon { get; } - bool? Disabled { get; } + bool? Disabled { get; } - FontIconType IconType { get; } - } -} \ No newline at end of file + FontIconType IconType { get; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundTagHelper.cs index 5e95e8bf87..9b4697c44a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundTagHelper.cs @@ -1,19 +1,18 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +[HtmlTargetElement("abp-card", Attributes = "background")] +[HtmlTargetElement("abp-card-header", Attributes = "background")] +[HtmlTargetElement("abp-card-body", Attributes = "background")] +[HtmlTargetElement("abp-card-footer", Attributes = "background")] +public class AbpCardBackgroundTagHelper : AbpTagHelper { - [HtmlTargetElement("abp-card", Attributes = "background")] - [HtmlTargetElement("abp-card-header", Attributes = "background")] - [HtmlTargetElement("abp-card-body", Attributes = "background")] - [HtmlTargetElement("abp-card-footer", Attributes = "background")] - public class AbpCardBackgroundTagHelper : AbpTagHelper - { - public AbpCardBackgroundType Background { get; set; } = AbpCardBackgroundType.Default; + public AbpCardBackgroundType Background { get; set; } = AbpCardBackgroundType.Default; - public AbpCardBackgroundTagHelper(AbpCardBackgroundTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpCardBackgroundTagHelper(AbpCardBackgroundTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundTagHelperService.cs index 248d737648..af8e0bbf51 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundTagHelperService.cs @@ -1,23 +1,22 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardBackgroundTagHelperService : AbpTagHelperService { - public class AbpCardBackgroundTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - SetBackground(context, output); - } + SetBackground(context, output); + } - protected virtual void SetBackground(TagHelperContext context, TagHelperOutput output) + protected virtual void SetBackground(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.Background == AbpCardBackgroundType.Default) { - if (TagHelper.Background == AbpCardBackgroundType.Default) - { - return; - } - - output.Attributes.AddClass("bg-" + TagHelper.Background.ToString().ToLowerInvariant()); + return; } + + output.Attributes.AddClass("bg-" + TagHelper.Background.ToString().ToLowerInvariant()); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundType.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundType.cs index 2eb9732ce8..da7aca4eec 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBackgroundType.cs @@ -1,15 +1,14 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public enum AbpCardBackgroundType { - public enum AbpCardBackgroundType - { - Default, - Primary, - Secondary, - Success, - Danger, - Warning, - Info, - Light, - Dark, - } + Default, + Primary, + Secondary, + Success, + Danger, + Warning, + Info, + Light, + Dark, } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelper.cs index b28b126cf2..34a14e6f33 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelper.cs @@ -1,15 +1,14 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardBodyTagHelper : AbpTagHelper { - public class AbpCardBodyTagHelper : AbpTagHelper - { - public string Title { get; set; } + public string Title { get; set; } - public string Subtitle { get; set; } + public string Subtitle { get; set; } - public AbpCardBodyTagHelper(AbpCardBodyTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpCardBodyTagHelper(AbpCardBodyTagHelperService tagHelperService) + : base(tagHelperService) + { - } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs index 5707775a3a..172fa778b6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs @@ -3,39 +3,38 @@ using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardBodyTagHelperService : AbpTagHelperService { - public class AbpCardBodyTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "div"; - output.Attributes.AddClass("card-body"); + output.TagName = "div"; + output.Attributes.AddClass("card-body"); - ProcessTitle(output); - ProcessSubtitle(output); - } + ProcessTitle(output); + ProcessSubtitle(output); + } - protected virtual void ProcessTitle(TagHelperOutput output) + protected virtual void ProcessTitle(TagHelperOutput output) + { + if (!TagHelper.Title.IsNullOrWhiteSpace()) { - if (!TagHelper.Title.IsNullOrWhiteSpace()) - { - var cardTitle = new TagBuilder(AbpCardTitleTagHelper.DefaultHeading.ToHtmlTag()); - cardTitle.AddCssClass("card-title"); - cardTitle.InnerHtml.AppendHtml(TagHelper.Title); - output.PreContent.AppendHtml(cardTitle); - } + var cardTitle = new TagBuilder(AbpCardTitleTagHelper.DefaultHeading.ToHtmlTag()); + cardTitle.AddCssClass("card-title"); + cardTitle.InnerHtml.AppendHtml(TagHelper.Title); + output.PreContent.AppendHtml(cardTitle); } + } - protected virtual void ProcessSubtitle(TagHelperOutput output) + protected virtual void ProcessSubtitle(TagHelperOutput output) + { + if (!TagHelper.Subtitle.IsNullOrWhiteSpace()) { - if (!TagHelper.Subtitle.IsNullOrWhiteSpace()) - { - var cardSubtitle = new TagBuilder(AbpCardSubtitleTagHelper.DefaultHeading.ToHtmlTag()); - cardSubtitle.AddCssClass("card-subtitle text-muted mb-2"); - cardSubtitle.InnerHtml.AppendHtml(TagHelper.Subtitle); - output.PreContent.AppendHtml(cardSubtitle); - } + var cardSubtitle = new TagBuilder(AbpCardSubtitleTagHelper.DefaultHeading.ToHtmlTag()); + cardSubtitle.AddCssClass("card-subtitle text-muted mb-2"); + cardSubtitle.InnerHtml.AppendHtml(TagHelper.Subtitle); + output.PreContent.AppendHtml(cardSubtitle); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBorderColorType.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBorderColorType.cs index 0f5ccc3363..8da13ccbb8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBorderColorType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBorderColorType.cs @@ -1,15 +1,14 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public enum AbpCardBorderColorType { - public enum AbpCardBorderColorType - { - Default, - Primary, - Secondary, - Success, - Danger, - Warning, - Info, - Light, - Dark, - } + Default, + Primary, + Secondary, + Success, + Danger, + Warning, + Info, + Light, + Dark, } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardFooterTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardFooterTagHelper.cs index 44b8588576..087c02320e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardFooterTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardFooterTagHelper.cs @@ -1,11 +1,10 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardFooterTagHelper : AbpTagHelper { - public class AbpCardFooterTagHelper : AbpTagHelper + public AbpCardFooterTagHelper(AbpCardFooterTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpCardFooterTagHelper(AbpCardFooterTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardFooterTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardFooterTagHelperService.cs index 2a127a495b..abf4e71020 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardFooterTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardFooterTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardFooterTagHelperService : AbpTagHelperService { - public class AbpCardFooterTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.AddClass("card-footer"); - output.TagName = "div"; - } + output.Attributes.AddClass("card-footer"); + output.TagName = "div"; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardHeaderTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardHeaderTagHelper.cs index 0584d40ced..54d61cf61d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardHeaderTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardHeaderTagHelper.cs @@ -1,10 +1,9 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardHeaderTagHelper : AbpTagHelper { - public class AbpCardHeaderTagHelper : AbpTagHelper + public AbpCardHeaderTagHelper(AbpCardHeaderTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpCardHeaderTagHelper(AbpCardHeaderTagHelperService tagHelperService) - : base(tagHelperService) - { - } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardHeaderTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardHeaderTagHelperService.cs index 2f68196607..96e8f91a83 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardHeaderTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardHeaderTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardHeaderTagHelperService : AbpTagHelperService { - public class AbpCardHeaderTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "div"; - output.Attributes.AddClass("card-header"); - } + output.TagName = "div"; + output.Attributes.AddClass("card-header"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImagePosition.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImagePosition.cs index b3396a513b..f101e44313 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImagePosition.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImagePosition.cs @@ -1,9 +1,8 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public enum AbpCardImagePosition { - public enum AbpCardImagePosition - { - None, - Top, - Bottom - } -} \ No newline at end of file + None, + Top, + Bottom +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImagePositionExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImagePositionExtensions.cs index 7a3f74449c..5ab3676651 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImagePositionExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImagePositionExtensions.cs @@ -1,20 +1,19 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public static class AbpCardImagePositionExtensions { - public static class AbpCardImagePositionExtensions + public static string ToClassName(this AbpCardImagePosition position) { - public static string ToClassName(this AbpCardImagePosition position) + switch (position) { - switch (position) - { - case AbpCardImagePosition.None: - return "card-img"; - case AbpCardImagePosition.Top: - return "card-img-top"; - case AbpCardImagePosition.Bottom: - return "card-img-bottom"; - default: - throw new AbpException("Unknown AbpCardImagePosition: " + position); - } + case AbpCardImagePosition.None: + return "card-img"; + case AbpCardImagePosition.Top: + return "card-img-top"; + case AbpCardImagePosition.Bottom: + return "card-img-bottom"; + default: + throw new AbpException("Unknown AbpCardImagePosition: " + position); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImageTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImageTagHelper.cs index 4a173968d0..0610cfa150 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImageTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImageTagHelper.cs @@ -1,18 +1,17 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +[HtmlTargetElement("img", Attributes = "abp-card-image", TagStructure = TagStructure.WithoutEndTag)] +[HtmlTargetElement("abp-image", Attributes = "abp-card-image", TagStructure = TagStructure.WithoutEndTag)] +public class AbpCardImageTagHelper : AbpTagHelper { - [HtmlTargetElement("img", Attributes = "abp-card-image", TagStructure = TagStructure.WithoutEndTag)] - [HtmlTargetElement("abp-image", Attributes = "abp-card-image", TagStructure = TagStructure.WithoutEndTag)] - public class AbpCardImageTagHelper : AbpTagHelper - { - [HtmlAttributeName("abp-card-image")] - public AbpCardImagePosition Position { get; set; } = AbpCardImagePosition.Top; + [HtmlAttributeName("abp-card-image")] + public AbpCardImagePosition Position { get; set; } = AbpCardImagePosition.Top; - public AbpCardImageTagHelper(AbpCardImageTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpCardImageTagHelper(AbpCardImageTagHelperService tagHelperService) + : base(tagHelperService) + { - } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImageTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImageTagHelperService.cs index 65cb8ff2bc..4d91cd30c9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImageTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardImageTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardImageTagHelperService : AbpTagHelperService { - public class AbpCardImageTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.AddClass(TagHelper.Position.ToClassName()); - output.Attributes.RemoveAll("abp-card-image"); - } + output.Attributes.AddClass(TagHelper.Position.ToClassName()); + output.Attributes.RemoveAll("abp-card-image"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardLinkTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardLinkTagHelper.cs index 8f7f29bbb5..9626dd2bd4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardLinkTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardLinkTagHelper.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +[HtmlTargetElement("a", Attributes = "abp-card-link")] +public class AbpCardLinkTagHelper : AbpTagHelper { - [HtmlTargetElement("a", Attributes = "abp-card-link")] - public class AbpCardLinkTagHelper : AbpTagHelper + public AbpCardLinkTagHelper(AbpCardLinkTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpCardLinkTagHelper(AbpCardLinkTagHelperService tagHelperService) - : base(tagHelperService) - { - - } + } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardLinkTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardLinkTagHelperService.cs index 5cdba7cdba..d4dc775479 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardLinkTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardLinkTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardLinkTagHelperService : AbpTagHelperService { - public class AbpCardLinkTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.AddClass("card-link"); - output.Attributes.RemoveAll("abp-card-link"); - } + output.Attributes.AddClass("card-link"); + output.Attributes.RemoveAll("abp-card-link"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardSubtitleTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardSubtitleTagHelper.cs index cc6ef506dd..e740af2664 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardSubtitleTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardSubtitleTagHelper.cs @@ -1,15 +1,14 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardSubtitleTagHelper : AbpTagHelper { - public class AbpCardSubtitleTagHelper : AbpTagHelper - { - public static HtmlHeadingType DefaultHeading { get; set; } = HtmlHeadingType.H6; + public static HtmlHeadingType DefaultHeading { get; set; } = HtmlHeadingType.H6; - public HtmlHeadingType Heading { get; set; } = DefaultHeading; + public HtmlHeadingType Heading { get; set; } = DefaultHeading; - public AbpCardSubtitleTagHelper(AbpCardSubtitleTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpCardSubtitleTagHelper(AbpCardSubtitleTagHelperService tagHelperService) + : base(tagHelperService) + { - } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardSubtitleTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardSubtitleTagHelperService.cs index 8f9084d3bf..5710c81000 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardSubtitleTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardSubtitleTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardSubtitleTagHelperService : AbpTagHelperService { - public class AbpCardSubtitleTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = TagHelper.Heading.ToHtmlTag(); - output.Attributes.AddClass("card-subtitle"); - } + output.TagName = TagHelper.Heading.ToHtmlTag(); + output.Attributes.AddClass("card-subtitle"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelper.cs index c186e8857c..5b568e16a6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelper.cs @@ -1,12 +1,11 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardTagHelper : AbpTagHelper { - public class AbpCardTagHelper : AbpTagHelper - { - public AbpCardBorderColorType Border { get; set; } = AbpCardBorderColorType.Default; + public AbpCardBorderColorType Border { get; set; } = AbpCardBorderColorType.Default; - public AbpCardTagHelper(AbpCardTagHelperService tagHelperService) - : base(tagHelperService) - { - } + public AbpCardTagHelper(AbpCardTagHelperService tagHelperService) + : base(tagHelperService) + { } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelperService.cs index 29a462797d..6169154904 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelperService.cs @@ -1,25 +1,24 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardTagHelperService : AbpTagHelperService { - public class AbpCardTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "div"; - output.Attributes.AddClass("card"); + output.TagName = "div"; + output.Attributes.AddClass("card"); - SetBorder(context, output); - } - protected virtual void SetBorder(TagHelperContext context, TagHelperOutput output) + SetBorder(context, output); + } + protected virtual void SetBorder(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.Border == AbpCardBorderColorType.Default) { - if (TagHelper.Border == AbpCardBorderColorType.Default) - { - return; - } - - output.Attributes.AddClass("border-" + TagHelper.Border.ToString().ToLowerInvariant()); + return; } + + output.Attributes.AddClass("border-" + TagHelper.Border.ToString().ToLowerInvariant()); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorTagHelper.cs index f9787b1f18..45ddf9725a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorTagHelper.cs @@ -1,19 +1,18 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +[HtmlTargetElement("abp-card", Attributes = "text-color")] +[HtmlTargetElement("abp-card-header", Attributes = "text-color")] +[HtmlTargetElement("abp-card-body", Attributes = "text-color")] +[HtmlTargetElement("abp-card-footer", Attributes = "text-color")] +public class AbpCardTextColorTagHelper : AbpTagHelper { - [HtmlTargetElement("abp-card", Attributes = "text-color")] - [HtmlTargetElement("abp-card-header", Attributes = "text-color")] - [HtmlTargetElement("abp-card-body", Attributes = "text-color")] - [HtmlTargetElement("abp-card-footer", Attributes = "text-color")] - public class AbpCardTextColorTagHelper : AbpTagHelper - { - public AbpCardTextColorType TextColor { get; set; } = AbpCardTextColorType.Default; + public AbpCardTextColorType TextColor { get; set; } = AbpCardTextColorType.Default; - public AbpCardTextColorTagHelper(AbpCardTextColorTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpCardTextColorTagHelper(AbpCardTextColorTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorTagHelperService.cs index 36484a33f8..04be1fa5bb 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorTagHelperService.cs @@ -1,23 +1,22 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardTextColorTagHelperService : AbpTagHelperService { - public class AbpCardTextColorTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - SetTextColor(context, output); - } + SetTextColor(context, output); + } - protected virtual void SetTextColor(TagHelperContext context, TagHelperOutput output) + protected virtual void SetTextColor(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.TextColor == AbpCardTextColorType.Default) { - if (TagHelper.TextColor == AbpCardTextColorType.Default) - { - return; - } - - output.Attributes.AddClass("text-" + TagHelper.TextColor.ToString().ToLowerInvariant()); + return; } + + output.Attributes.AddClass("text-" + TagHelper.TextColor.ToString().ToLowerInvariant()); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorType.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorType.cs index eb415945b0..45df26f761 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextColorType.cs @@ -1,16 +1,15 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public enum AbpCardTextColorType { - public enum AbpCardTextColorType - { - Default, - White, - Primary, - Secondary, - Success, - Danger, - Warning, - Info, - Light, - Dark - } + Default, + White, + Primary, + Secondary, + Success, + Danger, + Warning, + Info, + Light, + Dark } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextTagHelper.cs index 00e2468dfe..6cb434b17a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextTagHelper.cs @@ -1,10 +1,9 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardTextTagHelper : AbpTagHelper { - public class AbpCardTextTagHelper : AbpTagHelper + public AbpCardTextTagHelper(AbpCardTextTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpCardTextTagHelper(AbpCardTextTagHelperService tagHelperService) - : base(tagHelperService) - { - } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextTagHelperService.cs index ba2d619126..41195ef966 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTextTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardTextTagHelperService : AbpTagHelperService { - public class AbpCardTextTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "p"; - output.Attributes.AddClass("card-text"); - } + output.TagName = "p"; + output.Attributes.AddClass("card-text"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTitleTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTitleTagHelper.cs index 410a64d76d..ba3b6a41e1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTitleTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTitleTagHelper.cs @@ -1,14 +1,13 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardTitleTagHelper : AbpTagHelper { - public class AbpCardTitleTagHelper : AbpTagHelper - { - public static HtmlHeadingType DefaultHeading { get; set; } = HtmlHeadingType.H5; + public static HtmlHeadingType DefaultHeading { get; set; } = HtmlHeadingType.H5; - public HtmlHeadingType Heading { get; set; } = DefaultHeading; + public HtmlHeadingType Heading { get; set; } = DefaultHeading; - public AbpCardTitleTagHelper(AbpCardTitleTagHelperService tagHelperService) - : base(tagHelperService) - { - } + public AbpCardTitleTagHelper(AbpCardTitleTagHelperService tagHelperService) + : base(tagHelperService) + { } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTitleTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTitleTagHelperService.cs index 9ad27a49a0..2977fc42e9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTitleTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTitleTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +public class AbpCardTitleTagHelperService : AbpTagHelperService { - public class AbpCardTitleTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = TagHelper.Heading.ToHtmlTag(); - output.Attributes.AddClass("card-title"); - } + output.TagName = TagHelper.Heading.ToHtmlTag(); + output.Attributes.AddClass("card-title"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelper.cs index e8e983b34a..65a47b73c4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelper.cs @@ -1,21 +1,20 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel; + +public class AbpCarouselItemTagHelper : AbpTagHelper { - public class AbpCarouselItemTagHelper : AbpTagHelper - { - public bool? Active { get; set; } + public bool? Active { get; set; } - public string Src { get; set; } + public string Src { get; set; } - public string Alt { get; set; } + public string Alt { get; set; } - public string CaptionTitle { get; set; } + public string CaptionTitle { get; set; } - public string Caption { get; set; } + public string Caption { get; set; } - public AbpCarouselItemTagHelper(AbpCarouselItemTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpCarouselItemTagHelper(AbpCarouselItemTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelperService.cs index 1af2b759a4..a8a3030e95 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelperService.cs @@ -5,79 +5,78 @@ using System.Text.Encodings.Web; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel; + +public class AbpCarouselItemTagHelperService : AbpTagHelperService { - public class AbpCarouselItemTagHelperService : AbpTagHelperService - { - private readonly HtmlEncoder _encoder; + private readonly HtmlEncoder _encoder; - public AbpCarouselItemTagHelperService(HtmlEncoder encoder) - { - _encoder = encoder; - } + public AbpCarouselItemTagHelperService(HtmlEncoder encoder) + { + _encoder = encoder; + } - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "div"; - output.Attributes.AddClass("carousel-item"); - output.Attributes.AddClass(AbpCarouselItemActivePlaceholder); + public override void Process(TagHelperContext context, TagHelperOutput output) + { + output.TagName = "div"; + output.Attributes.AddClass("carousel-item"); + output.Attributes.AddClass(AbpCarouselItemActivePlaceholder); - SetInnerImgTag(context, output); - SetActive(context, output); - AddCaption(context, output); + SetInnerImgTag(context, output); + SetActive(context, output); + AddCaption(context, output); - AddToContext(context, output); + AddToContext(context, output); - output.SuppressOutput(); - } + output.SuppressOutput(); + } - private void AddToContext(TagHelperContext context, TagHelperOutput output) - { - var getOutputAsHtml = output.Render(_encoder); + private void AddToContext(TagHelperContext context, TagHelperOutput output) + { + var getOutputAsHtml = output.Render(_encoder); - var itemList = context.GetValue>(CarouselItemsContent); + var itemList = context.GetValue>(CarouselItemsContent); - itemList.Add(new CarouselItem(getOutputAsHtml, TagHelper.Active ?? false)); - } + itemList.Add(new CarouselItem(getOutputAsHtml, TagHelper.Active ?? false)); + } - protected virtual void SetInnerImgTag(TagHelperContext context, TagHelperOutput output) - { - var img = new TagBuilder("img"); - img.AddCssClass("d-block w-100"); - img.Attributes.Add("src", TagHelper.Src); - img.Attributes.Add("alt", TagHelper.Alt); + protected virtual void SetInnerImgTag(TagHelperContext context, TagHelperOutput output) + { + var img = new TagBuilder("img"); + img.AddCssClass("d-block w-100"); + img.Attributes.Add("src", TagHelper.Src); + img.Attributes.Add("alt", TagHelper.Alt); - output.Content.SetHtmlContent(img); - } + output.Content.SetHtmlContent(img); + } - protected virtual void SetActive(TagHelperContext context, TagHelperOutput output) + protected virtual void SetActive(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.Active ?? false) { - if (TagHelper.Active??false) - { - output.Attributes.AddClass("active"); - } + output.Attributes.AddClass("active"); } + } - protected virtual void AddCaption(TagHelperContext context, TagHelperOutput output) + protected virtual void AddCaption(TagHelperContext context, TagHelperOutput output) + { + if (string.IsNullOrWhiteSpace(TagHelper.Caption) && string.IsNullOrWhiteSpace(TagHelper.CaptionTitle)) { - if (string.IsNullOrWhiteSpace(TagHelper.Caption) && string.IsNullOrWhiteSpace(TagHelper.CaptionTitle)) - { - return; - } - - var title = new TagBuilder("h5"); - title.InnerHtml.AppendHtml(TagHelper.CaptionTitle); + return; + } - var caption = new TagBuilder("p"); - caption.InnerHtml.AppendHtml(TagHelper.Caption); + var title = new TagBuilder("h5"); + title.InnerHtml.AppendHtml(TagHelper.CaptionTitle); - var wrapper = new TagBuilder("div"); - wrapper.AddCssClass("carousel-caption d-none d-md-block"); - wrapper.InnerHtml.AppendHtml(title); - wrapper.InnerHtml.AppendHtml(caption); + var caption = new TagBuilder("p"); + caption.InnerHtml.AppendHtml(TagHelper.Caption); - output.PostContent.SetHtmlContent(wrapper); - } + var wrapper = new TagBuilder("div"); + wrapper.AddCssClass("carousel-caption d-none d-md-block"); + wrapper.InnerHtml.AppendHtml(title); + wrapper.InnerHtml.AppendHtml(caption); + output.PostContent.SetHtmlContent(wrapper); } + } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelper.cs index becb13ca47..e9931241c2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelper.cs @@ -1,19 +1,18 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel; + +public class AbpCarouselTagHelper : AbpTagHelper { - public class AbpCarouselTagHelper : AbpTagHelper - { - public string Id { get; set; } + public string Id { get; set; } - public bool? Crossfade { get; set; } + public bool? Crossfade { get; set; } - public bool? Controls { get; set; } + public bool? Controls { get; set; } - public bool? Indicators { get; set; } + public bool? Indicators { get; set; } - public AbpCarouselTagHelper(AbpCarouselTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpCarouselTagHelper(AbpCarouselTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs index bb3c38f12b..9264737b7b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs @@ -8,174 +8,173 @@ using System.Linq; using System.Threading.Tasks; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel; + +public class AbpCarouselTagHelperService : AbpTagHelperService { - public class AbpCarouselTagHelperService : AbpTagHelperService + protected IStringLocalizer L { get; } + + public AbpCarouselTagHelperService(IStringLocalizer localizer) { - protected IStringLocalizer L { get; } + L = localizer; + } - public AbpCarouselTagHelperService(IStringLocalizer localizer) - { - L = localizer; - } + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + output.TagName = "div"; - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "div"; + SetRandomIdIfNotProvided(); + AddBasicAttributes(context, output); - SetRandomIdIfNotProvided(); - AddBasicAttributes(context, output); + var itemList = InitilizeCarouselItemsContentsContext(context, output); - var itemList = InitilizeCarouselItemsContentsContext(context, output); + await output.GetChildContentAsync(); - await output.GetChildContentAsync(); + SetOneItemAsActive(context, output, itemList); + SetItems(context, output, itemList); + SetControls(context, output, itemList); + SetIndicators(context, output, itemList); + } - SetOneItemAsActive(context, output, itemList); - SetItems(context, output, itemList); - SetControls(context, output, itemList); - SetIndicators(context, output, itemList); - } + protected virtual List InitilizeCarouselItemsContentsContext(TagHelperContext context, TagHelperOutput output) + { + var items = new List(); + context.Items[CarouselItemsContent] = items; + return items; + } - protected virtual List InitilizeCarouselItemsContentsContext(TagHelperContext context, TagHelperOutput output) - { - var items = new List(); - context.Items[CarouselItemsContent] = items; - return items; - } + protected virtual void SetItems(TagHelperContext context, TagHelperOutput output, List itemList) + { + var wrapper = new TagBuilder("div"); + wrapper.AddCssClass("carousel-inner"); - protected virtual void SetItems(TagHelperContext context, TagHelperOutput output, List itemList) + foreach (var carouselItem in itemList) { - var wrapper = new TagBuilder("div"); - wrapper.AddCssClass("carousel-inner"); + SetActiveIfActive(carouselItem); - foreach (var carouselItem in itemList) - { - SetActiveIfActive(carouselItem); + wrapper.InnerHtml.AppendHtml(carouselItem.Html); + } - wrapper.InnerHtml.AppendHtml(carouselItem.Html); - } + output.Content.SetHtmlContent(wrapper); + } - output.Content.SetHtmlContent(wrapper); + protected virtual void SetControls(TagHelperContext context, TagHelperOutput output, List itemList) + { + if (!TagHelper.Controls ?? false) + { + return; } - protected virtual void SetControls(TagHelperContext context, TagHelperOutput output, List itemList) - { - if (!TagHelper.Controls ?? false) - { - return; - } + // create 'previous' item + var prevIcon = new TagBuilder("span"); + prevIcon.AddCssClass("carousel-control-prev-icon"); + prevIcon.Attributes.Add("aria-hidden", "true"); + + var prevText = new TagBuilder("span"); + prevText.AddCssClass("visually-hidden"); + prevText.InnerHtml.Append(L["Previous"].Value); + + var prevAnchor = new TagBuilder("a"); + prevAnchor.AddCssClass("carousel-control-prev"); + prevAnchor.Attributes.Add("href", "#" + TagHelper.Id); + prevAnchor.Attributes.Add("role", "button"); + prevAnchor.Attributes.Add("data-bs-slide", "prev"); + + prevAnchor.InnerHtml.AppendHtml(prevIcon); + prevAnchor.InnerHtml.AppendHtml(prevText); + + // create 'next' item + var nextIcon = new TagBuilder("span"); + nextIcon.AddCssClass("carousel-control-next-icon"); + nextIcon.Attributes.Add("aria-hidden", "true"); + + var nextText = new TagBuilder("span"); + nextText.AddCssClass("visually-hidden"); + nextText.InnerHtml.Append(L["Next"].Value); + + var nextAnchor = new TagBuilder("a"); + nextAnchor.AddCssClass("carousel-control-next"); + nextAnchor.Attributes.Add("href", "#" + TagHelper.Id); + nextAnchor.Attributes.Add("role", "button"); + nextAnchor.Attributes.Add("data-bs-slide", "next"); + + nextAnchor.InnerHtml.AppendHtml(nextIcon); + nextAnchor.InnerHtml.AppendHtml(nextText); + + // append post content + output.PostContent.AppendHtml(prevAnchor); + output.PostContent.AppendHtml(nextAnchor); + } - // create 'previous' item - var prevIcon = new TagBuilder("span"); - prevIcon.AddCssClass("carousel-control-prev-icon"); - prevIcon.Attributes.Add("aria-hidden", "true"); - - var prevText = new TagBuilder("span"); - prevText.AddCssClass("visually-hidden"); - prevText.InnerHtml.Append(L["Previous"].Value); - - var prevAnchor = new TagBuilder("a"); - prevAnchor.AddCssClass("carousel-control-prev"); - prevAnchor.Attributes.Add("href", "#" + TagHelper.Id); - prevAnchor.Attributes.Add("role", "button"); - prevAnchor.Attributes.Add("data-bs-slide", "prev"); - - prevAnchor.InnerHtml.AppendHtml(prevIcon); - prevAnchor.InnerHtml.AppendHtml(prevText); - - // create 'next' item - var nextIcon = new TagBuilder("span"); - nextIcon.AddCssClass("carousel-control-next-icon"); - nextIcon.Attributes.Add("aria-hidden", "true"); - - var nextText = new TagBuilder("span"); - nextText.AddCssClass("visually-hidden"); - nextText.InnerHtml.Append(L["Next"].Value); - - var nextAnchor = new TagBuilder("a"); - nextAnchor.AddCssClass("carousel-control-next"); - nextAnchor.Attributes.Add("href", "#" + TagHelper.Id); - nextAnchor.Attributes.Add("role", "button"); - nextAnchor.Attributes.Add("data-bs-slide", "next"); - - nextAnchor.InnerHtml.AppendHtml(nextIcon); - nextAnchor.InnerHtml.AppendHtml(nextText); - - // append post content - output.PostContent.AppendHtml(prevAnchor); - output.PostContent.AppendHtml(nextAnchor); + protected virtual void SetIndicators(TagHelperContext context, TagHelperOutput output, List itemList) + { + if (!TagHelper.Indicators ?? false) + { + return; } - protected virtual void SetIndicators(TagHelperContext context, TagHelperOutput output, List itemList) - { - if (!TagHelper.Indicators ?? false) - { - return; - } + var list = new TagBuilder("ol"); + list.AddCssClass("carousel-indicators"); - var list = new TagBuilder("ol"); - list.AddCssClass("carousel-indicators"); + for (var i = 0; i < itemList.Count; i++) + { + var listItem = new TagBuilder("li"); + listItem.Attributes.Add("data-bs-target", "#" + TagHelper.Id); + listItem.Attributes.Add("data-bs-slide-to", i.ToString()); - for (var i = 0; i < itemList.Count; i++) + if (itemList[i].Active) { - var listItem = new TagBuilder("li"); - listItem.Attributes.Add("data-bs-target", "#" + TagHelper.Id); - listItem.Attributes.Add("data-bs-slide-to", i.ToString()); - - if (itemList[i].Active) - { - listItem.AddCssClass("active"); - } - - list.InnerHtml.AppendHtml(listItem); + listItem.AddCssClass("active"); } - output.PreContent.SetHtmlContent(list); + list.InnerHtml.AppendHtml(listItem); } - protected virtual void SetOneItemAsActive(TagHelperContext context, TagHelperOutput output, List itemList) - { - if (!itemList.Any(it => it.Active) && itemList.Count > 0) - { - itemList.FirstOrDefault().Active = true; - } - } + output.PreContent.SetHtmlContent(list); + } - protected virtual void AddBasicAttributes(TagHelperContext context, TagHelperOutput output) + protected virtual void SetOneItemAsActive(TagHelperContext context, TagHelperOutput output, List itemList) + { + if (!itemList.Any(it => it.Active) && itemList.Count > 0) { - output.Attributes.Add("data-bs-ride", "carousel"); - output.Attributes.Add("id", TagHelper.Id); - AddBasicClasses(context, output); + itemList.FirstOrDefault().Active = true; } + } - protected virtual void AddBasicClasses(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.AddClass("carousel"); - output.Attributes.AddClass("slide"); - SetFadeAnimation(context, output); - } + protected virtual void AddBasicAttributes(TagHelperContext context, TagHelperOutput output) + { + output.Attributes.Add("data-bs-ride", "carousel"); + output.Attributes.Add("id", TagHelper.Id); + AddBasicClasses(context, output); + } - protected virtual void SetFadeAnimation(TagHelperContext context, TagHelperOutput output) - { - if (TagHelper.Crossfade ?? false) - { - output.Attributes.AddClass("carousel-fade"); - } - } + protected virtual void AddBasicClasses(TagHelperContext context, TagHelperOutput output) + { + output.Attributes.AddClass("carousel"); + output.Attributes.AddClass("slide"); + SetFadeAnimation(context, output); + } - protected virtual void SetRandomIdIfNotProvided() + protected virtual void SetFadeAnimation(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.Crossfade ?? false) { - if (string.IsNullOrWhiteSpace(TagHelper.Id)) - { - TagHelper.Id = "C" + Guid.NewGuid().ToString("N"); - } + output.Attributes.AddClass("carousel-fade"); } + } - protected virtual void SetActiveIfActive(CarouselItem item) + protected virtual void SetRandomIdIfNotProvided() + { + if (string.IsNullOrWhiteSpace(TagHelper.Id)) { - item.Html = item.Html.Replace(AbpCarouselItemActivePlaceholder, item.Active ? "active" : ""); + TagHelper.Id = "C" + Guid.NewGuid().ToString("N"); } + } + protected virtual void SetActiveIfActive(CarouselItem item) + { + item.Html = item.Html.Replace(AbpCarouselItemActivePlaceholder, item.Active ? "active" : ""); } + } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/CarouselItem.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/CarouselItem.cs index da901f39b0..2942a62541 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/CarouselItem.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/CarouselItem.cs @@ -1,15 +1,14 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel; + +public class CarouselItem { - public class CarouselItem + public CarouselItem(string html, bool active) { - public CarouselItem(string html, bool active) - { - Html = html; - Active = active; - } + Html = html; + Active = active; + } - public string Html { get; set; } + public string Html { get; set; } - public bool Active { get; set; } - } + public bool Active { get; set; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelper.cs index d1ec7e41b4..8742631e9a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelper.cs @@ -1,17 +1,16 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse; + +public class AbpAccordionItemTagHelper : AbpTagHelper { - public class AbpAccordionItemTagHelper : AbpTagHelper - { - public string Id { get; set; } + public string Id { get; set; } - public string Title { get; set; } + public string Title { get; set; } - public bool? Active { get; set; } + public bool? Active { get; set; } - public AbpAccordionItemTagHelper(AbpAccordionItemTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpAccordionItemTagHelper(AbpAccordionItemTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs index 3ea2824b62..7f3f7e9c17 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs @@ -5,81 +5,80 @@ using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse; + +public class AbpAccordionItemTagHelperService : AbpTagHelperService { - public class AbpAccordionItemTagHelperService : AbpTagHelperService + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) - { - SetRandomIdIfNotProvided(); + SetRandomIdIfNotProvided(); - var childContent = await output.GetChildContentAsync(); + var childContent = await output.GetChildContentAsync(); - var html = GetAccordionHeaderItem(context, output) + GetAccordionContentItem(context, output, childContent); + var html = GetAccordionHeaderItem(context, output) + GetAccordionContentItem(context, output, childContent); - var tabHeaderItems = context.GetValue>(AccordionItems); - tabHeaderItems.Add(html); + var tabHeaderItems = context.GetValue>(AccordionItems); + tabHeaderItems.Add(html); - output.SuppressOutput(); - } + output.SuppressOutput(); + } - protected virtual string GetAccordionHeaderItem(TagHelperContext context, TagHelperOutput output) - { - var button = new TagBuilder("button"); - button.AddCssClass("btn btn-link"); - button.Attributes.Add("type", "button"); - button.Attributes.Add("data-bs-toggle", "collapse"); - button.Attributes.Add("data-bs-target", "#" + GetContentId()); - button.Attributes.Add("aria-expanded", "true"); - button.Attributes.Add("aria-controls", GetContentId()); - button.InnerHtml.AppendHtml(TagHelper.Title); - - var h5 = new TagBuilder("h5"); - h5.AddCssClass("mb-0"); - h5.InnerHtml.AppendHtml(button); - - var header = new TagBuilder("div"); - header.AddCssClass("card-header"); - header.Attributes.Add("id", GetHeadingId()); - header.InnerHtml.AppendHtml(h5); - - return header.ToHtmlString(); - } + protected virtual string GetAccordionHeaderItem(TagHelperContext context, TagHelperOutput output) + { + var button = new TagBuilder("button"); + button.AddCssClass("btn btn-link"); + button.Attributes.Add("type", "button"); + button.Attributes.Add("data-bs-toggle", "collapse"); + button.Attributes.Add("data-bs-target", "#" + GetContentId()); + button.Attributes.Add("aria-expanded", "true"); + button.Attributes.Add("aria-controls", GetContentId()); + button.InnerHtml.AppendHtml(TagHelper.Title); + + var h5 = new TagBuilder("h5"); + h5.AddCssClass("mb-0"); + h5.InnerHtml.AppendHtml(button); + + var header = new TagBuilder("div"); + header.AddCssClass("card-header"); + header.Attributes.Add("id", GetHeadingId()); + header.InnerHtml.AppendHtml(h5); + + return header.ToHtmlString(); + } - protected virtual string GetAccordionContentItem(TagHelperContext context, TagHelperOutput output, TagHelperContent content) - { - var show = (TagHelper.Active ?? false) ? " show" : ""; + protected virtual string GetAccordionContentItem(TagHelperContext context, TagHelperOutput output, TagHelperContent content) + { + var show = (TagHelper.Active ?? false) ? " show" : ""; - var cardBody = new TagBuilder("div"); - cardBody.AddCssClass("card-body"); - cardBody.InnerHtml.AppendHtml(content); + var cardBody = new TagBuilder("div"); + cardBody.AddCssClass("card-body"); + cardBody.InnerHtml.AppendHtml(content); - var wrapper = new TagBuilder("div"); - wrapper.AddCssClass("collapse" + show); - wrapper.Attributes.Add("id", GetContentId()); - wrapper.Attributes.Add("aria-labelledby", GetHeadingId()); - wrapper.Attributes.Add("data-bs-parent", "#" + AbpAccordionParentIdPlaceholder); - wrapper.InnerHtml.AppendHtml(cardBody); + var wrapper = new TagBuilder("div"); + wrapper.AddCssClass("collapse" + show); + wrapper.Attributes.Add("id", GetContentId()); + wrapper.Attributes.Add("aria-labelledby", GetHeadingId()); + wrapper.Attributes.Add("data-bs-parent", "#" + AbpAccordionParentIdPlaceholder); + wrapper.InnerHtml.AppendHtml(cardBody); - return wrapper.ToHtmlString(); - } + return wrapper.ToHtmlString(); + } - protected virtual string GetHeadingId() - { - return "heading" + TagHelper.Id; ; - } + protected virtual string GetHeadingId() + { + return "heading" + TagHelper.Id; ; + } - protected virtual string GetContentId() - { - return "content" + TagHelper.Id; ; - } + protected virtual string GetContentId() + { + return "content" + TagHelper.Id; ; + } - protected virtual void SetRandomIdIfNotProvided() + protected virtual void SetRandomIdIfNotProvided() + { + if (string.IsNullOrWhiteSpace(TagHelper.Id)) { - if (string.IsNullOrWhiteSpace(TagHelper.Id)) - { - TagHelper.Id = "A" + Guid.NewGuid().ToString("N"); - } + TagHelper.Id = "A" + Guid.NewGuid().ToString("N"); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelper.cs index c297a7c630..49cca3199f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelper.cs @@ -1,13 +1,12 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse; + +public class AbpAccordionTagHelper : AbpTagHelper { - public class AbpAccordionTagHelper : AbpTagHelper - { - public string Id { get; set; } + public string Id { get; set; } - public AbpAccordionTagHelper(AbpAccordionTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpAccordionTagHelper(AbpAccordionTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelperService.cs index d55bb04d21..c33b25022b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelperService.cs @@ -6,60 +6,59 @@ using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse; + +public class AbpAccordionTagHelperService : AbpTagHelperService { - public class AbpAccordionTagHelperService : AbpTagHelperService - { - protected IHtmlGenerator HtmlGenerator { get; } + protected IHtmlGenerator HtmlGenerator { get; } - public AbpAccordionTagHelperService(IHtmlGenerator htmlGenerator) - { - HtmlGenerator = htmlGenerator; - } + public AbpAccordionTagHelperService(IHtmlGenerator htmlGenerator) + { + HtmlGenerator = htmlGenerator; + } - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) - { - SetRandomIdIfNotProvided(); + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + SetRandomIdIfNotProvided(); - output.TagName = "div"; - output.TagMode = TagMode.StartTagAndEndTag; - output.Attributes.AddClass("accordion"); - output.Attributes.Add("id",TagHelper.Id); + output.TagName = "div"; + output.TagMode = TagMode.StartTagAndEndTag; + output.Attributes.AddClass("accordion"); + output.Attributes.Add("id", TagHelper.Id); - var items = InitilizeFormGroupContentsContext(context, output); + var items = InitilizeFormGroupContentsContext(context, output); - await output.GetChildContentAsync(); + await output.GetChildContentAsync(); - SetContent(context, output, items); - } + SetContent(context, output, items); + } - protected virtual void SetContent(TagHelperContext context, TagHelperOutput output, List items) + protected virtual void SetContent(TagHelperContext context, TagHelperOutput output, List items) + { + foreach (var item in items) { - foreach (var item in items) - { - var content = item.Replace(AbpAccordionParentIdPlaceholder, HtmlGenerator.Encode(TagHelper.Id)); + var content = item.Replace(AbpAccordionParentIdPlaceholder, HtmlGenerator.Encode(TagHelper.Id)); - var wrapper = new TagBuilder("div"); - wrapper.AddCssClass("card"); - wrapper.InnerHtml.AppendHtml(content); + var wrapper = new TagBuilder("div"); + wrapper.AddCssClass("card"); + wrapper.InnerHtml.AppendHtml(content); - output.Content.AppendHtml(wrapper); - } + output.Content.AppendHtml(wrapper); } + } - protected virtual List InitilizeFormGroupContentsContext(TagHelperContext context, TagHelperOutput output) - { - var items = new List(); - context.Items[AccordionItems] = items; - return items; - } + protected virtual List InitilizeFormGroupContentsContext(TagHelperContext context, TagHelperOutput output) + { + var items = new List(); + context.Items[AccordionItems] = items; + return items; + } - protected virtual void SetRandomIdIfNotProvided() + protected virtual void SetRandomIdIfNotProvided() + { + if (string.IsNullOrWhiteSpace(TagHelper.Id)) { - if (string.IsNullOrWhiteSpace(TagHelper.Id)) - { - TagHelper.Id = "A" + Guid.NewGuid().ToString("N"); - } + TagHelper.Id = "A" + Guid.NewGuid().ToString("N"); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseBodyTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseBodyTagHelper.cs index a50907addb..cd6d5d43ed 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseBodyTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseBodyTagHelper.cs @@ -1,17 +1,16 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse; + +public class AbpCollapseBodyTagHelper : AbpTagHelper { - public class AbpCollapseBodyTagHelper : AbpTagHelper - { - public string Id { get; set; } + public string Id { get; set; } - public bool? Multi { get; set; } + public bool? Multi { get; set; } - public bool? Show { get; set; } + public bool? Show { get; set; } - public AbpCollapseBodyTagHelper(AbpCollapseBodyTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpCollapseBodyTagHelper(AbpCollapseBodyTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseBodyTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseBodyTagHelperService.cs index 843efae0f4..85685ccf80 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseBodyTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseBodyTagHelperService.cs @@ -2,29 +2,28 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse; + +public class AbpCollapseBodyTagHelperService : AbpTagHelperService { - public class AbpCollapseBodyTagHelperService : AbpTagHelperService + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "div"; - output.Attributes.AddClass("collapse"); - output.Attributes.Add("id", TagHelper.Id); + output.TagName = "div"; + output.Attributes.AddClass("collapse"); + output.Attributes.Add("id", TagHelper.Id); - if (TagHelper.Show ?? false) - { - output.Attributes.AddClass("show"); - } + if (TagHelper.Show ?? false) + { + output.Attributes.AddClass("show"); + } - if (TagHelper.Multi ?? false) - { - output.Attributes.AddClass("multi-collapse"); - } + if (TagHelper.Multi ?? false) + { + output.Attributes.AddClass("multi-collapse"); + } - var childContent = await output.GetChildContentAsync(); + var childContent = await output.GetChildContentAsync(); - output.Content.SetHtmlContent(childContent); - } + output.Content.SetHtmlContent(childContent); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseButtonTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseButtonTagHelper.cs index 6b8fc8c30b..c1219b3bbb 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseButtonTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseButtonTagHelper.cs @@ -1,20 +1,18 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse; + +[HtmlTargetElement("abp-button", Attributes = "abp-collapse-id")] +[HtmlTargetElement("a", Attributes = "abp-collapse-id")] +public class AbpCollapseButtonTagHelper : AbpTagHelper { + [HtmlAttributeName("abp-collapse-id")] + public string BodyId { get; set; } - [HtmlTargetElement("abp-button", Attributes = "abp-collapse-id")] - [HtmlTargetElement("a", Attributes = "abp-collapse-id")] - public class AbpCollapseButtonTagHelper : AbpTagHelper + public AbpCollapseButtonTagHelper(AbpCollapseButtonTagHelperService tagHelperService) + : base(tagHelperService) { - [HtmlAttributeName("abp-collapse-id")] - public string BodyId { get; set; } - - public AbpCollapseButtonTagHelper(AbpCollapseButtonTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseButtonTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseButtonTagHelperService.cs index fcc4ff413e..49b356379a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseButtonTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpCollapseButtonTagHelperService.cs @@ -2,54 +2,53 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse; + +public class AbpCollapseButtonTagHelperService : AbpTagHelperService { - public class AbpCollapseButtonTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - AddCommonAttributes(context, output); + AddCommonAttributes(context, output); - if (output.TagName == "abp-button" || output.TagName == "button") - { - AddButtonAttributes(context,output); - } - else if (output.TagName == "a") - { - AddLinkAttributes(context, output); - } + if (output.TagName == "abp-button" || output.TagName == "button") + { + AddButtonAttributes(context, output); } - - protected virtual void AddCommonAttributes(TagHelperContext context, TagHelperOutput output) + else if (output.TagName == "a") { - output.Attributes.Add("data-bs-toggle", "collapse"); - output.Attributes.Add("aria-expanded", "false"); - output.Attributes.Add("aria-controls", TagHelper.BodyId); + AddLinkAttributes(context, output); } + } - protected virtual void AddButtonAttributes(TagHelperContext context, TagHelperOutput output) - { - if (TagHelper.BodyId.Trim().Split(' ').Length > 1) - { - output.Attributes.Add("data-bs-target", ".multi-collapse"); - return; - } + protected virtual void AddCommonAttributes(TagHelperContext context, TagHelperOutput output) + { + output.Attributes.Add("data-bs-toggle", "collapse"); + output.Attributes.Add("aria-expanded", "false"); + output.Attributes.Add("aria-controls", TagHelper.BodyId); + } - output.Attributes.Add("data-bs-target", "#" + TagHelper.BodyId); + protected virtual void AddButtonAttributes(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.BodyId.Trim().Split(' ').Length > 1) + { + output.Attributes.Add("data-bs-target", ".multi-collapse"); + return; } - protected virtual void AddLinkAttributes(TagHelperContext context, TagHelperOutput output) - { - if (TagHelper.BodyId.Trim().Split(' ').Length > 1) - { - output.Attributes.Add("href", ".multi-collapse"); - return; - } + output.Attributes.Add("data-bs-target", "#" + TagHelper.BodyId); + } - output.Attributes.Add("href", "#" + TagHelper.BodyId); + protected virtual void AddLinkAttributes(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.BodyId.Trim().Split(' ').Length > 1) + { + output.Attributes.Add("href", ".multi-collapse"); + return; } + output.Attributes.Add("href", "#" + TagHelper.BodyId); } + } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelper.cs index de20c8743c..ac87900307 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelper.cs @@ -1,29 +1,28 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownButtonTagHelper : AbpTagHelper { - public class AbpDropdownButtonTagHelper : AbpTagHelper - { - public string Text { get; set; } + public string Text { get; set; } - public AbpButtonSize Size { get; set; } = AbpButtonSize.Default; + public AbpButtonSize Size { get; set; } = AbpButtonSize.Default; - public DropdownStyle DropdownStyle { get; set; } = DropdownStyle.Single; + public DropdownStyle DropdownStyle { get; set; } = DropdownStyle.Single; - public AbpButtonType ButtonType { get; set; } = AbpButtonType.Default; + public AbpButtonType ButtonType { get; set; } = AbpButtonType.Default; - public string Icon { get; set; } + public string Icon { get; set; } - public FontIconType IconType { get; set; } = FontIconType.FontAwesome; + public FontIconType IconType { get; set; } = FontIconType.FontAwesome; - public bool? Link { get; set; } + public bool? Link { get; set; } - public bool? NavLink { get; set; } + public bool? NavLink { get; set; } - public AbpDropdownButtonTagHelper(AbpDropdownButtonTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpDropdownButtonTagHelper(AbpDropdownButtonTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelperService.cs index faa8fe7d75..8a5f5df409 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelperService.cs @@ -8,138 +8,137 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown -{ - public class AbpDropdownButtonTagHelperService : AbpTagHelperService - { +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; - private readonly HtmlEncoder _htmlEncoder; - private readonly IServiceProvider _serviceProvider; +public class AbpDropdownButtonTagHelperService : AbpTagHelperService +{ - public AbpDropdownButtonTagHelperService( - HtmlEncoder htmlEncoder, - IServiceProvider serviceProvider) - { - _htmlEncoder = htmlEncoder; - _serviceProvider = serviceProvider; - } + private readonly HtmlEncoder _htmlEncoder; + private readonly IServiceProvider _serviceProvider; - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) - { - var content = await output.GetChildContentAsync(); + public AbpDropdownButtonTagHelperService( + HtmlEncoder htmlEncoder, + IServiceProvider serviceProvider) + { + _htmlEncoder = htmlEncoder; + _serviceProvider = serviceProvider; + } - var buttonsAsHtml = await GetButtonsAsHtmlAsync(context, output, content); + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + var content = await output.GetChildContentAsync(); - output.PreElement.SetHtmlContent(buttonsAsHtml); + var buttonsAsHtml = await GetButtonsAsHtmlAsync(context, output, content); - output.TagName = "div"; - output.TagMode = TagMode.StartTagAndEndTag; - output.Content.SetContent(""); - output.Attributes.Clear(); - } + output.PreElement.SetHtmlContent(buttonsAsHtml); - protected virtual async Task GetButtonsAsHtmlAsync(TagHelperContext context, TagHelperOutput output, - TagHelperContent content) - { - var buttonBuilder = new StringBuilder(""); + output.TagName = "div"; + output.TagMode = TagMode.StartTagAndEndTag; + output.Content.SetContent(""); + output.Attributes.Clear(); + } - var mainButton = await GetMainButtonAsync(context, output, content); + protected virtual async Task GetButtonsAsHtmlAsync(TagHelperContext context, TagHelperOutput output, + TagHelperContent content) + { + var buttonBuilder = new StringBuilder(""); - buttonBuilder.AppendLine(mainButton); + var mainButton = await GetMainButtonAsync(context, output, content); - if (TagHelper.DropdownStyle == DropdownStyle.Split) - { - var splitButton = await GetSplitButtonAsync(context, output); + buttonBuilder.AppendLine(mainButton); - buttonBuilder.AppendLine(splitButton); - } + if (TagHelper.DropdownStyle == DropdownStyle.Split) + { + var splitButton = await GetSplitButtonAsync(context, output); - return buttonBuilder.ToString(); + buttonBuilder.AppendLine(splitButton); } - protected virtual async Task GetMainButtonAsync(TagHelperContext context, TagHelperOutput output, TagHelperContent content) - { - var abpButtonTagHelper = _serviceProvider.GetRequiredService(); - - abpButtonTagHelper.Icon = TagHelper.Icon; - abpButtonTagHelper.Text = TagHelper.Text; - abpButtonTagHelper.IconType = TagHelper.IconType; - abpButtonTagHelper.Size = TagHelper.Size; - abpButtonTagHelper.ButtonType = TagHelper.ButtonType; - var attributes = GetAttributesForMainButton(context, output); + return buttonBuilder.ToString(); + } - var buttonTag = await abpButtonTagHelper.ProcessAndGetOutputAsync(attributes, context, "button", TagMode.StartTagAndEndTag); + protected virtual async Task GetMainButtonAsync(TagHelperContext context, TagHelperOutput output, TagHelperContent content) + { + var abpButtonTagHelper = _serviceProvider.GetRequiredService(); - buttonTag.PreContent.SetHtmlContent(content); + abpButtonTagHelper.Icon = TagHelper.Icon; + abpButtonTagHelper.Text = TagHelper.Text; + abpButtonTagHelper.IconType = TagHelper.IconType; + abpButtonTagHelper.Size = TagHelper.Size; + abpButtonTagHelper.ButtonType = TagHelper.ButtonType; + var attributes = GetAttributesForMainButton(context, output); - if ((TagHelper.NavLink ?? false) || (TagHelper.Link ?? false)) - { - var linkTag = ConvertButtonToLink(buttonTag); - return linkTag.Render(_htmlEncoder); - } + var buttonTag = await abpButtonTagHelper.ProcessAndGetOutputAsync(attributes, context, "button", TagMode.StartTagAndEndTag); - return buttonTag.Render(_htmlEncoder); - } + buttonTag.PreContent.SetHtmlContent(content); - protected virtual async Task GetSplitButtonAsync(TagHelperContext context, TagHelperOutput output) + if ((TagHelper.NavLink ?? false) || (TagHelper.Link ?? false)) { - var abpButtonTagHelper = _serviceProvider.GetRequiredService(); + var linkTag = ConvertButtonToLink(buttonTag); + return linkTag.Render(_htmlEncoder); + } - abpButtonTagHelper.Size = TagHelper.Size; - abpButtonTagHelper.ButtonType = TagHelper.ButtonType; - var attributes = GetAttributesForSplitButton(context, output); + return buttonTag.Render(_htmlEncoder); + } - return await abpButtonTagHelper.RenderAsync(attributes, context, _htmlEncoder, "button", TagMode.StartTagAndEndTag); - } + protected virtual async Task GetSplitButtonAsync(TagHelperContext context, TagHelperOutput output) + { + var abpButtonTagHelper = _serviceProvider.GetRequiredService(); - protected virtual TagHelperAttributeList GetAttributesForMainButton(TagHelperContext context, TagHelperOutput output) - { + abpButtonTagHelper.Size = TagHelper.Size; + abpButtonTagHelper.ButtonType = TagHelper.ButtonType; + var attributes = GetAttributesForSplitButton(context, output); - var attributes = new TagHelperAttributeList(); + return await abpButtonTagHelper.RenderAsync(attributes, context, _htmlEncoder, "button", TagMode.StartTagAndEndTag); + } - foreach (var tagHelperAttribute in output.Attributes) - { - attributes.Add(tagHelperAttribute); - } + protected virtual TagHelperAttributeList GetAttributesForMainButton(TagHelperContext context, TagHelperOutput output) + { - if (TagHelper.DropdownStyle != DropdownStyle.Split) - { - attributes.AddClass("dropdown-toggle"); - attributes.Add("data-bs-toggle", "dropdown"); - attributes.Add("aria-haspopup", "true"); - attributes.Add("aria-expanded", "false"); - } + var attributes = new TagHelperAttributeList(); - return attributes; + foreach (var tagHelperAttribute in output.Attributes) + { + attributes.Add(tagHelperAttribute); } - protected virtual TagHelperAttributeList GetAttributesForSplitButton(TagHelperContext context, TagHelperOutput output) + if (TagHelper.DropdownStyle != DropdownStyle.Split) { - var attributes = new TagHelperAttributeList + attributes.AddClass("dropdown-toggle"); + attributes.Add("data-bs-toggle", "dropdown"); + attributes.Add("aria-haspopup", "true"); + attributes.Add("aria-expanded", "false"); + } + + return attributes; + } + + protected virtual TagHelperAttributeList GetAttributesForSplitButton(TagHelperContext context, TagHelperOutput output) + { + var attributes = new TagHelperAttributeList { {"data-bs-toggle", "dropdown"}, {"aria-haspopup", "true"}, {"aria-expanded", "false"}, }; - attributes.AddClass("dropdown-toggle"); - attributes.AddClass("dropdown-toggle-split"); + attributes.AddClass("dropdown-toggle"); + attributes.AddClass("dropdown-toggle-split"); - return attributes; - } + return attributes; + } - protected virtual TagHelperOutput ConvertButtonToLink(TagHelperOutput buttonTag) - { - buttonTag.TagName = "a"; - buttonTag.Attributes.RemoveAll("type"); - buttonTag.Attributes.Add("roles", "button"); - buttonTag.Attributes.Add("href", "#"); + protected virtual TagHelperOutput ConvertButtonToLink(TagHelperOutput buttonTag) + { + buttonTag.TagName = "a"; + buttonTag.Attributes.RemoveAll("type"); + buttonTag.Attributes.Add("roles", "button"); + buttonTag.Attributes.Add("href", "#"); - if (TagHelper.NavLink??false) - { - buttonTag.Attributes.AddClass("nav-link"); - } - return buttonTag; + if (TagHelper.NavLink ?? false) + { + buttonTag.Attributes.AddClass("nav-link"); } + return buttonTag; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownDividerTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownDividerTagHelper.cs index 691d7c23a5..f87abb78f7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownDividerTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownDividerTagHelper.cs @@ -1,11 +1,10 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownDividerTagHelper : AbpTagHelper { - public class AbpDropdownDividerTagHelper : AbpTagHelper + public AbpDropdownDividerTagHelper(AbpDropdownDividerTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpDropdownDividerTagHelper(AbpDropdownDividerTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownDividerTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownDividerTagHelperService.cs index 4e420f538b..7ff04e4f1d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownDividerTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownDividerTagHelperService.cs @@ -1,15 +1,14 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownDividerTagHelperService : AbpTagHelperService { - public class AbpDropdownDividerTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "div"; - output.Attributes.AddClass("dropdown-divider"); - output.TagMode = TagMode.StartTagAndEndTag; - } + output.TagName = "div"; + output.Attributes.AddClass("dropdown-divider"); + output.TagMode = TagMode.StartTagAndEndTag; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownHeaderTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownHeaderTagHelper.cs index cc2a910a07..da0ee56f40 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownHeaderTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownHeaderTagHelper.cs @@ -1,11 +1,10 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownHeaderTagHelper : AbpTagHelper { - public class AbpDropdownHeaderTagHelper : AbpTagHelper + public AbpDropdownHeaderTagHelper(AbpDropdownHeaderTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpDropdownHeaderTagHelper(AbpDropdownHeaderTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownHeaderTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownHeaderTagHelperService.cs index 73157f45cc..aed611f6e0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownHeaderTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownHeaderTagHelperService.cs @@ -1,15 +1,14 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownHeaderTagHelperService : AbpTagHelperService { - public class AbpDropdownHeaderTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "h6"; - output.Attributes.AddClass("dropdown-header"); - output.TagMode = TagMode.StartTagAndEndTag; - } + output.TagName = "h6"; + output.Attributes.AddClass("dropdown-header"); + output.TagMode = TagMode.StartTagAndEndTag; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTagHelper.cs index c017986271..a2a8403863 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTagHelper.cs @@ -1,15 +1,14 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownItemTagHelper : AbpTagHelper { - public class AbpDropdownItemTagHelper : AbpTagHelper - { - public bool? Active { get; set; } + public bool? Active { get; set; } - public bool? Disabled { get; set; } + public bool? Disabled { get; set; } - public AbpDropdownItemTagHelper(AbpDropdownItemTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpDropdownItemTagHelper(AbpDropdownItemTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTagHelperService.cs index 5f74b7a303..29815c5a4e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTagHelperService.cs @@ -1,34 +1,33 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownItemTagHelperService : AbpTagHelperService { - public class AbpDropdownItemTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "a"; - output.Attributes.AddClass("dropdown-item"); - output.TagMode = TagMode.StartTagAndEndTag; + output.TagName = "a"; + output.Attributes.AddClass("dropdown-item"); + output.TagMode = TagMode.StartTagAndEndTag; - SetActiveClassIfActive(context,output); - SetDisabledClassIfDisabled(context,output); - } + SetActiveClassIfActive(context, output); + SetDisabledClassIfDisabled(context, output); + } - protected virtual void SetActiveClassIfActive(TagHelperContext context, TagHelperOutput output) + protected virtual void SetActiveClassIfActive(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.Active ?? false) { - if (TagHelper.Active??false) - { - output.Attributes.AddClass("active"); - } + output.Attributes.AddClass("active"); } + } - protected virtual void SetDisabledClassIfDisabled(TagHelperContext context, TagHelperOutput output) + protected virtual void SetDisabledClassIfDisabled(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.Disabled ?? false) { - if (TagHelper.Disabled??false) - { - output.Attributes.AddClass("disabled"); - } + output.Attributes.AddClass("disabled"); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTextTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTextTagHelper.cs index 4c5e82fac7..b231aa63ef 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTextTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTextTagHelper.cs @@ -1,11 +1,10 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownItemTextTagHelper : AbpTagHelper { - public class AbpDropdownItemTextTagHelper : AbpTagHelper + public AbpDropdownItemTextTagHelper(AbpDropdownItemTextTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpDropdownItemTextTagHelper(AbpDropdownItemTextTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTextTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTextTagHelperService.cs index 8cd84eade6..51b8af6250 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTextTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownItemTextTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownItemTextTagHelperService : AbpTagHelperService { - public class AbpDropdownItemTextTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.AddClass("dropdown-item-text"); - output.TagName = "span"; - } + output.Attributes.AddClass("dropdown-item-text"); + output.TagName = "span"; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownMenuTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownMenuTagHelper.cs index 04523033bc..1744d1922f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownMenuTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownMenuTagHelper.cs @@ -1,13 +1,12 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownMenuTagHelper : AbpTagHelper { - public class AbpDropdownMenuTagHelper : AbpTagHelper - { - public DropdownAlign Align { get; set; } = DropdownAlign.Start; + public DropdownAlign Align { get; set; } = DropdownAlign.Start; - public AbpDropdownMenuTagHelper(AbpDropdownMenuTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpDropdownMenuTagHelper(AbpDropdownMenuTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownMenuTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownMenuTagHelperService.cs index 80c4d31c87..1a9a1b6c55 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownMenuTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownMenuTagHelperService.cs @@ -1,29 +1,28 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownMenuTagHelperService : AbpTagHelperService { - public class AbpDropdownMenuTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "div"; - output.Attributes.AddClass("dropdown-menu"); - output.TagMode = TagMode.StartTagAndEndTag; + output.TagName = "div"; + output.Attributes.AddClass("dropdown-menu"); + output.TagMode = TagMode.StartTagAndEndTag; - SetAlign(context, output); - } + SetAlign(context, output); + } - protected virtual void SetAlign(TagHelperContext context, TagHelperOutput output) + protected virtual void SetAlign(TagHelperContext context, TagHelperOutput output) + { + switch (TagHelper.Align) { - switch (TagHelper.Align) - { - case DropdownAlign.End: - output.Attributes.AddClass("dropdown-menu-end"); - return; - case DropdownAlign.Start: - return; - } + case DropdownAlign.End: + output.Attributes.AddClass("dropdown-menu-end"); + return; + case DropdownAlign.Start: + return; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelper.cs index 5daad8a9b7..2cd06cff74 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelper.cs @@ -1,13 +1,12 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownTagHelper : AbpTagHelper { - public class AbpDropdownTagHelper : AbpTagHelper - { - public DropdownDirection Direction { get; set; } = DropdownDirection.Down; + public DropdownDirection Direction { get; set; } = DropdownDirection.Down; - public AbpDropdownTagHelper(AbpDropdownTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpDropdownTagHelper(AbpDropdownTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelperService.cs index 8f37fca5ee..581239a4b6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelperService.cs @@ -1,37 +1,36 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public class AbpDropdownTagHelperService : AbpTagHelperService { - public class AbpDropdownTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "div"; - output.Attributes.AddClass("dropdown"); - output.Attributes.AddClass("btn-group"); + output.TagName = "div"; + output.Attributes.AddClass("dropdown"); + output.Attributes.AddClass("btn-group"); - SetDirection(context, output); + SetDirection(context, output); - output.TagMode = TagMode.StartTagAndEndTag; - } + output.TagMode = TagMode.StartTagAndEndTag; + } - protected virtual void SetDirection(TagHelperContext context, TagHelperOutput output) + protected virtual void SetDirection(TagHelperContext context, TagHelperOutput output) + { + switch (TagHelper.Direction) { - switch (TagHelper.Direction) - { - case DropdownDirection.Down: - return; - case DropdownDirection.Up: - output.Attributes.AddClass("dropup"); - return; - case DropdownDirection.Right: - output.Attributes.AddClass("dropright"); - return; - case DropdownDirection.Left: - output.Attributes.AddClass("dropleft"); - return; - } + case DropdownDirection.Down: + return; + case DropdownDirection.Up: + output.Attributes.AddClass("dropup"); + return; + case DropdownDirection.Right: + output.Attributes.AddClass("dropright"); + return; + case DropdownDirection.Left: + output.Attributes.AddClass("dropleft"); + return; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownAlign.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownAlign.cs index a15cf821ea..331a0c3e5f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownAlign.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownAlign.cs @@ -1,8 +1,7 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public enum DropdownAlign { - public enum DropdownAlign - { - Start, - End - } -} \ No newline at end of file + Start, + End +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownDirection.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownDirection.cs index f7c90ae58c..e67025e614 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownDirection.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownDirection.cs @@ -1,10 +1,9 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public enum DropdownDirection { - public enum DropdownDirection - { - Down, - Up, - Right, - Left - } -} \ No newline at end of file + Down, + Up, + Right, + Left +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownStyle.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownStyle.cs index 67d02ace22..6ea9256748 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownStyle.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/DropdownStyle.cs @@ -1,8 +1,7 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown; + +public enum DropdownStyle { - public enum DropdownStyle - { - Single, - Split - } -} \ No newline at end of file + Single, + Split +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/ModelExplorerExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/ModelExplorerExtensions.cs index b6bedac11b..eced70b75e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/ModelExplorerExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/ModelExplorerExtensions.cs @@ -3,18 +3,17 @@ using System.Reflection; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; + +public static class ModelExplorerExtensions { - public static class ModelExplorerExtensions + public static T GetAttribute(this ModelExplorer property) where T : Attribute { - public static T GetAttribute(this ModelExplorer property) where T : Attribute - { - return property?.Metadata?.ContainerType?.GetTypeInfo()?.GetProperty(property.Metadata.PropertyName)?.GetCustomAttribute(); - } + return property?.Metadata?.ContainerType?.GetTypeInfo()?.GetProperty(property.Metadata.PropertyName)?.GetCustomAttribute(); + } - public static int GetDisplayOrder(this ModelExplorer explorer) - { - return GetAttribute(explorer)?.Number ?? DisplayOrder.Default; - } + public static int GetDisplayOrder(this ModelExplorer explorer) + { + return GetAttribute(explorer)?.Number ?? DisplayOrder.Default; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperAttributeExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperAttributeExtensions.cs index ef581529a6..09d0513fbd 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperAttributeExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperAttributeExtensions.cs @@ -1,25 +1,24 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; + +public static class TagHelperAttributeExtensions { - public static class TagHelperAttributeExtensions + public static string ToHtmlAttributeAsString(this TagHelperAttribute attribute) { - public static string ToHtmlAttributeAsString(this TagHelperAttribute attribute) - { - return attribute.Name + "=\"" + attribute.Value + "\""; - } - - public static string ToHtmlAttributesAsString(this List attributes) - { - var attributesAsString = ""; + return attribute.Name + "=\"" + attribute.Value + "\""; + } - foreach (var attribute in attributes) - { - attributesAsString += attribute.ToHtmlAttributeAsString() + " "; - } + public static string ToHtmlAttributesAsString(this List attributes) + { + var attributesAsString = ""; - return attributesAsString; + foreach (var attribute in attributes) + { + attributesAsString += attribute.ToHtmlAttributeAsString() + " "; } + + return attributesAsString; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperContextExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperContextExtensions.cs index c2b3e41192..47ab8827e7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperContextExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperContextExtensions.cs @@ -3,18 +3,17 @@ using System.Linq; using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; + +public static class TagHelperContextExtensions { - public static class TagHelperContextExtensions + public static T GetValue(this TagHelperContext context, string key) { - public static T GetValue(this TagHelperContext context, string key) + if (!context.Items.ContainsKey(key)) { - if (!context.Items.ContainsKey(key)) - { - return default(T); - } - - return (T)context.Items[key]; + return default(T); } + + return (T)context.Items[key]; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs index 761b72ec99..227c824002 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs @@ -3,43 +3,42 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.TagHelpers; using System.Text.Encodings.Web; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; + +public static class TagHelperExtensions { - public static class TagHelperExtensions + public static async Task ProcessAndGetOutputAsync( + this TagHelper tagHelper, + TagHelperAttributeList attributeList, + TagHelperContext context, + string tagName = "div", + TagMode tagMode = TagMode.SelfClosing) { - public static async Task ProcessAndGetOutputAsync( - this TagHelper tagHelper, - TagHelperAttributeList attributeList, - TagHelperContext context, - string tagName = "div", - TagMode tagMode = TagMode.SelfClosing) - { - var innerOutput = new TagHelperOutput( - tagName, - attributeList, - (useCachedResult, encoder) => Task.Run(() => new DefaultTagHelperContent())) - { - TagMode = tagMode - }; - - var innerContext = new TagHelperContext( - attributeList, - context.Items, - Guid.NewGuid().ToString() - ); - - tagHelper.Init(context); - - await tagHelper.ProcessAsync(innerContext, innerOutput); - - return innerOutput; - } - - public static async Task RenderAsync(this TagHelper tagHelper, TagHelperAttributeList attributeList, TagHelperContext context, HtmlEncoder htmlEncoder, string tagName = "div", TagMode tagMode = TagMode.SelfClosing) + var innerOutput = new TagHelperOutput( + tagName, + attributeList, + (useCachedResult, encoder) => Task.Run(() => new DefaultTagHelperContent())) { - var innerOutput = await tagHelper.ProcessAndGetOutputAsync(attributeList, context, tagName, tagMode); + TagMode = tagMode + }; + + var innerContext = new TagHelperContext( + attributeList, + context.Items, + Guid.NewGuid().ToString() + ); + + tagHelper.Init(context); + + await tagHelper.ProcessAsync(innerContext, innerOutput); + + return innerOutput; + } + + public static async Task RenderAsync(this TagHelper tagHelper, TagHelperAttributeList attributeList, TagHelperContext context, HtmlEncoder htmlEncoder, string tagName = "div", TagMode tagMode = TagMode.SelfClosing) + { + var innerOutput = await tagHelper.ProcessAndGetOutputAsync(attributeList, context, tagName, tagMode); - return innerOutput.Render(htmlEncoder); - } + return innerOutput.Render(htmlEncoder); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperOutputExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperOutputExtensions.cs index 0cc9bae6e2..40ca23a8f1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperOutputExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperOutputExtensions.cs @@ -2,17 +2,16 @@ using System.IO; using System.Text.Encodings.Web; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; + +public static class TagHelperOutputExtensions { - public static class TagHelperOutputExtensions + public static string Render(this TagHelperOutput output, HtmlEncoder htmlEncoder) { - public static string Render(this TagHelperOutput output, HtmlEncoder htmlEncoder) + using (var writer = new StringWriter()) { - using (var writer = new StringWriter()) - { - output.WriteTo(writer, htmlEncoder); - return writer.ToString(); - } + output.WriteTo(writer, htmlEncoder); + return writer.ToString(); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureCaptionTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureCaptionTagHelper.cs index 9bac7ca93a..ea35a52fa4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureCaptionTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureCaptionTagHelper.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure; + +[HtmlTargetElement("abp-figcaption")] +public class AbpFigureCaptionTagHelper : AbpTagHelper { - [HtmlTargetElement("abp-figcaption")] - public class AbpFigureCaptionTagHelper : AbpTagHelper + public AbpFigureCaptionTagHelper(AbpFigureCaptionTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpFigureCaptionTagHelper(AbpFigureCaptionTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureCaptionTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureCaptionTagHelperService.cs index f14a4a18fd..e2df91691b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureCaptionTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureCaptionTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure; + +public class AbpFigureCaptionTagHelperService : AbpTagHelperService { - public class AbpFigureCaptionTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "figcaption"; - output.Attributes.AddClass("figure-caption"); - } + output.TagName = "figcaption"; + output.Attributes.AddClass("figure-caption"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureImageTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureImageTagHelper.cs index 4b395508ad..7229a40656 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureImageTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureImageTagHelper.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure; + +[HtmlTargetElement("abp-image", ParentTag = "abp-figure")] +public class AbpFigureImageTagHelper : AbpTagHelper { - [HtmlTargetElement("abp-image", ParentTag = "abp-figure")] - public class AbpFigureImageTagHelper : AbpTagHelper + public AbpFigureImageTagHelper(AbpFigureImageTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpFigureImageTagHelper(AbpFigureImageTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureImageTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureImageTagHelperService.cs index 51237fe297..f79eb9b9e1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureImageTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureImageTagHelperService.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure; + +public class AbpFigureImageTagHelperService : AbpTagHelperService { - public class AbpFigureImageTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.AddClass("figure-img"); - } + output.Attributes.AddClass("figure-img"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureTagHelper.cs index 88b99b6138..1b9b8116e7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureTagHelper.cs @@ -1,11 +1,10 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure; + +public class AbpFigureTagHelper : AbpTagHelper { - public class AbpFigureTagHelper : AbpTagHelper + public AbpFigureTagHelper(AbpFigureTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpFigureTagHelper(AbpFigureTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureTagHelperService.cs index 7fe094f94e..6c75c299b5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Figure/AbpFigureTagHelperService.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Figure; + +public class AbpFigureTagHelperService : AbpTagHelperService { - public class AbpFigureTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.TagName = "figure"; - output.Attributes.AddClass("figure"); - } + output.TagName = "figure"; + output.Attributes.AddClass("figure"); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/FontIconType.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/FontIconType.cs index 81debc06b0..ebc23d68ff 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/FontIconType.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/FontIconType.cs @@ -1,8 +1,7 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers; + +public enum FontIconType { - public enum FontIconType - { - FontAwesome, - Other - } -} \ No newline at end of file + FontAwesome, + Other +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs index d710793151..da4a567b5d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs @@ -4,63 +4,62 @@ using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +[HtmlTargetElement("abp-dynamic-form", TagStructure = TagStructure.NormalOrSelfClosing)] +public class AbpDynamicFormTagHelper : AbpTagHelper { - [HtmlTargetElement("abp-dynamic-form", TagStructure = TagStructure.NormalOrSelfClosing)] - public class AbpDynamicFormTagHelper : AbpTagHelper - { - [HtmlAttributeName("abp-model")] - public ModelExpression Model { get; set; } + [HtmlAttributeName("abp-model")] + public ModelExpression Model { get; set; } - public bool? SubmitButton { get; set; } + public bool? SubmitButton { get; set; } - public bool? RequiredSymbols { get; set; } = true; + public bool? RequiredSymbols { get; set; } = true; -#region MvcFormTagHelperAttiributes + #region MvcFormTagHelperAttiributes - private const string ActionAttributeName = "asp-action"; - private const string AreaAttributeName = "asp-area"; - private const string PageAttributeName = "asp-page"; - private const string PageHandlerAttributeName = "asp-page-handler"; - private const string FragmentAttributeName = "asp-fragment"; - private const string ControllerAttributeName = "asp-controller"; - private const string RouteAttributeName = "asp-route"; - private const string RouteValuesDictionaryName = "asp-all-route-data"; - private const string RouteValuesPrefix = "asp-route-"; + private const string ActionAttributeName = "asp-action"; + private const string AreaAttributeName = "asp-area"; + private const string PageAttributeName = "asp-page"; + private const string PageHandlerAttributeName = "asp-page-handler"; + private const string FragmentAttributeName = "asp-fragment"; + private const string ControllerAttributeName = "asp-controller"; + private const string RouteAttributeName = "asp-route"; + private const string RouteValuesDictionaryName = "asp-all-route-data"; + private const string RouteValuesPrefix = "asp-route-"; - [HtmlAttributeName(ActionAttributeName)] - public string Action { get; set; } + [HtmlAttributeName(ActionAttributeName)] + public string Action { get; set; } - [HtmlAttributeName(ControllerAttributeName)] - public string Controller { get; set; } + [HtmlAttributeName(ControllerAttributeName)] + public string Controller { get; set; } - [HtmlAttributeName(AreaAttributeName)] - public string Area { get; set; } + [HtmlAttributeName(AreaAttributeName)] + public string Area { get; set; } - [HtmlAttributeName(PageAttributeName)] - public string Page { get; set; } + [HtmlAttributeName(PageAttributeName)] + public string Page { get; set; } - [HtmlAttributeName(PageHandlerAttributeName)] - public string PageHandler { get; set; } + [HtmlAttributeName(PageHandlerAttributeName)] + public string PageHandler { get; set; } - [HtmlAttributeName(FragmentAttributeName)] - public string Fragment { get; set; } + [HtmlAttributeName(FragmentAttributeName)] + public string Fragment { get; set; } - [HtmlAttributeName(RouteAttributeName)] - public string Route { get; set; } + [HtmlAttributeName(RouteAttributeName)] + public string Route { get; set; } - [EditorBrowsable(EditorBrowsableState.Never)] - public string Method { get; set; } + [EditorBrowsable(EditorBrowsableState.Never)] + public string Method { get; set; } - [HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)] - public IDictionary RouteValues { get; set; } + [HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)] + public IDictionary RouteValues { get; set; } -#endregion + #endregion - public AbpDynamicFormTagHelper(AbpDynamicFormTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpDynamicFormTagHelper(AbpDynamicFormTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs index 410a3d1454..38f55ca04e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs @@ -15,298 +15,297 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +public class AbpDynamicFormTagHelperService : AbpTagHelperService { - public class AbpDynamicFormTagHelperService : AbpTagHelperService + private readonly HtmlEncoder _htmlEncoder; + private readonly IHtmlGenerator _htmlGenerator; + private readonly IServiceProvider _serviceProvider; + private readonly IStringLocalizer _localizer; + + public AbpDynamicFormTagHelperService( + HtmlEncoder htmlEncoder, + IHtmlGenerator htmlGenerator, + IServiceProvider serviceProvider, + IStringLocalizer localizer) { - private readonly HtmlEncoder _htmlEncoder; - private readonly IHtmlGenerator _htmlGenerator; - private readonly IServiceProvider _serviceProvider; - private readonly IStringLocalizer _localizer; - - public AbpDynamicFormTagHelperService( - HtmlEncoder htmlEncoder, - IHtmlGenerator htmlGenerator, - IServiceProvider serviceProvider, - IStringLocalizer localizer) - { - _htmlEncoder = htmlEncoder; - _htmlGenerator = htmlGenerator; - _serviceProvider = serviceProvider; - _localizer = localizer; - } + _htmlEncoder = htmlEncoder; + _htmlGenerator = htmlGenerator; + _serviceProvider = serviceProvider; + _localizer = localizer; + } - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) - { - var list = InitilizeFormGroupContentsContext(context, output); + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + var list = InitilizeFormGroupContentsContext(context, output); - NormalizeTagMode(context, output); + NormalizeTagMode(context, output); - var childContent = await output.GetChildContentAsync(); + var childContent = await output.GetChildContentAsync(); - await ConvertToMvcForm(context, output); + await ConvertToMvcForm(context, output); - await ProcessFieldsAsync(context, output); + await ProcessFieldsAsync(context, output); - RemoveFormGroupItemsNotInModel(context, output, list); + RemoveFormGroupItemsNotInModel(context, output, list); - SetContent(context, output, list, childContent); + SetContent(context, output, list, childContent); - SetFormAttributes(context, output); + SetFormAttributes(context, output); - await SetSubmitButton(context, output); - } + await SetSubmitButton(context, output); + } - protected virtual async Task ConvertToMvcForm(TagHelperContext context, TagHelperOutput output) + protected virtual async Task ConvertToMvcForm(TagHelperContext context, TagHelperOutput output) + { + var formTagHelper = new FormTagHelper(_htmlGenerator) { - var formTagHelper = new FormTagHelper(_htmlGenerator) - { - Action = TagHelper.Action, - Controller = TagHelper.Controller, - Area = TagHelper.Area, - Page = TagHelper.Page, - PageHandler = TagHelper.PageHandler, - Antiforgery = true, - Fragment = TagHelper.Fragment, - Route = TagHelper.Route, - Method = TagHelper.Method, - RouteValues = TagHelper.RouteValues, - ViewContext = TagHelper.ViewContext - }; - - var formTagOutput = await formTagHelper.ProcessAndGetOutputAsync(output.Attributes, context, "form", TagMode.StartTagAndEndTag); - - await formTagOutput.GetChildContentAsync(); - - output.PostContent.AppendHtml(formTagOutput.PostContent); - output.PreContent.AppendHtml(formTagOutput.PreContent); - } + Action = TagHelper.Action, + Controller = TagHelper.Controller, + Area = TagHelper.Area, + Page = TagHelper.Page, + PageHandler = TagHelper.PageHandler, + Antiforgery = true, + Fragment = TagHelper.Fragment, + Route = TagHelper.Route, + Method = TagHelper.Method, + RouteValues = TagHelper.RouteValues, + ViewContext = TagHelper.ViewContext + }; + + var formTagOutput = await formTagHelper.ProcessAndGetOutputAsync(output.Attributes, context, "form", TagMode.StartTagAndEndTag); + + await formTagOutput.GetChildContentAsync(); + + output.PostContent.AppendHtml(formTagOutput.PostContent); + output.PreContent.AppendHtml(formTagOutput.PreContent); + } + + protected virtual void NormalizeTagMode(TagHelperContext context, TagHelperOutput output) + { + output.TagMode = TagMode.StartTagAndEndTag; + output.TagName = "form"; + } + + protected virtual void SetFormAttributes(TagHelperContext context, TagHelperOutput output) + { + output.Attributes.AddIfNotContains("method", "post"); + } + + protected virtual void SetContent(TagHelperContext context, TagHelperOutput output, List items, TagHelperContent childContent) + { + var contentBuilder = new StringBuilder(""); - protected virtual void NormalizeTagMode(TagHelperContext context, TagHelperOutput output) + foreach (var item in items.OrderBy(o => o.Order)) { - output.TagMode = TagMode.StartTagAndEndTag; - output.TagName = "form"; + contentBuilder.AppendLine(item.HtmlContent); } - protected virtual void SetFormAttributes(TagHelperContext context, TagHelperOutput output) + var content = childContent.GetContent(); + if (content.Contains(AbpFormContentPlaceHolder)) { - output.Attributes.AddIfNotContains("method", "post"); + content = content.Replace(AbpFormContentPlaceHolder, contentBuilder.ToString()); } - - protected virtual void SetContent(TagHelperContext context, TagHelperOutput output, List items, TagHelperContent childContent) + else { - var contentBuilder = new StringBuilder(""); - - foreach (var item in items.OrderBy(o => o.Order)) - { - contentBuilder.AppendLine(item.HtmlContent); - } + content = contentBuilder + content; + } - var content = childContent.GetContent(); - if (content.Contains(AbpFormContentPlaceHolder)) - { - content = content.Replace(AbpFormContentPlaceHolder, contentBuilder.ToString()); - } - else - { - content = contentBuilder + content; - } + output.Content.SetHtmlContent(content); + } - output.Content.SetHtmlContent(content); + protected virtual async Task SetSubmitButton(TagHelperContext context, TagHelperOutput output) + { + if (!TagHelper.SubmitButton ?? true) + { + return; } - protected virtual async Task SetSubmitButton(TagHelperContext context, TagHelperOutput output) - { - if (!TagHelper.SubmitButton ?? true) - { - return; - } + var buttonHtml = await ProcessSubmitButtonAndGetContentAsync(context, output); - var buttonHtml = await ProcessSubmitButtonAndGetContentAsync(context, output); + output.PostContent.AppendHtml(buttonHtml); + } - output.PostContent.AppendHtml(buttonHtml); - } + protected virtual List InitilizeFormGroupContentsContext(TagHelperContext context, TagHelperOutput output) + { + var items = new List(); + context.Items[FormGroupContents] = items; + return items; + } - protected virtual List InitilizeFormGroupContentsContext(TagHelperContext context, TagHelperOutput output) - { - var items = new List(); - context.Items[FormGroupContents] = items; - return items; - } + protected virtual async Task ProcessFieldsAsync(TagHelperContext context, TagHelperOutput output) + { + var models = GetModels(context, output); - protected virtual async Task ProcessFieldsAsync(TagHelperContext context, TagHelperOutput output) + foreach (var model in models) { - var models = GetModels(context, output); - - foreach (var model in models) + if (IsSelectGroup(context, model)) { - if (IsSelectGroup(context, model)) - { - await ProcessSelectGroupAsync(context, output, model); - } - else - { - await ProcessInputGroupAsync(context, output, model); - } + await ProcessSelectGroupAsync(context, output, model); + } + else + { + await ProcessInputGroupAsync(context, output, model); } } + } - protected virtual void RemoveFormGroupItemsNotInModel(TagHelperContext context, TagHelperOutput output, List items) - { - var models = GetModels(context, output); + protected virtual void RemoveFormGroupItemsNotInModel(TagHelperContext context, TagHelperOutput output, List items) + { + var models = GetModels(context, output); - items.RemoveAll(x => models.All(m => !m.Name.Equals(x.PropertyName, StringComparison.InvariantCultureIgnoreCase))); - } + items.RemoveAll(x => models.All(m => !m.Name.Equals(x.PropertyName, StringComparison.InvariantCultureIgnoreCase))); + } - protected virtual async Task ProcessSelectGroupAsync(TagHelperContext context, TagHelperOutput output, ModelExpression model) - { - var abpSelectTagHelper = GetSelectGroupTagHelper(context, output, model); + protected virtual async Task ProcessSelectGroupAsync(TagHelperContext context, TagHelperOutput output, ModelExpression model) + { + var abpSelectTagHelper = GetSelectGroupTagHelper(context, output, model); - await abpSelectTagHelper.RenderAsync(new TagHelperAttributeList(), context, _htmlEncoder, "div", TagMode.StartTagAndEndTag); - } + await abpSelectTagHelper.RenderAsync(new TagHelperAttributeList(), context, _htmlEncoder, "div", TagMode.StartTagAndEndTag); + } - protected virtual AbpTagHelper GetSelectGroupTagHelper(TagHelperContext context, TagHelperOutput output, ModelExpression model) - { - return IsRadioGroup(model.ModelExplorer) ? - GetAbpRadioInputTagHelper(model) : - GetSelectTagHelper(model); - } + protected virtual AbpTagHelper GetSelectGroupTagHelper(TagHelperContext context, TagHelperOutput output, ModelExpression model) + { + return IsRadioGroup(model.ModelExplorer) ? + GetAbpRadioInputTagHelper(model) : + GetSelectTagHelper(model); + } - protected virtual AbpTagHelper GetSelectTagHelper(ModelExpression model) - { - var abpSelectTagHelper = _serviceProvider.GetRequiredService(); - abpSelectTagHelper.AspFor = model; - abpSelectTagHelper.AspItems = null; - abpSelectTagHelper.ViewContext = TagHelper.ViewContext; - return abpSelectTagHelper; - } + protected virtual AbpTagHelper GetSelectTagHelper(ModelExpression model) + { + var abpSelectTagHelper = _serviceProvider.GetRequiredService(); + abpSelectTagHelper.AspFor = model; + abpSelectTagHelper.AspItems = null; + abpSelectTagHelper.ViewContext = TagHelper.ViewContext; + return abpSelectTagHelper; + } - protected virtual AbpTagHelper GetAbpRadioInputTagHelper(ModelExpression model) - { - var radioButtonAttribute = model.ModelExplorer.GetAttribute(); - var abpRadioInputTagHelper = _serviceProvider.GetRequiredService(); - abpRadioInputTagHelper.AspFor = model; - abpRadioInputTagHelper.AspItems = null; - abpRadioInputTagHelper.Inline = radioButtonAttribute.Inline; - abpRadioInputTagHelper.Disabled = radioButtonAttribute.Disabled; - abpRadioInputTagHelper.ViewContext = TagHelper.ViewContext; - return abpRadioInputTagHelper; - } + protected virtual AbpTagHelper GetAbpRadioInputTagHelper(ModelExpression model) + { + var radioButtonAttribute = model.ModelExplorer.GetAttribute(); + var abpRadioInputTagHelper = _serviceProvider.GetRequiredService(); + abpRadioInputTagHelper.AspFor = model; + abpRadioInputTagHelper.AspItems = null; + abpRadioInputTagHelper.Inline = radioButtonAttribute.Inline; + abpRadioInputTagHelper.Disabled = radioButtonAttribute.Disabled; + abpRadioInputTagHelper.ViewContext = TagHelper.ViewContext; + return abpRadioInputTagHelper; + } - protected virtual async Task ProcessSubmitButtonAndGetContentAsync(TagHelperContext context, TagHelperOutput output) - { - var abpButtonTagHelper = _serviceProvider.GetRequiredService(); - var attributes = new TagHelperAttributeList { new TagHelperAttribute("type", "submit") }; - abpButtonTagHelper.Text = _localizer["Submit"]; - abpButtonTagHelper.ButtonType = AbpButtonType.Primary; + protected virtual async Task ProcessSubmitButtonAndGetContentAsync(TagHelperContext context, TagHelperOutput output) + { + var abpButtonTagHelper = _serviceProvider.GetRequiredService(); + var attributes = new TagHelperAttributeList { new TagHelperAttribute("type", "submit") }; + abpButtonTagHelper.Text = _localizer["Submit"]; + abpButtonTagHelper.ButtonType = AbpButtonType.Primary; - return await abpButtonTagHelper.RenderAsync(attributes, context, _htmlEncoder, "button", TagMode.StartTagAndEndTag); - } + return await abpButtonTagHelper.RenderAsync(attributes, context, _htmlEncoder, "button", TagMode.StartTagAndEndTag); + } - protected virtual async Task ProcessInputGroupAsync(TagHelperContext context, TagHelperOutput output, ModelExpression model) - { - var abpInputTagHelper = _serviceProvider.GetRequiredService(); - abpInputTagHelper.AspFor = model; - abpInputTagHelper.ViewContext = TagHelper.ViewContext; - abpInputTagHelper.DisplayRequiredSymbol = TagHelper.RequiredSymbols ?? true; + protected virtual async Task ProcessInputGroupAsync(TagHelperContext context, TagHelperOutput output, ModelExpression model) + { + var abpInputTagHelper = _serviceProvider.GetRequiredService(); + abpInputTagHelper.AspFor = model; + abpInputTagHelper.ViewContext = TagHelper.ViewContext; + abpInputTagHelper.DisplayRequiredSymbol = TagHelper.RequiredSymbols ?? true; - await abpInputTagHelper.RenderAsync(new TagHelperAttributeList(), context, _htmlEncoder, "div", TagMode.StartTagAndEndTag); - } + await abpInputTagHelper.RenderAsync(new TagHelperAttributeList(), context, _htmlEncoder, "div", TagMode.StartTagAndEndTag); + } - protected virtual List GetModels(TagHelperContext context, TagHelperOutput output) + protected virtual List GetModels(TagHelperContext context, TagHelperOutput output) + { + return TagHelper.Model.ModelExplorer.Properties.Aggregate(new List(), ExploreModelsRecursively); + } + + protected virtual List ExploreModelsRecursively(List list, ModelExplorer model) + { + if (model.GetAttribute() != null) { - return TagHelper.Model.ModelExplorer.Properties.Aggregate(new List(), ExploreModelsRecursively); + return list; } - protected virtual List ExploreModelsRecursively(List list, ModelExplorer model) + if (IsCsharpClassOrPrimitive(model.ModelType) || IsListOfCsharpClassOrPrimitive(model.ModelType)) { - if (model.GetAttribute() != null) - { - return list; - } - - if (IsCsharpClassOrPrimitive(model.ModelType) || IsListOfCsharpClassOrPrimitive(model.ModelType)) - { - list.Add(ModelExplorerToModelExpressionConverter(model)); - - return list; - } - - if (IsListOfSelectItem(model.ModelType)) - { - return list; - } + list.Add(ModelExplorerToModelExpressionConverter(model)); - return model.Properties.Aggregate(list, ExploreModelsRecursively); + return list; } - protected virtual ModelExpression ModelExplorerToModelExpressionConverter(ModelExplorer explorer) + if (IsListOfSelectItem(model.ModelType)) { - var temp = explorer; - var propertyName = explorer.Metadata.PropertyName; - - while (temp?.Container?.Metadata?.PropertyName != null) - { - temp = temp.Container; - propertyName = temp.Metadata.PropertyName + "." + propertyName; - } - - return new ModelExpression(propertyName, explorer); + return list; } - protected virtual bool IsListOfCsharpClassOrPrimitive(Type type) - { - var genericType = type.GenericTypeArguments.FirstOrDefault(); + return model.Properties.Aggregate(list, ExploreModelsRecursively); + } - if (genericType == null || !IsCsharpClassOrPrimitive(genericType)) - { - return false; - } + protected virtual ModelExpression ModelExplorerToModelExpressionConverter(ModelExplorer explorer) + { + var temp = explorer; + var propertyName = explorer.Metadata.PropertyName; - return type.ToString().StartsWith("System.Collections.Generic.IEnumerable`") || type.ToString().StartsWith("System.Collections.Generic.List`"); + while (temp?.Container?.Metadata?.PropertyName != null) + { + temp = temp.Container; + propertyName = temp.Metadata.PropertyName + "." + propertyName; } - protected virtual bool IsCsharpClassOrPrimitive(Type type) - { - if (type == null) - { - return false; - } + return new ModelExpression(propertyName, explorer); + } - return type.IsPrimitive || - type.IsValueType || - type == typeof(string) || - type == typeof(Guid) || - type == typeof(DateTime) || - type == typeof(ValueType) || - type == typeof(TimeSpan) || - type == typeof(DateTimeOffset) || - type.IsEnum; - } + protected virtual bool IsListOfCsharpClassOrPrimitive(Type type) + { + var genericType = type.GenericTypeArguments.FirstOrDefault(); - protected virtual bool IsListOfSelectItem(Type type) + if (genericType == null || !IsCsharpClassOrPrimitive(genericType)) { - return type == typeof(List) || type == typeof(IEnumerable); + return false; } - protected virtual bool IsSelectGroup(TagHelperContext context, ModelExpression model) - { - return IsEnum(model.ModelExplorer) || AreSelectItemsProvided(model.ModelExplorer); - } + return type.ToString().StartsWith("System.Collections.Generic.IEnumerable`") || type.ToString().StartsWith("System.Collections.Generic.List`"); + } - protected virtual bool IsEnum(ModelExplorer explorer) + protected virtual bool IsCsharpClassOrPrimitive(Type type) + { + if (type == null) { - return explorer.Metadata.IsEnum; + return false; } - protected virtual bool AreSelectItemsProvided(ModelExplorer explorer) - { - return explorer.GetAttribute() != null; - } + return type.IsPrimitive || + type.IsValueType || + type == typeof(string) || + type == typeof(Guid) || + type == typeof(DateTime) || + type == typeof(ValueType) || + type == typeof(TimeSpan) || + type == typeof(DateTimeOffset) || + type.IsEnum; + } - protected virtual bool IsRadioGroup(ModelExplorer explorer) - { - return explorer.GetAttribute() != null; - } + protected virtual bool IsListOfSelectItem(Type type) + { + return type == typeof(List) || type == typeof(IEnumerable); + } + + protected virtual bool IsSelectGroup(TagHelperContext context, ModelExpression model) + { + return IsEnum(model.ModelExplorer) || AreSelectItemsProvided(model.ModelExplorer); + } + + protected virtual bool IsEnum(ModelExplorer explorer) + { + return explorer.Metadata.IsEnum; + } + + protected virtual bool AreSelectItemsProvided(ModelExplorer explorer) + { + return explorer.GetAttribute() != null; + } + + protected virtual bool IsRadioGroup(ModelExplorer explorer) + { + return explorer.GetAttribute() != null; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormContentTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormContentTagHelper.cs index 9606cc1b77..af0ff4e2d3 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormContentTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormContentTagHelper.cs @@ -1,15 +1,14 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +[HtmlTargetElement("abp-form-content", TagStructure = TagStructure.WithoutEndTag)] +public class AbpFormContentTagHelper : AbpTagHelper, ITransientDependency { - [HtmlTargetElement("abp-form-content", TagStructure = TagStructure.WithoutEndTag)] - public class AbpFormContentTagHelper : AbpTagHelper, ITransientDependency + public AbpFormContentTagHelper(AbpFormContentTagHelperService tagHelperService) + : base(tagHelperService) { - public AbpFormContentTagHelper(AbpFormContentTagHelperService tagHelperService) - : base(tagHelperService) - { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormContentTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormContentTagHelperService.cs index 9cb06d5364..d973c31fb2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormContentTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormContentTagHelperService.cs @@ -1,15 +1,14 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +public class AbpFormContentTagHelperService : AbpTagHelperService { - public class AbpFormContentTagHelperService : AbpTagHelperService + public override void Process(TagHelperContext context, TagHelperOutput output) { - public override void Process(TagHelperContext context, TagHelperOutput output) - { - output.Attributes.Clear(); - output.TagName = "div"; - output.TagMode = TagMode.StartTagAndEndTag; - output.Content.SetContent(AbpFormContentPlaceHolder); - } + output.Attributes.Clear(); + output.TagName = "div"; + output.TagMode = TagMode.StartTagAndEndTag; + output.Content.SetContent(AbpFormContentPlaceHolder); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormControlSize.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormControlSize.cs index 79070b559e..94acbca97e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormControlSize.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormControlSize.cs @@ -1,10 +1,9 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +public enum AbpFormControlSize { - public enum AbpFormControlSize - { - Default, - Small, - Medium, - Large - } + Default, + Small, + Medium, + Large } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpIdNameTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpIdNameTagHelper.cs index 7bc1c674a7..d24e247abe 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpIdNameTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpIdNameTagHelper.cs @@ -7,43 +7,42 @@ using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.Options; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +[HtmlTargetElement(Attributes = "abp-id-name")] +public class AbpIdNameTagHelper : AbpTagHelper { - [HtmlTargetElement(Attributes = "abp-id-name")] - public class AbpIdNameTagHelper : AbpTagHelper - { - /// - /// Make sure this TagHelper is executed first. - /// - public override int Order => -1000 - 1; + /// + /// Make sure this TagHelper is executed first. + /// + public override int Order => -1000 - 1; - [HtmlAttributeName("abp-id-name")] - public ModelExpression IdNameFor { get; set; } + [HtmlAttributeName("abp-id-name")] + public ModelExpression IdNameFor { get; set; } - private readonly MvcViewOptions _mvcViewOptions; + private readonly MvcViewOptions _mvcViewOptions; - public AbpIdNameTagHelper(IOptions mvcViewOptions) - { - _mvcViewOptions = mvcViewOptions.Value; - } + public AbpIdNameTagHelper(IOptions mvcViewOptions) + { + _mvcViewOptions = mvcViewOptions.Value; + } - public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + if (IdNameFor != null) { - if (IdNameFor != null) + if (!context.AllAttributes.Any(x => x.Name.Equals("id", StringComparison.OrdinalIgnoreCase))) { - if (!context.AllAttributes.Any(x => x.Name.Equals("id", StringComparison.OrdinalIgnoreCase))) - { - var id = TagBuilder.CreateSanitizedId(IdNameFor.Name, _mvcViewOptions.HtmlHelperOptions.IdAttributeDotReplacement); - output.Attributes.Add("id", id); - } - - if (!context.AllAttributes.Any(x => x.Name.Equals("name", StringComparison.OrdinalIgnoreCase))) - { - output.Attributes.Add("name", IdNameFor.Name); - } + var id = TagBuilder.CreateSanitizedId(IdNameFor.Name, _mvcViewOptions.HtmlHelperOptions.IdAttributeDotReplacement); + output.Attributes.Add("id", id); } - return Task.CompletedTask; + if (!context.AllAttributes.Any(x => x.Name.Equals("name", StringComparison.OrdinalIgnoreCase))) + { + output.Attributes.Add("name", IdNameFor.Name); + } } + + return Task.CompletedTask; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs index fdab58203a..5778e47692 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs @@ -2,48 +2,47 @@ using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +public class AbpInputTagHelper : AbpTagHelper { - public class AbpInputTagHelper : AbpTagHelper - { - public ModelExpression AspFor { get; set; } + public ModelExpression AspFor { get; set; } - public string Label { get; set; } + public string Label { get; set; } - [HtmlAttributeName("info")] - public string InfoText { get; set; } + [HtmlAttributeName("info")] + public string InfoText { get; set; } - [HtmlAttributeName("disabled")] - public bool IsDisabled { get; set; } = false; + [HtmlAttributeName("disabled")] + public bool IsDisabled { get; set; } = false; - [HtmlAttributeName("readonly")] - public bool? IsReadonly { get; set; } = false; + [HtmlAttributeName("readonly")] + public bool? IsReadonly { get; set; } = false; - public bool AutoFocus { get; set; } + public bool AutoFocus { get; set; } - [HtmlAttributeName("type")] - public string InputTypeName { get; set; } + [HtmlAttributeName("type")] + public string InputTypeName { get; set; } - public AbpFormControlSize Size { get; set; } = AbpFormControlSize.Default; + public AbpFormControlSize Size { get; set; } = AbpFormControlSize.Default; - [HtmlAttributeName("required-symbol")] - public bool DisplayRequiredSymbol { get; set; } = true; + [HtmlAttributeName("required-symbol")] + public bool DisplayRequiredSymbol { get; set; } = true; - [HtmlAttributeName("asp-format")] - public string Format { get; set; } + [HtmlAttributeName("asp-format")] + public string Format { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public string Value { get; set; } + public string Value { get; set; } - public bool SuppressLabel { get; set; } + public bool SuppressLabel { get; set; } - public CheckBoxHiddenInputRenderMode? CheckBoxHiddenInputRenderMode { get; set; } + public CheckBoxHiddenInputRenderMode? CheckBoxHiddenInputRenderMode { get; set; } - public AbpInputTagHelper(AbpInputTagHelperService tagHelperService) - : base(tagHelperService) - { + public AbpInputTagHelper(AbpInputTagHelperService tagHelperService) + : base(tagHelperService) + { - } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index 1ef29c799b..3bc99436ca 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -11,462 +11,461 @@ using System.Threading.Tasks; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +public class AbpInputTagHelperService : AbpTagHelperService { - public class AbpInputTagHelperService : AbpTagHelperService + private readonly IHtmlGenerator _generator; + private readonly HtmlEncoder _encoder; + private readonly IAbpTagHelperLocalizer _tagHelperLocalizer; + + public AbpInputTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IAbpTagHelperLocalizer tagHelperLocalizer) { - private readonly IHtmlGenerator _generator; - private readonly HtmlEncoder _encoder; - private readonly IAbpTagHelperLocalizer _tagHelperLocalizer; + _generator = generator; + _encoder = encoder; + _tagHelperLocalizer = tagHelperLocalizer; + } - public AbpInputTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IAbpTagHelperLocalizer tagHelperLocalizer) - { - _generator = generator; - _encoder = encoder; - _tagHelperLocalizer = tagHelperLocalizer; - } + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + var (innerHtml, isCheckBox) = await GetFormInputGroupAsHtmlAsync(context, output); - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + if (isCheckBox && TagHelper.CheckBoxHiddenInputRenderMode.HasValue) { - var (innerHtml, isCheckBox) = await GetFormInputGroupAsHtmlAsync(context, output); - - if (isCheckBox && TagHelper.CheckBoxHiddenInputRenderMode.HasValue) - { - TagHelper.ViewContext.CheckBoxHiddenInputRenderMode = TagHelper.CheckBoxHiddenInputRenderMode.Value; - } + TagHelper.ViewContext.CheckBoxHiddenInputRenderMode = TagHelper.CheckBoxHiddenInputRenderMode.Value; + } - var order = TagHelper.AspFor.ModelExplorer.GetDisplayOrder(); + var order = TagHelper.AspFor.ModelExplorer.GetDisplayOrder(); - AddGroupToFormGroupContents( - context, - TagHelper.AspFor.Name, - SurroundInnerHtmlAndGet(context, output, innerHtml, isCheckBox), - order, - out var suppress - ); + AddGroupToFormGroupContents( + context, + TagHelper.AspFor.Name, + SurroundInnerHtmlAndGet(context, output, innerHtml, isCheckBox), + order, + out var suppress + ); - if (suppress) - { - output.SuppressOutput(); - } - else + if (suppress) + { + output.SuppressOutput(); + } + else + { + output.TagMode = TagMode.StartTagAndEndTag; + output.TagName = "div"; + LeaveOnlyGroupAttributes(context, output); + output.Attributes.AddClass(isCheckBox ? "mb-2" : "mb-3"); + if (isCheckBox) { - output.TagMode = TagMode.StartTagAndEndTag; - output.TagName = "div"; - LeaveOnlyGroupAttributes(context, output); - output.Attributes.AddClass(isCheckBox ? "mb-2" : "mb-3"); - if (isCheckBox) - { - output.Attributes.AddClass("form-check"); - } - output.Content.AppendHtml(innerHtml); + output.Attributes.AddClass("form-check"); } + output.Content.AppendHtml(innerHtml); } + } - protected virtual async Task<(string, bool)> GetFormInputGroupAsHtmlAsync(TagHelperContext context, TagHelperOutput output) - { - var (inputTag, isCheckBox) = await GetInputTagHelperOutputAsync(context, output); + protected virtual async Task<(string, bool)> GetFormInputGroupAsHtmlAsync(TagHelperContext context, TagHelperOutput output) + { + var (inputTag, isCheckBox) = await GetInputTagHelperOutputAsync(context, output); - var inputHtml = inputTag.Render(_encoder); - var label = await GetLabelAsHtmlAsync(context, output, inputTag, isCheckBox); - var info = GetInfoAsHtml(context, output, inputTag, isCheckBox); - var validation = isCheckBox ? "" : await GetValidationAsHtmlAsync(context, output, inputTag); + var inputHtml = inputTag.Render(_encoder); + var label = await GetLabelAsHtmlAsync(context, output, inputTag, isCheckBox); + var info = GetInfoAsHtml(context, output, inputTag, isCheckBox); + var validation = isCheckBox ? "" : await GetValidationAsHtmlAsync(context, output, inputTag); - return (GetContent(context, output, label, inputHtml, validation, info, isCheckBox), isCheckBox); - } + return (GetContent(context, output, label, inputHtml, validation, info, isCheckBox), isCheckBox); + } - protected virtual async Task GetValidationAsHtmlAsync(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag) + protected virtual async Task GetValidationAsHtmlAsync(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag) + { + if (IsOutputHidden(inputTag)) { - if (IsOutputHidden(inputTag)) - { - return ""; - } + return ""; + } - var validationMessageTagHelper = new ValidationMessageTagHelper(_generator) - { - For = TagHelper.AspFor, - ViewContext = TagHelper.ViewContext - }; + var validationMessageTagHelper = new ValidationMessageTagHelper(_generator) + { + For = TagHelper.AspFor, + ViewContext = TagHelper.ViewContext + }; - var attributeList = new TagHelperAttributeList { { "class", "text-danger" } }; + var attributeList = new TagHelperAttributeList { { "class", "text-danger" } }; - return await validationMessageTagHelper.RenderAsync(attributeList, context, _encoder, "span", TagMode.StartTagAndEndTag); - } + return await validationMessageTagHelper.RenderAsync(attributeList, context, _encoder, "span", TagMode.StartTagAndEndTag); + } - protected virtual string GetContent(TagHelperContext context, TagHelperOutput output, string label, string inputHtml, string validation, string infoHtml, bool isCheckbox) - { - var innerContent = isCheckbox ? - inputHtml + label : - label + inputHtml; + protected virtual string GetContent(TagHelperContext context, TagHelperOutput output, string label, string inputHtml, string validation, string infoHtml, bool isCheckbox) + { + var innerContent = isCheckbox ? + inputHtml + label : + label + inputHtml; - return innerContent + infoHtml + validation; - } + return innerContent + infoHtml + validation; + } - protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml, bool isCheckbox) - { - return "
    " + - Environment.NewLine + innerHtml + Environment.NewLine + - "
    "; - } + protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml, bool isCheckbox) + { + return "
    " + + Environment.NewLine + innerHtml + Environment.NewLine + + "
    "; + } - protected virtual TagHelper GetInputTagHelper(TagHelperContext context, TagHelperOutput output) + protected virtual TagHelper GetInputTagHelper(TagHelperContext context, TagHelperOutput output) + { + if (TagHelper.AspFor.ModelExplorer.GetAttribute